dsh-context 0.53.4 → 0.54.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/lib/client.js +212 -8
- package/lib/index.d.ts +37 -1
- package/lib/index.js +176 -11
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -56,6 +56,8 @@ window.__ModuleLoader__.load({
|
|
|
56
56
|
"timing.callTimes": "{n}次",
|
|
57
57
|
"timing.toolTimes": "{n}次",
|
|
58
58
|
"timing.empty": "暂无耗时数据 · 对话开始后这里会显示时长分布",
|
|
59
|
+
"timing.tps": "{tps} tok/s",
|
|
60
|
+
"timing.tpsTip": "输出速度(TPS):累计解码输出 tokens ÷ 累计解码时长,与 DeepSeek Harness 会话统计同口径。",
|
|
59
61
|
"tokens.title": "Token 统计",
|
|
60
62
|
"tokens.total": "总用量",
|
|
61
63
|
"tokens.output": "输出",
|
|
@@ -320,6 +322,7 @@ window.__ModuleLoader__.load({
|
|
|
320
322
|
"ov.title": "上下文洞察",
|
|
321
323
|
"ov.unavailable": "会话列表暂不可用",
|
|
322
324
|
"ov.range.label": "统计范围",
|
|
325
|
+
"ov.range.24h": "近 24 小时",
|
|
323
326
|
"ov.range.7d": "近 7 天",
|
|
324
327
|
"ov.range.30d": "近 30 天",
|
|
325
328
|
"ov.range.all": "全部",
|
|
@@ -329,6 +332,9 @@ window.__ModuleLoader__.load({
|
|
|
329
332
|
"ov.kpi.sessionsSub": "含 {n} 个会话",
|
|
330
333
|
"ov.kpi.toolSub": "工具执行 {dur}",
|
|
331
334
|
"ov.kpi.wallSub": "模型调用 {n}",
|
|
335
|
+
"balance.title": "DeepSeek 余额",
|
|
336
|
+
"balance.tip.granted": "赠送余额",
|
|
337
|
+
"balance.tip.toppedUp": "充值余额",
|
|
332
338
|
"ov.heat.title": "活跃度",
|
|
333
339
|
"ov.heat.sub": "按日期筛选",
|
|
334
340
|
"ov.heat.empty": "暂无活跃数据 — 会话产生模型请求后,这里会显示每日用量",
|
|
@@ -422,6 +428,8 @@ window.__ModuleLoader__.load({
|
|
|
422
428
|
"timing.callTimes": "{n} calls",
|
|
423
429
|
"timing.toolTimes": "{n} runs",
|
|
424
430
|
"timing.empty": "No timing data yet — durations appear as the conversation runs",
|
|
431
|
+
"timing.tps": "{tps} tok/s",
|
|
432
|
+
"timing.tpsTip": "Output speed (TPS): summed decoded output tokens over summed decode time — the same pairing as the harness session stats.",
|
|
425
433
|
"tokens.title": "Token Stats",
|
|
426
434
|
"tokens.total": "Total",
|
|
427
435
|
"tokens.output": "Output",
|
|
@@ -686,6 +694,7 @@ window.__ModuleLoader__.load({
|
|
|
686
694
|
"ov.title": "Context Insights",
|
|
687
695
|
"ov.unavailable": "The session list is unavailable",
|
|
688
696
|
"ov.range.label": "Range",
|
|
697
|
+
"ov.range.24h": "Last 24 hours",
|
|
689
698
|
"ov.range.7d": "Last 7 days",
|
|
690
699
|
"ov.range.30d": "Last 30 days",
|
|
691
700
|
"ov.range.all": "All",
|
|
@@ -695,6 +704,9 @@ window.__ModuleLoader__.load({
|
|
|
695
704
|
"ov.kpi.sessionsSub": "across {n} sessions",
|
|
696
705
|
"ov.kpi.toolSub": "tool runs {dur}",
|
|
697
706
|
"ov.kpi.wallSub": "{n} model calls",
|
|
707
|
+
"balance.title": "DeepSeek balance",
|
|
708
|
+
"balance.tip.granted": "Granted balance",
|
|
709
|
+
"balance.tip.toppedUp": "Topped-up balance",
|
|
698
710
|
"ov.heat.title": "Activity",
|
|
699
711
|
"ov.heat.sub": "Filter by date",
|
|
700
712
|
"ov.heat.empty": "No activity yet — daily usage appears once sessions make model requests",
|
|
@@ -5970,6 +5982,7 @@ window.__ModuleLoader__.load({
|
|
|
5970
5982
|
}
|
|
5971
5983
|
/** The range window's start instant (epoch ms), or null for "all". */
|
|
5972
5984
|
function rangeStartOf(range, now) {
|
|
5985
|
+
if (range === "24h") return now - 864e5;
|
|
5973
5986
|
if (range === "7d") return now - 6048e5;
|
|
5974
5987
|
if (range === "30d") return now - 2592e6;
|
|
5975
5988
|
return null;
|
|
@@ -6199,6 +6212,181 @@ window.__ModuleLoader__.load({
|
|
|
6199
6212
|
return t("ov.time.d", { n: Math.floor(hours / 24) });
|
|
6200
6213
|
}
|
|
6201
6214
|
//#endregion
|
|
6215
|
+
//#region src/client/balance.ts
|
|
6216
|
+
const BALANCE_ROUTE = "/api/dsh-context/balance";
|
|
6217
|
+
/** Where the last live figure is remembered across opens, per browser. */
|
|
6218
|
+
const STORAGE_KEY = "dsh-context:platform-balance";
|
|
6219
|
+
/** One platform amount ('110.00', or an already-numeric producer variant), or null. */
|
|
6220
|
+
function amountOf(value) {
|
|
6221
|
+
const n = typeof value === "string" ? Number(value) : value;
|
|
6222
|
+
return typeof n === "number" && Number.isFinite(n) && n >= 0 ? n : null;
|
|
6223
|
+
}
|
|
6224
|
+
/**
|
|
6225
|
+
* Narrow the delivered payload to a render-safe value (the same boundary
|
|
6226
|
+
* rigor as `timelineOf`): each entry's currency and amounts re-proved, an
|
|
6227
|
+
* entry failing the shape drops whole, and a payload with no valid entry is
|
|
6228
|
+
* no balance — the caller renders nothing rather than half a figure.
|
|
6229
|
+
*/
|
|
6230
|
+
function platformBalanceOf(value) {
|
|
6231
|
+
const data = asRecord(value);
|
|
6232
|
+
if (data === null) return null;
|
|
6233
|
+
const infos = Array.isArray(data.balances) ? data.balances : [];
|
|
6234
|
+
const balances = [];
|
|
6235
|
+
for (const info of infos) try {
|
|
6236
|
+
const entry = asRecord(info);
|
|
6237
|
+
if (entry === null) continue;
|
|
6238
|
+
const currency = entry.currency;
|
|
6239
|
+
const total = amountOf(entry.total);
|
|
6240
|
+
const granted = amountOf(entry.granted);
|
|
6241
|
+
const toppedUp = amountOf(entry.toppedUp);
|
|
6242
|
+
if (typeof currency !== "string" || currency === "" || total === null || granted === null || toppedUp === null) continue;
|
|
6243
|
+
balances.push({
|
|
6244
|
+
currency,
|
|
6245
|
+
total,
|
|
6246
|
+
granted,
|
|
6247
|
+
toppedUp
|
|
6248
|
+
});
|
|
6249
|
+
} catch {
|
|
6250
|
+
continue;
|
|
6251
|
+
}
|
|
6252
|
+
return balances.length > 0 ? {
|
|
6253
|
+
isAvailable: data.isAvailable === true,
|
|
6254
|
+
balances
|
|
6255
|
+
} : null;
|
|
6256
|
+
}
|
|
6257
|
+
/**
|
|
6258
|
+
* The entry matching the display currency (zh → CNY, en → USD), falling back
|
|
6259
|
+
* to the account's first entry when the platform reports none in it — a
|
|
6260
|
+
* number in the account's own currency beats no number.
|
|
6261
|
+
*/
|
|
6262
|
+
function balanceEntryOf(balance, currency) {
|
|
6263
|
+
if (balance === null || balance === void 0 || balance.balances.length === 0) return null;
|
|
6264
|
+
const want = currency === "cny" ? "CNY" : "USD";
|
|
6265
|
+
return balance.balances.find((entry) => entry.currency === want) ?? balance.balances[0];
|
|
6266
|
+
}
|
|
6267
|
+
let cached$1 = null;
|
|
6268
|
+
/** The route read in flight, so a second open joins it instead of starting one. */
|
|
6269
|
+
let refreshing = null;
|
|
6270
|
+
/** The storage this module reads and writes; swapped in tests. */
|
|
6271
|
+
let storage;
|
|
6272
|
+
function storageFn() {
|
|
6273
|
+
if (storage !== void 0) return storage;
|
|
6274
|
+
try {
|
|
6275
|
+
const found = globalThis.localStorage;
|
|
6276
|
+
storage = found === null || found === void 0 ? null : found;
|
|
6277
|
+
} catch {
|
|
6278
|
+
storage = null;
|
|
6279
|
+
}
|
|
6280
|
+
return storage;
|
|
6281
|
+
}
|
|
6282
|
+
/** The stored figure, or `null` when absent, unreadable, or no longer a valid balance. */
|
|
6283
|
+
function storedBalance() {
|
|
6284
|
+
try {
|
|
6285
|
+
const raw = storageFn()?.getItem(STORAGE_KEY);
|
|
6286
|
+
return raw === null || raw === void 0 ? null : platformBalanceOf(JSON.parse(raw));
|
|
6287
|
+
} catch {
|
|
6288
|
+
return null;
|
|
6289
|
+
}
|
|
6290
|
+
}
|
|
6291
|
+
function rememberBalance(value) {
|
|
6292
|
+
try {
|
|
6293
|
+
storageFn()?.setItem(STORAGE_KEY, JSON.stringify(value));
|
|
6294
|
+
} catch {}
|
|
6295
|
+
}
|
|
6296
|
+
/** One route POST, narrowed through `platformBalanceOf`. Never rejects. */
|
|
6297
|
+
async function readRoute() {
|
|
6298
|
+
try {
|
|
6299
|
+
const response = await fetch(BALANCE_ROUTE, {
|
|
6300
|
+
method: "POST",
|
|
6301
|
+
headers: { "content-type": "application/json" }
|
|
6302
|
+
});
|
|
6303
|
+
if (!response.ok) return null;
|
|
6304
|
+
const r = asRecord(await response.json());
|
|
6305
|
+
if (r === null || r.ok !== true || r.value === null || r.value === void 0) return null;
|
|
6306
|
+
return platformBalanceOf(r.value);
|
|
6307
|
+
} catch {
|
|
6308
|
+
return null;
|
|
6309
|
+
}
|
|
6310
|
+
}
|
|
6311
|
+
/**
|
|
6312
|
+
* One route read per open, with `onRefresh` once it lands on a figure: the
|
|
6313
|
+
* capsule paints the remembered total immediately and settles on the live
|
|
6314
|
+
* one. `refreshing` is assigned before anything awaits, so an open arriving
|
|
6315
|
+
* while a read is still in flight joins it rather than starting a second one;
|
|
6316
|
+
* it paints the remembered figure and takes the in-flight read's landing for
|
|
6317
|
+
* its own. A failed or absent read notifies nobody and keeps serving the
|
|
6318
|
+
* remembered figure, so an offline open never trades a number for a blank
|
|
6319
|
+
* pill; `null` (whether pending, absent, or failed) means nothing to show yet.
|
|
6320
|
+
*/
|
|
6321
|
+
function readPlatformBalance(onRefresh) {
|
|
6322
|
+
if (refreshing === null) refreshing = (async () => {
|
|
6323
|
+
const value = await readRoute();
|
|
6324
|
+
if (value === null) return;
|
|
6325
|
+
cached$1 = value;
|
|
6326
|
+
rememberBalance(value);
|
|
6327
|
+
onRefresh(value);
|
|
6328
|
+
})().finally(() => {
|
|
6329
|
+
refreshing = null;
|
|
6330
|
+
});
|
|
6331
|
+
return cached$1 ?? storedBalance();
|
|
6332
|
+
}
|
|
6333
|
+
//#endregion
|
|
6334
|
+
//#region src/client/components/balanceCapsule.tsx
|
|
6335
|
+
/**
|
|
6336
|
+
* BalanceCapsule — the DeepSeek open-platform balance pill in the Context
|
|
6337
|
+
* Dashboard's header (the plugin's one account-level figure, beside the
|
|
6338
|
+
* session-level ones). Clicking it opens the platform's usage console in a
|
|
6339
|
+
* new tab. Mounted only by the dashboard header; it paints the figure the
|
|
6340
|
+
* previous open remembered while client/balance.ts revalidates it in the
|
|
6341
|
+
* background, and renders NOTHING when nothing is remembered and the route
|
|
6342
|
+
* answers nothing — the pill exists only when there is a figure to show. The
|
|
6343
|
+
* non-zero parts of the breakdown (topped-up / granted) ride the harness
|
|
6344
|
+
* Tooltip primitive — immediate on hover, where a native `title` lags — in
|
|
6345
|
+
* the entry's own currency (the pill itself carries the total); the entry
|
|
6346
|
+
* follows the active locale (zh → CNY) with the account's first currency as
|
|
6347
|
+
* the fallback.
|
|
6348
|
+
*/
|
|
6349
|
+
/** The symbol prefix for the currencies the platform serves; others show their code. */
|
|
6350
|
+
function symbolOf(currency) {
|
|
6351
|
+
if (currency === "CNY") return "¥";
|
|
6352
|
+
if (currency === "USD") return "$";
|
|
6353
|
+
return currency + " ";
|
|
6354
|
+
}
|
|
6355
|
+
/** The platform console page behind the capsule's click — usage and top-ups. */
|
|
6356
|
+
const USAGE_URL = "https://platform.deepseek.com/usage";
|
|
6357
|
+
function makeBalanceCapsule(ctx, kit) {
|
|
6358
|
+
const { t } = kit;
|
|
6359
|
+
return function BalanceCapsule() {
|
|
6360
|
+
const [balance, setBalance] = (0, react.useState)(() => readPlatformBalance((v) => {
|
|
6361
|
+
setBalance(v);
|
|
6362
|
+
}));
|
|
6363
|
+
const locale = ctx.locale;
|
|
6364
|
+
const entry = balanceEntryOf(balance, (typeof locale.getLocale === "function" ? locale.getLocale().active : "en") === "zh" ? "cny" : "usd");
|
|
6365
|
+
if (entry === null) return null;
|
|
6366
|
+
const money = (amount) => symbolOf(entry.currency) + amount.toFixed(2);
|
|
6367
|
+
const tipLines = [...entry.toppedUp > 0 ? [t("balance.tip.toppedUp") + ": " + money(entry.toppedUp)] : [], ...entry.granted > 0 ? [t("balance.tip.granted") + ": " + money(entry.granted)] : []];
|
|
6368
|
+
const pill = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
6369
|
+
className: "lc-ov-balance",
|
|
6370
|
+
"aria-label": tipLines.length > 0 ? tipLines.join("\n") : void 0,
|
|
6371
|
+
href: USAGE_URL,
|
|
6372
|
+
target: "_blank",
|
|
6373
|
+
rel: "noreferrer",
|
|
6374
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6375
|
+
className: "lc-ov-balance-label",
|
|
6376
|
+
children: t("balance.title")
|
|
6377
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6378
|
+
className: "lc-ov-balance-value",
|
|
6379
|
+
children: money(entry.total)
|
|
6380
|
+
})]
|
|
6381
|
+
});
|
|
6382
|
+
return tipLines.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
6383
|
+
label: tipLines.join("\n"),
|
|
6384
|
+
side: "bottom",
|
|
6385
|
+
children: pill
|
|
6386
|
+
}) : pill;
|
|
6387
|
+
};
|
|
6388
|
+
}
|
|
6389
|
+
//#endregion
|
|
6202
6390
|
//#region src/shared/days.ts
|
|
6203
6391
|
/**
|
|
6204
6392
|
* The local-day key (`YYYY-MM-DD`) shared by the activity fold (host) and the
|
|
@@ -6647,6 +6835,7 @@ window.__ModuleLoader__.load({
|
|
|
6647
6835
|
* closes the panel.
|
|
6648
6836
|
*/
|
|
6649
6837
|
const RANGES = [
|
|
6838
|
+
"24h",
|
|
6650
6839
|
"7d",
|
|
6651
6840
|
"30d",
|
|
6652
6841
|
"all"
|
|
@@ -6660,6 +6849,7 @@ window.__ModuleLoader__.load({
|
|
|
6660
6849
|
const { t, fmtDuration } = kit;
|
|
6661
6850
|
const Heatmap = makeHeatmap(kit);
|
|
6662
6851
|
const OverviewCard = makeOverviewCard(kit);
|
|
6852
|
+
const BalanceCapsule = makeBalanceCapsule(ctx, kit);
|
|
6663
6853
|
const ErrorBoundary = makeErrorBoundary(t);
|
|
6664
6854
|
/** The display currency follows the active locale (zh → CNY), read per render — the slot outlet re-renders on a locale switch. */
|
|
6665
6855
|
function activeCurrency() {
|
|
@@ -6744,6 +6934,7 @@ window.__ModuleLoader__.load({
|
|
|
6744
6934
|
className: "lc-ov-title",
|
|
6745
6935
|
children: t("ov.title")
|
|
6746
6936
|
}),
|
|
6937
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BalanceCapsule, {}),
|
|
6747
6938
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6748
6939
|
className: "lc-gran lc-ov-range",
|
|
6749
6940
|
role: "group",
|
|
@@ -9494,7 +9685,7 @@ window.__ModuleLoader__.load({
|
|
|
9494
9685
|
return function PluginInfo() {
|
|
9495
9686
|
const [latest, setLatest] = (0, react.useState)(null);
|
|
9496
9687
|
(0, react.useEffect)(() => {
|
|
9497
|
-
if ("0.
|
|
9688
|
+
if ("0.54.1".includes("-dev")) return;
|
|
9498
9689
|
let on = true;
|
|
9499
9690
|
fetchLatestVersion().then((v) => {
|
|
9500
9691
|
if (on && v) setLatest(v);
|
|
@@ -9503,8 +9694,8 @@ window.__ModuleLoader__.load({
|
|
|
9503
9694
|
on = false;
|
|
9504
9695
|
};
|
|
9505
9696
|
}, []);
|
|
9506
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
9507
|
-
const nameText = "dsh-context (v0.
|
|
9697
|
+
const update = latest !== null && isNewerVersion(latest, "0.54.1") ? latest : null;
|
|
9698
|
+
const nameText = "dsh-context (v0.54.1)";
|
|
9508
9699
|
const nameValue = [nameText];
|
|
9509
9700
|
if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9510
9701
|
className: "lc-pi-update",
|
|
@@ -10235,6 +10426,14 @@ window.__ModuleLoader__.load({
|
|
|
10235
10426
|
tools: "var(--color-teal-500)",
|
|
10236
10427
|
other: "var(--color-slate-400)"
|
|
10237
10428
|
};
|
|
10429
|
+
/**
|
|
10430
|
+
* The decode-throughput figure, formatted exactly as the harness's own
|
|
10431
|
+
* session-stats seat formats it: whole tokens from ten up, one decimal below.
|
|
10432
|
+
*/
|
|
10433
|
+
function formatTokensPerSecond(tps) {
|
|
10434
|
+
const clamped = Math.max(0, tps);
|
|
10435
|
+
return clamped >= 10 ? String(Math.round(clamped)) : String(Math.round(clamped * 10) / 10);
|
|
10436
|
+
}
|
|
10238
10437
|
function makeStatsTiming(kit, Donut) {
|
|
10239
10438
|
const { t, fmt, fmtDuration, fmtShare } = kit;
|
|
10240
10439
|
const SliceList = makeSliceList(kit);
|
|
@@ -10242,6 +10441,7 @@ window.__ModuleLoader__.load({
|
|
|
10242
10441
|
const [hoverKey, setHoverKey] = (0, react.useState)(null);
|
|
10243
10442
|
const timing = props.timing;
|
|
10244
10443
|
const wall = timing !== null && Number.isFinite(timing.wallMs) && timing.wallMs > 0 ? timing.wallMs : 0;
|
|
10444
|
+
const tps = timing !== null && typeof timing.speedMs === "number" && Number.isFinite(timing.speedMs) && timing.speedMs > 0 && typeof timing.speedTokens === "number" && Number.isFinite(timing.speedTokens) && timing.speedTokens >= 0 ? formatTokensPerSecond(timing.speedTokens / (timing.speedMs / 1e3)) : null;
|
|
10245
10445
|
let segments = [];
|
|
10246
10446
|
let rows = [];
|
|
10247
10447
|
if (timing !== null && (wall > 0 || timing.calls > 0 || timing.toolCalls > 0)) {
|
|
@@ -10350,12 +10550,16 @@ window.__ModuleLoader__.load({
|
|
|
10350
10550
|
}
|
|
10351
10551
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10352
10552
|
className: "lc-card lc-col-stats lc-col-donut flex-1 min-w-[min(360px,100%)]",
|
|
10353
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
10553
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10354
10554
|
className: "lc-card-title",
|
|
10355
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10555
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10356
10556
|
className: "lc-card-title-text",
|
|
10357
10557
|
children: t("timing.title")
|
|
10358
|
-
})
|
|
10558
|
+
}), tps !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10559
|
+
className: "lc-timing-tps",
|
|
10560
|
+
title: t("timing.tpsTip"),
|
|
10561
|
+
children: t("timing.tps", { tps })
|
|
10562
|
+
})]
|
|
10359
10563
|
}), rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10360
10564
|
className: "lc-empty",
|
|
10361
10565
|
children: t("timing.empty")
|
|
@@ -11284,7 +11488,7 @@ window.__ModuleLoader__.load({
|
|
|
11284
11488
|
}
|
|
11285
11489
|
//#endregion
|
|
11286
11490
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/stats.css.mjs
|
|
11287
|
-
const css$13 = ".lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:color-mix(in srgb, var(--color-neutral-500) 18%, transparent)}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:4px 20px;min-width:0;display:grid;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-hint{text-decoration:none}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-pi-row-btn .lc-pi-value{font-weight:400}";
|
|
11491
|
+
const css$13 = ".lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-timing-tps{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);font-variant-numeric:tabular-nums;cursor:default;border-radius:999px;flex:none;margin-left:auto;padding:1px 8px;font-size:12px;line-height:18px}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:color-mix(in srgb, var(--color-neutral-500) 18%, transparent)}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:4px 20px;min-width:0;display:grid;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-hint{text-decoration:none}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-pi-row-btn .lc-pi-value{font-weight:400}";
|
|
11288
11492
|
const tagId$13 = "dsh-context/stats.css";
|
|
11289
11493
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
|
|
11290
11494
|
const tag = document.createElement("style");
|
|
@@ -11427,7 +11631,7 @@ window.__ModuleLoader__.load({
|
|
|
11427
11631
|
}
|
|
11428
11632
|
//#endregion
|
|
11429
11633
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/overview.css.mjs
|
|
11430
|
-
const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);max-height:min(86vh,900px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:13px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:13px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
|
|
11634
|
+
const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);max-height:min(86vh,900px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:inherit;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex}.lc-ov-balance:hover .lc-ov-balance-value{text-decoration:underline}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:13px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:13px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
|
|
11431
11635
|
const tagId = "dsh-context/overview.css";
|
|
11432
11636
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
11433
11637
|
const tag = document.createElement("style");
|
package/lib/index.d.ts
CHANGED
|
@@ -638,6 +638,17 @@ interface TimingTotals {
|
|
|
638
638
|
textMs?: number;
|
|
639
639
|
/** Tool-call-argument decode slice of `genMs`. */
|
|
640
640
|
toolArgMs?: number;
|
|
641
|
+
/**
|
|
642
|
+
* The decode-throughput seat, paired exactly as the harness's own
|
|
643
|
+
* session-stats fold pairs them: `speedTokens` sums provider-reported
|
|
644
|
+
* output tokens and `speedMs` the first-token → assistant-message
|
|
645
|
+
* windows, over the calls that carried BOTH a first-token stamp and a
|
|
646
|
+
* usage report — a subset of `genMs`, which counts every token-stamped
|
|
647
|
+
* call regardless of usage. Additive-optional: cached rows written before
|
|
648
|
+
* the seat existed lack them, and the card falls back to no chip.
|
|
649
|
+
*/
|
|
650
|
+
speedTokens?: number;
|
|
651
|
+
speedMs?: number;
|
|
641
652
|
/** Completed model calls (assistant messages folded). */
|
|
642
653
|
calls: number;
|
|
643
654
|
/** Summed per-call durations of completed tool calls. */
|
|
@@ -796,10 +807,35 @@ interface HeaderRecord {
|
|
|
796
807
|
interface ContextHeaders {
|
|
797
808
|
headers: HeaderRecord[];
|
|
798
809
|
}
|
|
810
|
+
/** One currency's DeepSeek open-platform balance figures. */
|
|
811
|
+
interface PlatformBalanceEntry {
|
|
812
|
+
/** The ISO code the platform reported (`CNY` / `USD`). */
|
|
813
|
+
currency: string;
|
|
814
|
+
/** Total available funds: `granted` + `toppedUp`. Derived here rather than read
|
|
815
|
+
* from the platform's own `total_balance`, which rounds independently of its
|
|
816
|
+
* parts and can land a cent away from what the breakdown beside it shows. */
|
|
817
|
+
total: number;
|
|
818
|
+
/** The not-expired granted (gift) balance. */
|
|
819
|
+
granted: number;
|
|
820
|
+
/** The topped-up balance. */
|
|
821
|
+
toppedUp: number;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* The DeepSeek open-platform balance, served by the plugin's
|
|
825
|
+
* `/api/dsh-context/balance` fetch route (host/balance.ts). `null` on the
|
|
826
|
+
* wire — and nothing rendered client-side — whenever the platform is not
|
|
827
|
+
* configured or the read fails: the capsule only ever shows a live figure.
|
|
828
|
+
*/
|
|
829
|
+
interface PlatformBalance {
|
|
830
|
+
/** Whether the platform reports the balance sufficient for API calls. */
|
|
831
|
+
isAvailable: boolean;
|
|
832
|
+
/** One entry per currency the account holds; at least one. */
|
|
833
|
+
balances: PlatformBalanceEntry[];
|
|
834
|
+
}
|
|
799
835
|
//#endregion
|
|
800
836
|
//#region src/host/index.d.ts
|
|
801
837
|
export declare const name = "dsh-context";
|
|
802
838
|
export declare const inject: string[];
|
|
803
839
|
export declare function apply(ctx: Context, config: Config): void;
|
|
804
840
|
//#endregion
|
|
805
|
-
export type { ActivityDay, ActivityState, Category, ContextActivity, ContextEventRecord, ContextHeaders, ContextTimeline, ContextTimelineDetail, HeaderRecord, HeaderTool, HeadersState, RequestRecord, Snapshot, SurfaceNode, TimelineCounts, TimelineLast, TimelineState };
|
|
841
|
+
export type { ActivityDay, ActivityState, Category, ContextActivity, ContextEventRecord, ContextHeaders, ContextTimeline, ContextTimelineDetail, HeaderRecord, HeaderTool, HeadersState, PlatformBalance, PlatformBalanceEntry, RequestRecord, Snapshot, SurfaceNode, TimelineCounts, TimelineLast, TimelineState };
|
package/lib/index.js
CHANGED
|
@@ -1430,11 +1430,12 @@ function applyTimeline(state, event, bounds) {
|
|
|
1430
1430
|
};
|
|
1431
1431
|
if (data && typeof data.turn === "number") record.turn = data.turn;
|
|
1432
1432
|
if (data && typeof data.step === "number") record.step = data.step;
|
|
1433
|
+
let output = null;
|
|
1433
1434
|
if (usage !== null && typeof usage === "object") {
|
|
1434
1435
|
const input = tokenCountOf(usage.inputTokens);
|
|
1435
1436
|
const cacheRead = tokenCountOf(usage.cacheReadTokens);
|
|
1436
1437
|
const cacheWrite = tokenCountOf(usage.cacheWriteTokens);
|
|
1437
|
-
|
|
1438
|
+
output = tokenCountOf(usage.outputTokens);
|
|
1438
1439
|
if (input !== null || cacheRead !== null || cacheWrite !== null || output !== null) {
|
|
1439
1440
|
record.prompt = (input ?? 0) + (cacheRead ?? 0) + (cacheWrite ?? 0);
|
|
1440
1441
|
if (cacheRead !== null) record.cacheRead = cacheRead;
|
|
@@ -1456,6 +1457,10 @@ function applyTimeline(state, event, bounds) {
|
|
|
1456
1457
|
if (firstToken !== void 0) {
|
|
1457
1458
|
timing.ttftMs += durOf(stepStart.time, firstToken);
|
|
1458
1459
|
timing.genMs += durOf(firstToken, event.time);
|
|
1460
|
+
if (output !== null) {
|
|
1461
|
+
timing.speedMs = (timing.speedMs ?? 0) + durOf(firstToken, event.time);
|
|
1462
|
+
timing.speedTokens = (timing.speedTokens ?? 0) + output;
|
|
1463
|
+
}
|
|
1459
1464
|
if (stepStart.decode !== void 0) {
|
|
1460
1465
|
const decode = { ...stepStart.decode };
|
|
1461
1466
|
if (stepStart.block !== void 0) decode[stepStart.block.kind] += durOf(stepStart.block.since, event.time);
|
|
@@ -2057,6 +2062,156 @@ function createToolAttribution(ctx) {
|
|
|
2057
2062
|
return { ownerOf: (name) => mcpSourceOf(name) ?? live.get(name) ?? pinnedSourceOf(name) ?? (boot.has(name) ? "<unknown-plugin>" : void 0) };
|
|
2058
2063
|
}
|
|
2059
2064
|
//#endregion
|
|
2065
|
+
//#region src/host/balance.ts
|
|
2066
|
+
/** The plugin's balance route, under the authenticated `/api` fence. */
|
|
2067
|
+
const BALANCE_ROUTE = "/api/dsh-context/balance";
|
|
2068
|
+
/** The settings namespace llm-deepseek installs its connection section under. */
|
|
2069
|
+
const DEEPSEEK_SETTINGS_NS = "llm-deepseek";
|
|
2070
|
+
/** llm-deepseek's default credential ref (the env-var name its section resolves). */
|
|
2071
|
+
const DEFAULT_API_KEY_ENV = "DEEPSEEK_API_KEY";
|
|
2072
|
+
/** The platform's public API root (llm-deepseek's PUBLIC_BASE_URL default). */
|
|
2073
|
+
const PUBLIC_BASE_URL = "https://api.deepseek.com";
|
|
2074
|
+
/** One platform read's whole budget — never worth blocking the route longer. */
|
|
2075
|
+
const FETCH_TIMEOUT_MS = 1e4;
|
|
2076
|
+
/** Narrow an unknown value to a string-keyed record, or null. */
|
|
2077
|
+
function asRecord$1(value) {
|
|
2078
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2079
|
+
}
|
|
2080
|
+
/** One platform amount ('110.00', or an already-numeric producer variant), or null. */
|
|
2081
|
+
function amountOf(value) {
|
|
2082
|
+
const n = typeof value === "string" ? Number(value) : value;
|
|
2083
|
+
return typeof n === "number" && Number.isFinite(n) && n >= 0 ? n : null;
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* Resolve the DeepSeek connection facts exactly as llm-deepseek serves its
|
|
2087
|
+
* requests: its settings section for the credential ref and endpoint, the
|
|
2088
|
+
* credentials service for the key. `null` whenever any fact is missing —
|
|
2089
|
+
* the platform is simply not configured for this deployment.
|
|
2090
|
+
*/
|
|
2091
|
+
async function resolveFacts(ctx) {
|
|
2092
|
+
const settings = ctx.get("settings");
|
|
2093
|
+
const section = typeof settings?.get === "function" ? asRecord$1(settings.get(DEEPSEEK_SETTINGS_NS)) : null;
|
|
2094
|
+
if (section === null) return null;
|
|
2095
|
+
const apiKeyEnv = typeof section.apiKeyEnv === "string" && section.apiKeyEnv !== "" ? section.apiKeyEnv : DEFAULT_API_KEY_ENV;
|
|
2096
|
+
const baseUrl = typeof section.baseURL === "string" && section.baseURL !== "" ? section.baseURL : PUBLIC_BASE_URL;
|
|
2097
|
+
const credentials = ctx.get("credentials");
|
|
2098
|
+
const resolve = typeof credentials?.resolve === "function" ? credentials.resolve.bind(credentials) : void 0;
|
|
2099
|
+
if (resolve === void 0) return null;
|
|
2100
|
+
let hit;
|
|
2101
|
+
try {
|
|
2102
|
+
hit = await resolve(apiKeyEnv);
|
|
2103
|
+
} catch {
|
|
2104
|
+
return null;
|
|
2105
|
+
}
|
|
2106
|
+
const apiKey = typeof hit?.value === "string" ? hit.value : "";
|
|
2107
|
+
return apiKey === "" ? null : {
|
|
2108
|
+
baseUrl,
|
|
2109
|
+
apiKey
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2112
|
+
/**
|
|
2113
|
+
* Narrow the platform's payload to the wire value (the boundary rigor every
|
|
2114
|
+
* parser owes untrusted input): each entry's currency and parts are re-proved
|
|
2115
|
+
* and the total is the two summed — the platform's own `total_balance` rounds
|
|
2116
|
+
* independently of them, so it can land a cent away from what the breakdown
|
|
2117
|
+
* beside it adds up to, and the viewer's arithmetic is the authority. An entry
|
|
2118
|
+
* failing the shape drops whole, and a payload with no valid entry is no
|
|
2119
|
+
* balance at all.
|
|
2120
|
+
*/
|
|
2121
|
+
function balanceOfPayload(value) {
|
|
2122
|
+
const data = asRecord$1(value);
|
|
2123
|
+
if (data === null) return null;
|
|
2124
|
+
const infos = Array.isArray(data.balance_infos) ? data.balance_infos : [];
|
|
2125
|
+
const balances = [];
|
|
2126
|
+
for (const info of infos) try {
|
|
2127
|
+
const entry = asRecord$1(info);
|
|
2128
|
+
if (entry === null) continue;
|
|
2129
|
+
const currency = entry.currency;
|
|
2130
|
+
const granted = amountOf(entry.granted_balance);
|
|
2131
|
+
const toppedUp = amountOf(entry.topped_up_balance);
|
|
2132
|
+
if (typeof currency !== "string" || currency === "" || granted === null || toppedUp === null) continue;
|
|
2133
|
+
balances.push({
|
|
2134
|
+
currency,
|
|
2135
|
+
total: granted + toppedUp,
|
|
2136
|
+
granted,
|
|
2137
|
+
toppedUp
|
|
2138
|
+
});
|
|
2139
|
+
} catch {
|
|
2140
|
+
continue;
|
|
2141
|
+
}
|
|
2142
|
+
return balances.length > 0 ? {
|
|
2143
|
+
isAvailable: data.is_available === true,
|
|
2144
|
+
balances
|
|
2145
|
+
} : null;
|
|
2146
|
+
}
|
|
2147
|
+
/** The outbound platform fetch through the real global fetch. */
|
|
2148
|
+
const defaultFetcher = (url, init) => fetch(url, init);
|
|
2149
|
+
let fetcher = defaultFetcher;
|
|
2150
|
+
let inFlight = null;
|
|
2151
|
+
/**
|
|
2152
|
+
* One platform read for these facts, shared by the identical reads already in
|
|
2153
|
+
* flight. Each read that finds none goes to the platform: the account's
|
|
2154
|
+
* balance moves with every request the harness bills, so a remembered figure
|
|
2155
|
+
* would disagree with the console page the capsule links to. The client's own
|
|
2156
|
+
* open-time read is the rate limit. Every failure path — transport, timeout,
|
|
2157
|
+
* non-ok status, malformed payload — resolves `null`; the route never throws
|
|
2158
|
+
* into the transport.
|
|
2159
|
+
*/
|
|
2160
|
+
async function readBalance(facts) {
|
|
2161
|
+
if (inFlight !== null) return inFlight;
|
|
2162
|
+
inFlight = (async () => {
|
|
2163
|
+
let value = null;
|
|
2164
|
+
try {
|
|
2165
|
+
const response = await fetcher(facts.baseUrl.replace(/\/+$/, "") + "/user/balance", {
|
|
2166
|
+
headers: {
|
|
2167
|
+
accept: "application/json",
|
|
2168
|
+
authorization: "Bearer " + facts.apiKey
|
|
2169
|
+
},
|
|
2170
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
2171
|
+
});
|
|
2172
|
+
if (response.ok) value = balanceOfPayload(await response.json());
|
|
2173
|
+
} catch {}
|
|
2174
|
+
return value;
|
|
2175
|
+
})();
|
|
2176
|
+
const settled = inFlight;
|
|
2177
|
+
try {
|
|
2178
|
+
return await settled;
|
|
2179
|
+
} finally {
|
|
2180
|
+
inFlight = null;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
/**
|
|
2184
|
+
* Serve the balance route whenever the connection service composes (see
|
|
2185
|
+
* host/detail.ts for the load-order contract). The registration rides the
|
|
2186
|
+
* injected fiber: the service unloading withdraws the route. A rejecting
|
|
2187
|
+
* registry only closes the capsule — never the plugin.
|
|
2188
|
+
*/
|
|
2189
|
+
function watchBalanceChannel(ctx) {
|
|
2190
|
+
ctx.inject(["connection"], (c) => {
|
|
2191
|
+
const connection = c.get("connection");
|
|
2192
|
+
const register = typeof connection?.fetch?.register === "function" ? connection.fetch.register.bind(connection.fetch) : void 0;
|
|
2193
|
+
if (register === void 0) return;
|
|
2194
|
+
const handler = async () => {
|
|
2195
|
+
const facts = await resolveFacts(ctx);
|
|
2196
|
+
const value = facts !== null ? await readBalance(facts) : null;
|
|
2197
|
+
return Response.json({
|
|
2198
|
+
ok: true,
|
|
2199
|
+
value
|
|
2200
|
+
}, { headers: { "cache-control": "no-store" } });
|
|
2201
|
+
};
|
|
2202
|
+
try {
|
|
2203
|
+
c.effect(() => register({
|
|
2204
|
+
path: BALANCE_ROUTE,
|
|
2205
|
+
methods: ["POST"],
|
|
2206
|
+
requestBody: "buffered",
|
|
2207
|
+
fetch: handler
|
|
2208
|
+
}), "dsh-context: balance route");
|
|
2209
|
+
} catch {
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
//#endregion
|
|
2060
2215
|
//#region src/host/backfill.ts
|
|
2061
2216
|
/** The plugin's warm-up trigger route, under the authenticated `/api` fence. */
|
|
2062
2217
|
const BACKFILL_ROUTE = "/api/dsh-context/backfill";
|
|
@@ -2067,8 +2222,15 @@ const BACKFILL_ROUTE = "/api/dsh-context/backfill";
|
|
|
2067
2222
|
* workspace sidebar rides it), so the pass breathes between sessions.
|
|
2068
2223
|
*/
|
|
2069
2224
|
const YIELD_MS = 100;
|
|
2070
|
-
/**
|
|
2071
|
-
|
|
2225
|
+
/**
|
|
2226
|
+
* The `name` family dsh raises for a durable log it cannot interpret. The
|
|
2227
|
+
* format edge throws `SessionFormatUnsupportedMigrationError`, but the
|
|
2228
|
+
* persistence seam translates it — together with every other "intact yet not
|
|
2229
|
+
* interpretable" refusal — into `SessionFormatUnsupportedError` before the
|
|
2230
|
+
* error escapes (issue #75): classify by the family prefix, since either
|
|
2231
|
+
* exact name may be the one that arrives.
|
|
2232
|
+
*/
|
|
2233
|
+
const UNSUPPORTED_FORMAT = "SessionFormatUnsupported";
|
|
2072
2234
|
function asRecord(value) {
|
|
2073
2235
|
return value !== null && typeof value === "object" ? value : null;
|
|
2074
2236
|
}
|
|
@@ -2123,15 +2285,15 @@ function isLive(sessions, id) {
|
|
|
2123
2285
|
}
|
|
2124
2286
|
}
|
|
2125
2287
|
/**
|
|
2126
|
-
* Whether the cold read failed because the running harness refuses
|
|
2127
|
-
*
|
|
2128
|
-
*
|
|
2129
|
-
*
|
|
2130
|
-
*
|
|
2288
|
+
* Whether the cold read failed because the running harness refuses this log's
|
|
2289
|
+
* format — a permanent, source-side refusal (the artifact is left unchanged,
|
|
2290
|
+
* so retrying cannot succeed). Matched by the error's documented `name`, so no
|
|
2291
|
+
* dsh symbol needs importing and every supported baseline classifies alike: an
|
|
2292
|
+
* unknown error shape just falls to the warn path.
|
|
2131
2293
|
*/
|
|
2132
|
-
function
|
|
2294
|
+
function isUnsupportedFormat(error) {
|
|
2133
2295
|
try {
|
|
2134
|
-
return asRecord(error)?.name
|
|
2296
|
+
return String(asRecord(error)?.name).startsWith(UNSUPPORTED_FORMAT);
|
|
2135
2297
|
} catch {
|
|
2136
2298
|
return false;
|
|
2137
2299
|
}
|
|
@@ -2210,7 +2372,7 @@ function watchActivityBackfill(ctx) {
|
|
|
2210
2372
|
cache.coldSnapshot(log.header, log.inheritedEventCount, log.events);
|
|
2211
2373
|
folded++;
|
|
2212
2374
|
} catch (error) {
|
|
2213
|
-
if (
|
|
2375
|
+
if (isUnsupportedFormat(error)) {
|
|
2214
2376
|
refused++;
|
|
2215
2377
|
ctx.logger.debug(`dsh-context: projection backfill skipped "${header.id}" (${messageOf(error)})`);
|
|
2216
2378
|
} else ctx.logger.warn(`dsh-context: projection backfill skipped "${header.id}" (${messageOf(error)})`);
|
|
@@ -2727,6 +2889,8 @@ const timingTotalsSchema = z.object({
|
|
|
2727
2889
|
reasoningMs: z.number().nonnegative().optional(),
|
|
2728
2890
|
textMs: z.number().nonnegative().optional(),
|
|
2729
2891
|
toolArgMs: z.number().nonnegative().optional(),
|
|
2892
|
+
speedTokens: z.number().nonnegative().optional(),
|
|
2893
|
+
speedMs: z.number().nonnegative().optional(),
|
|
2730
2894
|
calls: z.number().int().nonnegative(),
|
|
2731
2895
|
toolsMs: z.number().nonnegative(),
|
|
2732
2896
|
toolCalls: z.number().int().nonnegative(),
|
|
@@ -3281,6 +3445,7 @@ function detectHarnessVersion(ctx, resolveUrl = resolveRunningModule, pluginRoot
|
|
|
3281
3445
|
const name = "dsh-context";
|
|
3282
3446
|
const inject = ["sessionProjections"];
|
|
3283
3447
|
function apply(ctx, config) {
|
|
3448
|
+
watchBalanceChannel(ctx);
|
|
3284
3449
|
const harnessVersion = detectHarnessVersion(ctx);
|
|
3285
3450
|
if (harnessVersion !== void 0 && !meetsBaseline(harnessVersion)) {
|
|
3286
3451
|
ctx.sessionProjections.register(createFallbackTimelineDefinition(harnessVersion));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.1",
|
|
4
4
|
"description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
|
|
5
5
|
"author": "bowenliang123",
|
|
6
6
|
"repository": {
|