dsh-claude-style 0.17.0 → 0.18.0-dev.35
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 +88 -0
- package/LICENSE +11 -3
- package/NOTICE.md +22 -0
- package/README.en.md +10 -3
- package/README.md +10 -3
- package/THIRD_PARTY_NOTICES.md +5 -1
- package/docs/STYLE.md +33 -4
- package/lib/assets/6f75a6052515.js.br +0 -0
- package/lib/assets/d5aa963bf99b.js.br +0 -0
- package/lib/assets/manifest.json +8 -8
- package/lib/client.js +15 -13
- package/lib/fonts/OFL.txt +8 -0
- package/lib/host/brand.js +2 -0
- package/lib/host/json-document.js +7 -0
- package/lib/host/peakrate-catalog.js +1 -3
- package/lib/host/peakrate.js +26 -16
- package/lib/host/prices-document.js +194 -0
- package/lib/host/prices.js +153 -0
- package/lib/host/routes.js +35 -1
- package/lib/host/session-cost.js +306 -0
- package/lib/host/settings.js +13 -0
- package/lib/host/usage-fold.js +3 -1
- package/lib/model-descriptions.json +1 -1
- package/lib/peakrate-catalog.json +192 -52
- package/lib/prices.json +2202 -0
- package/package.json +9 -7
- package/packages/client/src/features/sodium/LICENSE +174 -0
- package/lib/assets/812ff61f28cc.js.br +0 -0
- package/lib/assets/94873ed8d52d.js.br +0 -0
- package/lib/fonts/AUTHORS.txt +0 -10
- package/lib/fonts/OFL-Inter.txt +0 -93
- package/lib/fonts/OFL-NotoSerif.txt +0 -93
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
// packages/contracts/src/package.ts
|
|
2
|
+
var PACKAGE_NAME = "dsh-claude-style";
|
|
3
|
+
|
|
4
|
+
// packages/contracts/src/prices.ts
|
|
5
|
+
var CURRENCY_CNY = "CNY";
|
|
6
|
+
var CURRENCY_USD = "USD";
|
|
7
|
+
var CURRENCY_SYMBOL = {
|
|
8
|
+
[CURRENCY_CNY]: "\xA5",
|
|
9
|
+
[CURRENCY_USD]: "$"
|
|
10
|
+
};
|
|
11
|
+
var WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
12
|
+
var formatters = /* @__PURE__ */ new Map();
|
|
13
|
+
function formatter(timeZone) {
|
|
14
|
+
let held = formatters.get(timeZone);
|
|
15
|
+
if (held === void 0) {
|
|
16
|
+
held = new Intl.DateTimeFormat("en-CA", {
|
|
17
|
+
timeZone,
|
|
18
|
+
weekday: "short",
|
|
19
|
+
year: "numeric",
|
|
20
|
+
month: "2-digit",
|
|
21
|
+
day: "2-digit",
|
|
22
|
+
hour: "2-digit",
|
|
23
|
+
minute: "2-digit",
|
|
24
|
+
hourCycle: "h23"
|
|
25
|
+
});
|
|
26
|
+
formatters.set(timeZone, held);
|
|
27
|
+
}
|
|
28
|
+
return held;
|
|
29
|
+
}
|
|
30
|
+
function clockAt(atMs, timeZone) {
|
|
31
|
+
if (!Number.isFinite(atMs)) return null;
|
|
32
|
+
let parts;
|
|
33
|
+
try {
|
|
34
|
+
parts = formatter(timeZone).formatToParts(new Date(atMs));
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const field = (type) => parts.find((part) => part.type === type)?.value ?? "";
|
|
39
|
+
const weekday = WEEKDAYS.indexOf(field("weekday"));
|
|
40
|
+
const year = Number(field("year"));
|
|
41
|
+
const month = Number(field("month"));
|
|
42
|
+
const day = Number(field("day"));
|
|
43
|
+
const hour = Number(field("hour"));
|
|
44
|
+
const minute = Number(field("minute"));
|
|
45
|
+
if (weekday < 0 || !Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day) || !Number.isFinite(hour) || !Number.isFinite(minute)) return null;
|
|
46
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
47
|
+
return { weekday, date: `${year}-${pad(month)}-${pad(day)}`, minutes: hour * 60 + minute };
|
|
48
|
+
}
|
|
49
|
+
function clockMinutes(value) {
|
|
50
|
+
const match = /^(\d{1,2}):(\d{2})$/.exec(value);
|
|
51
|
+
if (match === null) return Number.NaN;
|
|
52
|
+
return Number(match[1]) * 60 + Number(match[2]);
|
|
53
|
+
}
|
|
54
|
+
function isPeak(atMs, schedule) {
|
|
55
|
+
const clock = clockAt(atMs, schedule.timeZone);
|
|
56
|
+
if (clock === null || !schedule.peakDays.includes(clock.weekday)) return false;
|
|
57
|
+
const holiday = clockAt(atMs, schedule.holidayZone);
|
|
58
|
+
if (holiday !== null && schedule.holidays.includes(holiday.date)) return false;
|
|
59
|
+
for (let i = 0; i < schedule.peakWindows.length; i++) {
|
|
60
|
+
const window = schedule.peakWindows[i];
|
|
61
|
+
const start = clockMinutes(window.start);
|
|
62
|
+
const end = clockMinutes(window.end);
|
|
63
|
+
if (clock.minutes >= start && clock.minutes < end) return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function bareModelId(modelId) {
|
|
68
|
+
const raw = modelId.trim().toLowerCase();
|
|
69
|
+
if (raw === "") return null;
|
|
70
|
+
return raw.includes("/") ? raw.slice(raw.lastIndexOf("/") + 1) : raw;
|
|
71
|
+
}
|
|
72
|
+
var CATALOG_SUFFIXES = ["-high", "-low", "-fast", "-sg", "-256k", "-128k", "-1m", "-32k", "-200k", "-flex"];
|
|
73
|
+
function catalogIdFor(document, bare) {
|
|
74
|
+
const catalog = document.catalog;
|
|
75
|
+
if (catalog === void 0) return null;
|
|
76
|
+
const candidates = [document.aliases[bare], bare];
|
|
77
|
+
for (const suffix of CATALOG_SUFFIXES) {
|
|
78
|
+
if (bare.endsWith(suffix) && bare.length > suffix.length) candidates.push(bare.slice(0, -suffix.length));
|
|
79
|
+
}
|
|
80
|
+
for (const candidate of candidates) {
|
|
81
|
+
if (candidate !== void 0 && Object.hasOwn(catalog, candidate)) return candidate;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
function priceModelId(document, modelId) {
|
|
86
|
+
const bare = bareModelId(modelId);
|
|
87
|
+
if (bare === null) return null;
|
|
88
|
+
if (Object.hasOwn(document.models, bare)) return bare;
|
|
89
|
+
const alias = document.aliases[bare];
|
|
90
|
+
return alias !== void 0 && Object.hasOwn(document.models, alias) ? alias : null;
|
|
91
|
+
}
|
|
92
|
+
function priceFor(document, modelId, currency) {
|
|
93
|
+
const id = priceModelId(document, modelId);
|
|
94
|
+
if (id !== null) return document.models[id]?.[currency];
|
|
95
|
+
const bare = bareModelId(modelId);
|
|
96
|
+
if (bare === null) return void 0;
|
|
97
|
+
const catalogId = catalogIdFor(document, bare);
|
|
98
|
+
if (catalogId === null) return void 0;
|
|
99
|
+
const published = currency === "CNY" ? document.catalogCny?.[catalogId] : void 0;
|
|
100
|
+
const parts = (published ?? document.catalog?.[catalogId] ?? "").split(" ").map(Number);
|
|
101
|
+
if (parts.length !== 3 || !parts.every((value) => Number.isFinite(value) && value >= 0)) return void 0;
|
|
102
|
+
if (parts[0] === 0 && parts[2] === 0) return void 0;
|
|
103
|
+
const rate = published !== void 0 ? 1 : currency === "CNY" ? document.usdToCny ?? 7.2 : 1;
|
|
104
|
+
const tier = { cacheHit: parts[1] * rate, cacheMiss: parts[0] * rate, output: parts[2] * rate };
|
|
105
|
+
return { offPeak: tier, peak: tier };
|
|
106
|
+
}
|
|
107
|
+
function priceTierAt(price, atMs, schedule) {
|
|
108
|
+
return isPeak(atMs, schedule) ? price.peak : price.offPeak;
|
|
109
|
+
}
|
|
110
|
+
function costMicro(usage, tier) {
|
|
111
|
+
const count2 = (value) => Number.isFinite(value) && value > 0 ? value : 0;
|
|
112
|
+
const cost = count2(usage.input) * tier.cacheMiss + count2(usage.cacheRead) * tier.cacheHit + count2(usage.cacheWrite) * tier.cacheHit + count2(usage.output) * tier.output;
|
|
113
|
+
return Number.isFinite(cost) && cost > 0 ? cost : 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// packages/engine/node/session-events.ts
|
|
117
|
+
function eventsOf(events) {
|
|
118
|
+
return Array.isArray(events) ? events : null;
|
|
119
|
+
}
|
|
120
|
+
function unreadableLog(error) {
|
|
121
|
+
const failure = error;
|
|
122
|
+
return failure?.code === "SESSION_QUERY_CORRUPT_SESSION" || failure?.code === "SESSION_QUERY_SESSION_NOT_FOUND" ? failure : null;
|
|
123
|
+
}
|
|
124
|
+
function reportUnreadable(ctx, packageName, error, sessionId, label) {
|
|
125
|
+
ctx.logger?.warn?.(`${packageName}: session ${sessionId} left out of ${label}: ${unreadableLog(error)?.message}`);
|
|
126
|
+
}
|
|
127
|
+
function releaseObservation(observation) {
|
|
128
|
+
const named = observation.dispose;
|
|
129
|
+
if (typeof named === "function") named.call(observation);
|
|
130
|
+
else if (Symbol.dispose in observation) observation[Symbol.dispose]();
|
|
131
|
+
}
|
|
132
|
+
async function readEvents(ctx, packageName, sessionId, label) {
|
|
133
|
+
const query = ctx.get("sessionQuery");
|
|
134
|
+
if (query === null || query === void 0) return null;
|
|
135
|
+
try {
|
|
136
|
+
if (typeof query.observeSession === "function") {
|
|
137
|
+
const observation = await query.observeSession(sessionId, { projectionMode: "none" });
|
|
138
|
+
try {
|
|
139
|
+
return eventsOf(observation.events);
|
|
140
|
+
} finally {
|
|
141
|
+
releaseObservation(observation);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (typeof query.readSession !== "function") return null;
|
|
145
|
+
const snapshot = await query.readSession(sessionId);
|
|
146
|
+
return eventsOf(snapshot?.events);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (unreadableLog(error) === null) throw error;
|
|
149
|
+
reportUnreadable(ctx, packageName, error, sessionId, label);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// packages/host/src/session-cost.ts
|
|
155
|
+
import { modelOf, usageOf } from "./usage-fold.js";
|
|
156
|
+
var KEPT_SESSIONS = 128;
|
|
157
|
+
function count(value) {
|
|
158
|
+
const number = Number(value);
|
|
159
|
+
return Number.isFinite(number) && number > 0 ? number : 0;
|
|
160
|
+
}
|
|
161
|
+
function usageOfSample(event) {
|
|
162
|
+
const sample = usageOf(event);
|
|
163
|
+
if (sample === void 0) return null;
|
|
164
|
+
return {
|
|
165
|
+
input: count(sample.inputTokens),
|
|
166
|
+
output: count(sample.outputTokens),
|
|
167
|
+
cacheRead: count(sample.cacheReadTokens),
|
|
168
|
+
cacheWrite: count(sample.cacheWriteTokens)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function sameUsage(settlement, usage) {
|
|
172
|
+
return settlement.input === usage.input && settlement.output === usage.output && settlement.cacheRead === usage.cacheRead && settlement.cacheWrite === usage.cacheWrite;
|
|
173
|
+
}
|
|
174
|
+
function applyCostEvent(state, event, document) {
|
|
175
|
+
const type = event?.type;
|
|
176
|
+
if (type === "llm/retry-started") {
|
|
177
|
+
const turn2 = event.data?.turn;
|
|
178
|
+
const step2 = event.data?.step;
|
|
179
|
+
if (state.last !== null && state.last.turn === turn2 && state.last.step === step2) {
|
|
180
|
+
return { ...state, last: null };
|
|
181
|
+
}
|
|
182
|
+
return state;
|
|
183
|
+
}
|
|
184
|
+
if (type !== "assistant/message" && type !== "assistant/attempt") return state;
|
|
185
|
+
const usage = usageOfSample(event);
|
|
186
|
+
if (usage === null) {
|
|
187
|
+
const named = modelOf(event);
|
|
188
|
+
return named === null || named === state.model ? state : { ...state, model: named };
|
|
189
|
+
}
|
|
190
|
+
const time = event.time;
|
|
191
|
+
const model = modelOf(event) ?? state.model;
|
|
192
|
+
if (typeof time !== "number" || !Number.isFinite(time)) return state;
|
|
193
|
+
let cny = 0;
|
|
194
|
+
let usd = 0;
|
|
195
|
+
let partial = state.partial;
|
|
196
|
+
const priced = (currency) => {
|
|
197
|
+
const price = model === null ? void 0 : priceFor(document, model, currency);
|
|
198
|
+
if (price === void 0) return null;
|
|
199
|
+
return costMicro(usage, priceTierAt(price, time, document.schedule));
|
|
200
|
+
};
|
|
201
|
+
const cnyCost = priced("CNY");
|
|
202
|
+
const usdCost = priced("USD");
|
|
203
|
+
if (cnyCost === null || usdCost === null) partial = true;
|
|
204
|
+
cny = cnyCost ?? 0;
|
|
205
|
+
usd = usdCost ?? 0;
|
|
206
|
+
const turn = event.data?.turn;
|
|
207
|
+
const step = event.data?.step;
|
|
208
|
+
const previous = state.last;
|
|
209
|
+
const replacing = previous !== null && previous.turn === turn && previous.step === step;
|
|
210
|
+
if (replacing && previous !== null && sameUsage(previous, usage)) return state;
|
|
211
|
+
const takeBack = replacing && previous !== null ? previous : null;
|
|
212
|
+
return {
|
|
213
|
+
cny: Math.max(0, state.cny - (takeBack?.cny ?? 0) + cny),
|
|
214
|
+
usd: Math.max(0, state.usd - (takeBack?.usd ?? 0) + usd),
|
|
215
|
+
partial,
|
|
216
|
+
last: {
|
|
217
|
+
...turn === void 0 ? {} : { turn },
|
|
218
|
+
...step === void 0 ? {} : { step },
|
|
219
|
+
...usage,
|
|
220
|
+
cny,
|
|
221
|
+
usd
|
|
222
|
+
},
|
|
223
|
+
model: modelOf(event) ?? state.model
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function foldSessionCost(events, document) {
|
|
227
|
+
let state = { cny: 0, usd: 0, partial: false, last: null, model: null };
|
|
228
|
+
for (let i = 0; i < events.length; i++) state = applyCostEvent(state, events[i], document);
|
|
229
|
+
return state;
|
|
230
|
+
}
|
|
231
|
+
var STALE_MS = 1e4;
|
|
232
|
+
function createSessionCost(ctx, prices) {
|
|
233
|
+
const states = /* @__PURE__ */ new Map();
|
|
234
|
+
const touchedAt = /* @__PURE__ */ new Map();
|
|
235
|
+
const pending = /* @__PURE__ */ new Map();
|
|
236
|
+
const offPriceChange = prices.onChange(() => {
|
|
237
|
+
states.clear();
|
|
238
|
+
touchedAt.clear();
|
|
239
|
+
});
|
|
240
|
+
function trim() {
|
|
241
|
+
while (states.size > KEPT_SESSIONS) {
|
|
242
|
+
const oldest = states.keys().next().value;
|
|
243
|
+
if (oldest === void 0 || oldest === "") break;
|
|
244
|
+
states.delete(oldest);
|
|
245
|
+
touchedAt.delete(oldest);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function answer(state) {
|
|
249
|
+
return { cny: state.cny, usd: state.usd, partial: state.partial, model: state.model };
|
|
250
|
+
}
|
|
251
|
+
function observe(sessionId, event) {
|
|
252
|
+
const state = states.get(sessionId);
|
|
253
|
+
if (state === void 0) return;
|
|
254
|
+
const document = prices.document();
|
|
255
|
+
if (document === null) return;
|
|
256
|
+
const next = applyCostEvent(state, event, document);
|
|
257
|
+
if (next !== state) states.set(sessionId, next);
|
|
258
|
+
touchedAt.set(sessionId, Date.now());
|
|
259
|
+
}
|
|
260
|
+
function ensure() {
|
|
261
|
+
if (typeof ctx.on !== "function") return;
|
|
262
|
+
ctx.on("session/event", (session, event) => {
|
|
263
|
+
const id = typeof session?.id === "string" ? session.id : "";
|
|
264
|
+
if (id !== "") observe(id, event);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
async function get(sessionId) {
|
|
268
|
+
prices.ensure();
|
|
269
|
+
const held = states.get(sessionId);
|
|
270
|
+
const touched = touchedAt.get(sessionId);
|
|
271
|
+
if (held !== void 0 && touched !== void 0 && Date.now() - touched < STALE_MS) return answer(held);
|
|
272
|
+
const running = pending.get(sessionId);
|
|
273
|
+
if (running !== void 0) return running;
|
|
274
|
+
const document = prices.document();
|
|
275
|
+
if (document === null) return null;
|
|
276
|
+
const work = readEvents(ctx, PACKAGE_NAME, sessionId, "the session cost").then((events) => {
|
|
277
|
+
pending.delete(sessionId);
|
|
278
|
+
if (events === null) return null;
|
|
279
|
+
const state = foldSessionCost(events, document);
|
|
280
|
+
states.set(sessionId, state);
|
|
281
|
+
touchedAt.set(sessionId, Date.now());
|
|
282
|
+
trim();
|
|
283
|
+
return answer(state);
|
|
284
|
+
}, (error) => {
|
|
285
|
+
pending.delete(sessionId);
|
|
286
|
+
throw error;
|
|
287
|
+
});
|
|
288
|
+
pending.set(sessionId, work);
|
|
289
|
+
return work;
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
ensure,
|
|
293
|
+
get,
|
|
294
|
+
dispose() {
|
|
295
|
+
offPriceChange();
|
|
296
|
+
states.clear();
|
|
297
|
+
touchedAt.clear();
|
|
298
|
+
pending.clear();
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
export {
|
|
303
|
+
applyCostEvent,
|
|
304
|
+
createSessionCost,
|
|
305
|
+
foldSessionCost
|
|
306
|
+
};
|
package/lib/host/settings.js
CHANGED
|
@@ -4,7 +4,18 @@ var PACKAGE_NAME = "dsh-claude-style";
|
|
|
4
4
|
// packages/contracts/src/services.ts
|
|
5
5
|
var OFFICIAL_MODEL_GROUP = "deepseek-official";
|
|
6
6
|
|
|
7
|
+
// packages/contracts/src/prices.ts
|
|
8
|
+
var CURRENCY_CNY = "CNY";
|
|
9
|
+
var CURRENCY_USD = "USD";
|
|
10
|
+
var CURRENCIES = [CURRENCY_CNY, CURRENCY_USD];
|
|
11
|
+
var CURRENCY_SYMBOL = {
|
|
12
|
+
[CURRENCY_CNY]: "\xA5",
|
|
13
|
+
[CURRENCY_USD]: "$"
|
|
14
|
+
};
|
|
15
|
+
|
|
7
16
|
// packages/contracts/src/prefs.ts
|
|
17
|
+
var BILLING_OFF = "off";
|
|
18
|
+
var BILLING_CHOICES = [BILLING_OFF, ...CURRENCIES];
|
|
8
19
|
var BRAND_CLAUDE = "claude";
|
|
9
20
|
var BRAND_DEEPSEEK = "deepseek";
|
|
10
21
|
var BRAND_DEEPSEEK_LEGACY = "off";
|
|
@@ -122,6 +133,8 @@ var PREFS = {
|
|
|
122
133
|
mascotScope: { type: "choice", values: MASCOT_SCOPES, default: MASCOT_SCOPE_ALL },
|
|
123
134
|
permissionsControl: { type: "boolean", default: true },
|
|
124
135
|
statsPosition: { type: "choice", values: STATS_POSITIONS, default: STATS_POSITION_CONTEXT },
|
|
136
|
+
/** Which official price table the session's cost follows, or off for no figure at all. */
|
|
137
|
+
billingCurrency: { type: "choice", values: BILLING_CHOICES, default: CURRENCY_CNY },
|
|
125
138
|
workspaceView: { type: "boolean", default: true },
|
|
126
139
|
dockCards: { type: "boolean", default: true },
|
|
127
140
|
sidebarSearch: { type: "boolean", default: true },
|
package/lib/host/usage-fold.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"fallback": "en",
|
|
4
4
|
"brands": {
|
|
5
|
-
"note": "Which brand mark a row shows. Every id names a vendored lockup in packages/assets/src/icons/combine/; scripts/build.mjs fails the build on an id that is not there. This is presentation data, kept out of the bundle like the rest of this document, and it is separate from the copy rules above: a vendor's mark and a vendor's description line change for different reasons. `providers` is deliberately empty: a model no rule claims draws **no** lockup rather than its provider's, because the reseller's own lockup on a model it did not make reads as a wrong answer rather than a missing one. `models` is an ordered, anchored rule list resolved against the model id, exactly like `families` and with the same discipline: first match wins, so a broad rule must sit below the narrow ones and every rule must name its vendor (`step` cannot match `step-3.7-flash` by accident only if no earlier rule already claimed it). A brand with no lockup renders the label without a mark.",
|
|
5
|
+
"note": "Which brand mark a row shows. Every id names a vendored lockup in packages/assets/src/icons/combine/; scripts/build.mjs fails the build on an id that is not there. This is presentation data, kept out of the bundle like the rest of this document, and it is separate from the copy rules above: a vendor's mark and a vendor's description line change for different reasons. `providers` is deliberately empty: a model no rule claims draws **no** lockup rather than its provider's, because the reseller's own lockup on a model it did not make reads as a wrong answer rather than a missing one. `models` is an ordered, anchored rule list resolved against the model's id and then its display name (D79), exactly like `families` and with the same discipline: first match wins, so a broad rule must sit below the narrow ones and every rule must name its vendor (`step` cannot match `step-3.7-flash` by accident only if no earlier rule already claimed it). A brand with no lockup renders the label without a mark.",
|
|
6
6
|
"providers": {},
|
|
7
7
|
"models": [
|
|
8
8
|
{
|