@kenz1117/dsh-ui-usage-billing 1.0.26 → 1.0.28
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 +5 -5
- package/lib/index.js +127 -54
- package/lib/types/aggregate.d.ts +2 -2
- package/lib/types/client/live-cost.d.ts +10 -0
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/client/pricing.d.ts +8 -2
- package/lib/types/client/usage-billing-settings.d.ts +3 -1
- package/lib/types/index.d.ts +1 -1
- package/package.json +14 -8
package/lib/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
|
6
6
|
import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
|
|
7
7
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
8
8
|
import z from "@deepseek-ai/schemastery";
|
|
9
|
+
import { SessionLogOffset } from "@deepseek-ai/dsh-session/types";
|
|
9
10
|
import { createHash, createHmac } from "node:crypto";
|
|
10
11
|
//#region lib/types/client/plan-knowledge.js
|
|
11
12
|
/**
|
|
@@ -1817,12 +1818,6 @@ function foldUsage(acc, usage, key, subscription, timeMs, official = false) {
|
|
|
1817
1818
|
}
|
|
1818
1819
|
}
|
|
1819
1820
|
/**
|
|
1820
|
-
* 联网搜索辅助请求的单次费用估算默认值(人民币元)。DeepSeek 官方对搜索请求
|
|
1821
|
-
* (web_search 服务端工具注入上下文)照常计费,实测每次约 0.01~0.03 元,取中值;
|
|
1822
|
-
* 部署可在插件配置 `searchCallEstimateCny` 覆盖(设 0 关闭估算)。
|
|
1823
|
-
*/
|
|
1824
|
-
const DEFAULT_SEARCH_CALL_ESTIMATE_CNY = .02;
|
|
1825
|
-
/**
|
|
1826
1821
|
* Fold one auxiliary web-search LLM request (issue #15) into an accumulator.
|
|
1827
1822
|
* 这类调用绕过对话通道直连官方端点,日志只记请求(无响应/用量事件),token
|
|
1828
1823
|
* 不可知:按「每次估值」计入费用并单独累计 `searchCalls`,不产生 token 维度。
|
|
@@ -2029,26 +2024,22 @@ function turnState(turns, turn) {
|
|
|
2029
2024
|
turns.set(turn, fresh);
|
|
2030
2025
|
return fresh;
|
|
2031
2026
|
}
|
|
2032
|
-
/**
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
* @returns the per-session fold (cached by the incremental aggregator).
|
|
2049
|
-
*/
|
|
2050
|
-
function foldSession(events, subscriptionProviders, officialProviderIds, routes = {}, searchCallEstimateCny = DEFAULT_SEARCH_CALL_ESTIMATE_CNY, seedLength = 0) {
|
|
2051
|
-
const fold = {
|
|
2027
|
+
/** 空白折叠状态机(与全量折叠的初值逐字一致)。 */
|
|
2028
|
+
function freshMachine() {
|
|
2029
|
+
return {
|
|
2030
|
+
key: "other",
|
|
2031
|
+
subscription: false,
|
|
2032
|
+
official: false,
|
|
2033
|
+
siteBucket: "unknown",
|
|
2034
|
+
turns: /* @__PURE__ */ new Map(),
|
|
2035
|
+
steps: /* @__PURE__ */ new Map(),
|
|
2036
|
+
lastOpenStepKey: void 0,
|
|
2037
|
+
toolSeen: /* @__PURE__ */ new Set()
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
2040
|
+
/** 空白 fold 的统一构造:foldSession 与聚合器的分片/增量路径共用同一字段初值。 */
|
|
2041
|
+
function freshFold() {
|
|
2042
|
+
return {
|
|
2052
2043
|
total: emptyUsage(),
|
|
2053
2044
|
byModel: /* @__PURE__ */ new Map(),
|
|
2054
2045
|
byDay: /* @__PURE__ */ new Map(),
|
|
@@ -2069,14 +2060,29 @@ function foldSession(events, subscriptionProviders, officialProviderIds, routes
|
|
|
2069
2060
|
},
|
|
2070
2061
|
lastActive: 0
|
|
2071
2062
|
};
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* 把一批事件折进既有 fold(状态机跨批次延续)。全量折叠、超大会话的分片折叠、
|
|
2066
|
+
* 活跃会话的 seq 增量折叠共用同一段循环体,保证三种路径的字段语义逐字一致。
|
|
2067
|
+
* @param fold - 累计目标(原地修改)。
|
|
2068
|
+
* @param machine - 折叠状态机:进入时为上一批次末态,返回时为本批次末态。
|
|
2069
|
+
* @param events - 本批次事件(日志顺序)。
|
|
2070
|
+
* @param subscriptionProviders - provider ids billed through subscription plans.
|
|
2071
|
+
* @param officialProviderIds - 官方直连 provider 集合(undefined = 按 deepseek 前缀判定)。
|
|
2072
|
+
* @param routes - 当前 provider 路由视图(中转站归组)。
|
|
2073
|
+
* @param searchCallEstimateCny - 联网搜索请求的单次费用估算(人民币元)。
|
|
2074
|
+
* @param seedLength - fork 血缘边界(seq 低于它的事件是父会话种子,跳过);
|
|
2075
|
+
* 来源为存储元数据的 `inheritedEventCount`。
|
|
2076
|
+
*/
|
|
2077
|
+
function foldInto(fold, machine, events, subscriptionProviders, officialProviderIds, routes, searchCallEstimateCny, seedLength) {
|
|
2078
|
+
let key = machine.key;
|
|
2079
|
+
let subscription = machine.subscription;
|
|
2080
|
+
let official = machine.official;
|
|
2081
|
+
let siteBucket = machine.siteBucket;
|
|
2082
|
+
const turns = machine.turns;
|
|
2083
|
+
const steps = machine.steps;
|
|
2084
|
+
let lastOpenStepKey = machine.lastOpenStepKey;
|
|
2085
|
+
const toolSeen = machine.toolSeen;
|
|
2080
2086
|
for (const event of events) {
|
|
2081
2087
|
if (seedLength > 0 && typeof event.seq === "number" && Number.isFinite(event.seq) && event.seq < seedLength) continue;
|
|
2082
2088
|
fold.lastActive = Math.max(fold.lastActive, event.time);
|
|
@@ -2224,7 +2230,15 @@ function foldSession(events, subscriptionProviders, officialProviderIds, routes
|
|
|
2224
2230
|
}
|
|
2225
2231
|
}
|
|
2226
2232
|
}
|
|
2227
|
-
|
|
2233
|
+
machine.key = key;
|
|
2234
|
+
machine.subscription = subscription;
|
|
2235
|
+
machine.official = official;
|
|
2236
|
+
machine.siteBucket = siteBucket;
|
|
2237
|
+
machine.lastOpenStepKey = lastOpenStepKey;
|
|
2238
|
+
}
|
|
2239
|
+
/** 从轮次状态派生 fold.turns(每批次结束后重派生;半开轮次跨批次保留)。 */
|
|
2240
|
+
function refreshTurns(fold, machine) {
|
|
2241
|
+
fold.turns = [...machine.turns.values()].filter((state) => state.input > 0 || state.output > 0).sort((a, b) => a.turn - b.turn).map((state) => ({
|
|
2228
2242
|
turn: state.turn,
|
|
2229
2243
|
model: state.model,
|
|
2230
2244
|
input: state.input,
|
|
@@ -2235,7 +2249,6 @@ function foldSession(events, subscriptionProviders, officialProviderIds, routes
|
|
|
2235
2249
|
startedAt: state.startedAt === Number.MAX_SAFE_INTEGER ? fold.lastActive : state.startedAt,
|
|
2236
2250
|
...state.endedAt === void 0 ? {} : { endedAt: state.endedAt }
|
|
2237
2251
|
}));
|
|
2238
|
-
return fold;
|
|
2239
2252
|
}
|
|
2240
2253
|
/**
|
|
2241
2254
|
* 生成一个 step 的性能样本;无效 / 超出 sane 上限(15 分钟)时返回 undefined,
|
|
@@ -2315,6 +2328,23 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
2315
2328
|
/** 每次聚合取最新的 provider 路由视图(中转站零配置发现);缺省按空处理(全部未知路由)。 */
|
|
2316
2329
|
const routesOf = () => options.resolveRoutes?.() ?? {};
|
|
2317
2330
|
const searchEstimate = options.searchCallEstimateCny ?? .02;
|
|
2331
|
+
const FOLD_CHUNK_EVENTS = 8e3;
|
|
2332
|
+
/** 把一份(全量或增量)折叠结果登记进 durable ledger;内容无变化时不落盘。 */
|
|
2333
|
+
const recordLedger = (id, cwd, stamp, fold) => {
|
|
2334
|
+
if (options.ledger === void 0) return;
|
|
2335
|
+
const entry = {
|
|
2336
|
+
id,
|
|
2337
|
+
...cwd === void 0 ? {} : { cwd },
|
|
2338
|
+
...stamp === null ? {} : { stamp },
|
|
2339
|
+
foldVersion: 7,
|
|
2340
|
+
fold: serializeFold(fold)
|
|
2341
|
+
};
|
|
2342
|
+
const row = ledger.get(id);
|
|
2343
|
+
if (row === void 0 || row.stamp !== entry.stamp || row.cwd !== entry.cwd || row.foldVersion !== entry.foldVersion || stamp === null && JSON.stringify(row.fold) !== JSON.stringify(entry.fold)) {
|
|
2344
|
+
ledger.set(id, entry);
|
|
2345
|
+
ledgerNeedsSave = true;
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2318
2348
|
const ensureLedgerLoaded = async () => {
|
|
2319
2349
|
if (ledgerLoaded || options.ledger === void 0) return;
|
|
2320
2350
|
ledgerLoaded = true;
|
|
@@ -2376,34 +2406,77 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
2376
2406
|
continue;
|
|
2377
2407
|
}
|
|
2378
2408
|
try {
|
|
2379
|
-
const
|
|
2409
|
+
const cwd = meta.cwd;
|
|
2410
|
+
const ledgerRow = ledger.get(id);
|
|
2411
|
+
if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 7) {
|
|
2412
|
+
const fold = deserializeFold(ledgerRow.fold);
|
|
2413
|
+
cache.set(id, {
|
|
2414
|
+
stamp,
|
|
2415
|
+
fold
|
|
2416
|
+
});
|
|
2417
|
+
folds.push({
|
|
2418
|
+
id,
|
|
2419
|
+
...cwd === void 0 ? {} : { cwd },
|
|
2420
|
+
fold
|
|
2421
|
+
});
|
|
2422
|
+
included.add(id);
|
|
2423
|
+
continue;
|
|
2424
|
+
}
|
|
2425
|
+
const previous = cache.get(id);
|
|
2426
|
+
if (previous?.machine !== void 0 && previous.lastSeq !== void 0) {
|
|
2427
|
+
const from = previous.lastSeq + 1;
|
|
2428
|
+
const { events, inheritedEventCount } = await persistence.readFrom(meta.id, SessionLogOffset(from));
|
|
2429
|
+
const after = await stampOf(meta);
|
|
2430
|
+
if (stamp !== null && after !== stamp) {
|
|
2431
|
+
cache.delete(id);
|
|
2432
|
+
continue;
|
|
2433
|
+
}
|
|
2434
|
+
const first = events[0]?.seq;
|
|
2435
|
+
if (events.length === 0 || first !== from) {
|
|
2436
|
+
cache.delete(id);
|
|
2437
|
+
continue;
|
|
2438
|
+
}
|
|
2439
|
+
foldInto(previous.fold, previous.machine, events, subscriptionProviders, officialProviderIds, routesOf(), searchEstimate, inheritedEventCount);
|
|
2440
|
+
refreshTurns(previous.fold, previous.machine);
|
|
2441
|
+
previous.lastSeq = events[events.length - 1]?.seq ?? previous.lastSeq;
|
|
2442
|
+
previous.stamp = stamp;
|
|
2443
|
+
cache.delete(id);
|
|
2444
|
+
cache.set(id, previous);
|
|
2445
|
+
folds.push({
|
|
2446
|
+
id,
|
|
2447
|
+
...cwd === void 0 ? {} : { cwd },
|
|
2448
|
+
fold: previous.fold
|
|
2449
|
+
});
|
|
2450
|
+
included.add(id);
|
|
2451
|
+
recordLedger(id, cwd, stamp, previous.fold);
|
|
2452
|
+
continue;
|
|
2453
|
+
}
|
|
2454
|
+
const { events, inheritedEventCount } = await persistence.readFrom(meta.id, SessionLogOffset(0));
|
|
2380
2455
|
const after = await stampOf(meta);
|
|
2381
2456
|
if (stamp !== null && after !== stamp) continue;
|
|
2382
|
-
const fold =
|
|
2457
|
+
const fold = freshFold();
|
|
2458
|
+
const machine = freshMachine();
|
|
2459
|
+
for (let start = 0; start < events.length; start += FOLD_CHUNK_EVENTS) {
|
|
2460
|
+
foldInto(fold, machine, events.slice(start, start + FOLD_CHUNK_EVENTS), subscriptionProviders, officialProviderIds, routesOf(), searchEstimate, inheritedEventCount);
|
|
2461
|
+
if (start + FOLD_CHUNK_EVENTS < events.length) await new Promise((resolve) => {
|
|
2462
|
+
setImmediate(resolve);
|
|
2463
|
+
});
|
|
2464
|
+
}
|
|
2465
|
+
refreshTurns(fold, machine);
|
|
2466
|
+
const last = events[events.length - 1];
|
|
2383
2467
|
cache.set(id, {
|
|
2384
2468
|
stamp,
|
|
2385
|
-
fold
|
|
2469
|
+
fold,
|
|
2470
|
+
machine,
|
|
2471
|
+
...last !== void 0 && typeof last.seq === "number" ? { lastSeq: last.seq } : {}
|
|
2386
2472
|
});
|
|
2387
2473
|
folds.push({
|
|
2388
2474
|
id,
|
|
2389
|
-
...
|
|
2475
|
+
...cwd === void 0 ? {} : { cwd },
|
|
2390
2476
|
fold
|
|
2391
2477
|
});
|
|
2392
2478
|
included.add(id);
|
|
2393
|
-
|
|
2394
|
-
const entry = {
|
|
2395
|
-
id,
|
|
2396
|
-
...meta.cwd === void 0 ? {} : { cwd: meta.cwd },
|
|
2397
|
-
...stamp === null ? {} : { stamp },
|
|
2398
|
-
foldVersion: 7,
|
|
2399
|
-
fold: serializeFold(fold)
|
|
2400
|
-
};
|
|
2401
|
-
const previous = ledger.get(id);
|
|
2402
|
-
if (previous === void 0 || previous.stamp !== entry.stamp || previous.cwd !== entry.cwd || previous.foldVersion !== entry.foldVersion || stamp === null && JSON.stringify(previous.fold) !== JSON.stringify(entry.fold)) {
|
|
2403
|
-
ledger.set(id, entry);
|
|
2404
|
-
ledgerNeedsSave = true;
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2479
|
+
recordLedger(id, cwd, stamp, fold);
|
|
2407
2480
|
} catch (error) {
|
|
2408
2481
|
skipped.push(id);
|
|
2409
2482
|
console.warn("[usage-billing] skip unreadable session", id, error);
|
package/lib/types/aggregate.d.ts
CHANGED
|
@@ -475,10 +475,10 @@ export declare function messageTextLength(message: unknown): number;
|
|
|
475
475
|
* (default: any `deepseek`-prefixed id). Others count as third-party.
|
|
476
476
|
* @param routes - 当前 provider 路由视图(中转站归组)。
|
|
477
477
|
* @param searchCallEstimateCny - 联网搜索请求的单次费用估算(人民币元;0 关闭估算)。
|
|
478
|
-
* @param seedLength - 持久化的 fork
|
|
478
|
+
* @param seedLength - 持久化的 fork 血缘边界(存储元数据 `inheritedEventCount`,缺省 0):
|
|
479
479
|
* fork 子会话日志里 `seq < seedLength` 的事件拷贝自父会话、已在父会话计费,
|
|
480
480
|
* 折叠时跳过以避免重复计费;resume 续写复用同一会话日志(每次续写追加一个
|
|
481
|
-
* `session/end-seed`
|
|
481
|
+
* `session/end-seed` 标记)但继承切割保持原值,历史段照常计费。
|
|
482
482
|
* @returns the per-session fold (cached by the incremental aggregator).
|
|
483
483
|
*/
|
|
484
484
|
export declare function foldSession(events: readonly {
|
|
@@ -78,4 +78,14 @@ export interface LiveCostBarProps {
|
|
|
78
78
|
* @param props - framework session identity and locale.
|
|
79
79
|
*/
|
|
80
80
|
export declare function LiveCostBar({ sessionId, t }: LiveCostBarProps): React.ReactNode;
|
|
81
|
+
/**
|
|
82
|
+
* Render the compact inline chip for the composer tool row (position
|
|
83
|
+
* 「模型选择前」). Occupies a fixed minimal footprint — tier glyph + session
|
|
84
|
+
* spend — so it never crowds the model picker; the full breakdown (switch
|
|
85
|
+
* countdown, turn cost, quota alerts) rides the host Tooltip primitive (the
|
|
86
|
+
* native title never renders in the webview), and a low quota tints the chip
|
|
87
|
+
* amber/red instead of growing it.
|
|
88
|
+
* @param props - framework session identity and locale.
|
|
89
|
+
*/
|
|
90
|
+
export declare function LiveCostChip({ sessionId, t }: LiveCostBarProps): React.ReactNode;
|
|
81
91
|
//# sourceMappingURL=live-cost.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Locale dictionaries for the usage billing surface. */
|
|
2
|
-
export type UsageBillingKey = 'title' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'trendEmpty' | 'budget' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'project' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'actual' | 'pricing' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'band' | 'close' | 'footer' | 'footerCredit' | 'lastUpdated' | 'noData' | 'todayRate' | 'rateLive' | 'rateBuiltin' | 'promoBadge' | 'promoUntil' | 'promoOpenEnded' | 'pricingTip' | 'pricingUnit' | 'pricingNotes' | 'ubPeak' | 'ubOff' | 'ubStd' | 'peakBand' | 'pricingSource' | 'noteCache' | 'noteBand' | 'noteSource' | 'balance' | 'balanceUnconfigured' | 'balanceUnauthorized' | 'balanceUnreachable' | 'uncatalogued' | 'estimatedPricing' | 'balanceDays' | 'balanceLowBody' | 'reconcileDrift' | 'reconcileDismiss' | 'subscriptionNotConfigured' | 'subscriptionUnauthorized' | 'subscriptionUnavailable' | 'subscriptionInvalid' | 'subscriptionRateLimited' | 'subscriptionSession' | 'subscriptionWeekly' | 'subscriptionMonthly' | 'subscriptionBilling' | 'subscriptionRemaining' | 'subscriptionExhausted' | 'subscriptionReset' | 'subscriptionNoApi' | 'floatWindow' | 'floatModeCombined' | 'floatModeSubscription' | 'floatMode' | 'floatTargets' | 'floatWindowHint' | 'floatNoTargets' | 'floatNoTargetsHint' | 'cardDisplay' | 'cardDisplayHint' | 'cardMetric' | 'cardMetricMoney' | 'cardMetricTokens' | 'triggerMonthTokens' | 'subscriptionsStale' | 'staleLedgerNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceOffPeak' | 'userPriceCurrency' | 'userPriceAdd' | '
|
|
2
|
+
export type UsageBillingKey = 'title' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'trendEmpty' | 'budget' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'project' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'actual' | 'pricing' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'band' | 'close' | 'footer' | 'footerCredit' | 'lastUpdated' | 'noData' | 'todayRate' | 'rateLive' | 'rateBuiltin' | 'promoBadge' | 'promoUntil' | 'promoOpenEnded' | 'pricingTip' | 'pricingUnit' | 'pricingNotes' | 'ubPeak' | 'ubOff' | 'ubStd' | 'peakBand' | 'pricingSource' | 'noteCache' | 'noteBand' | 'noteSource' | 'balance' | 'balanceUnconfigured' | 'balanceUnauthorized' | 'balanceUnreachable' | 'uncatalogued' | 'estimatedPricing' | 'balanceDays' | 'balanceLowBody' | 'reconcileDrift' | 'reconcileDismiss' | 'subscriptionNotConfigured' | 'subscriptionUnauthorized' | 'subscriptionUnavailable' | 'subscriptionInvalid' | 'subscriptionRateLimited' | 'subscriptionSession' | 'subscriptionWeekly' | 'subscriptionMonthly' | 'subscriptionBilling' | 'subscriptionRemaining' | 'subscriptionExhausted' | 'subscriptionReset' | 'subscriptionNoApi' | 'floatWindow' | 'floatModeCombined' | 'floatModeSubscription' | 'floatMode' | 'floatTargets' | 'floatWindowHint' | 'floatNoTargets' | 'floatNoTargetsHint' | 'cardDisplay' | 'cardDisplayHint' | 'cardMetric' | 'cardMetricMoney' | 'cardMetricTokens' | 'triggerMonthTokens' | 'subscriptionsStale' | 'staleLedgerNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceOffPeak' | 'userPriceNormal' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemoveSelected' | 'userPriceSelect' | 'sessionStaleBadge' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'workspaces' | 'workspacesHint' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabPricing' | 'tabSettings' | 'budgetHint' | 'peakAlertHint' | 'peakAlertDescPeak' | 'peakAlertDescOff' | 'export' | 'exportCsvDay' | 'exportCsvSession' | 'exportJson' | 'peakShare' | 'peakSharePerCall' | 'offPeakSavings' | 'perfMax' | 'weekCost' | 'roleCost' | 'roleUser' | 'roleAssistant' | 'roleTool' | 'roleHint' | 'tierPeak' | 'tierOff' | 'tierToPeak' | 'tierToOff' | 'tierAlertEnterPeak' | 'tierAlertEnterOff' | 'peakAlertTitlePeak' | 'peakAlertTitleOff' | 'peakAlert' | 'peakAlertLeadMin' | 'peakAlertPos' | 'peakAlertMode' | 'peakAlertPosCorner' | 'peakAlertPosCenter' | 'peakAlertModePeak' | 'peakAlertModeOff' | 'peakAlertModeBoth' | 'peakAlertWebNotify' | 'peakAlertPreview' | 'planTypeCode' | 'planTypeToken' | 'subscriptionFeePerMonth' | 'triggerToday' | 'triggerMonth' | 'subscriptionIncluded' | 'free' | 'official' | 'thirdParty' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'perfAll' | 'perfChartEmpty' | 'heatmapYear' | 'heatmapMonth' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginVersion' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | 'tokenCacheHitRate' | 'tokenReasoningShare' | 'tokenReasoningShort' | 'tokenIo' | 'tokenPeak' | 'tokenDaily' | 'tokenByModel' | 'tokenMiss' | 'tokenHit' | 'tokenOutput' | 'tokenViewStructure' | 'tokenViewModel' | 'tokenHitShort' | 'tokenMissShort' | 'tokenTotal' | 'tokenShare' | 'usageStatsTool' | 'usageStatsToolHint' | 'balanceGranted' | 'balanceTopped' | 'balanceDaily' | 'balanceDaysLong' | 'balanceDaysUnit' | 'popTodayModel' | 'popNoConsumption' | 'popTitle' | 'popDirectLead' | 'popSubLead' | 'unpricedHint' | 'searchEstimateHint' | 'siteListDisplay' | 'siteListDisplayHint' | 'liveCostBar' | 'liveCostBarHint' | 'liveCostBarPosition' | 'liveCostBarPosBelow' | 'liveCostBarPosAbove' | 'liveCostBarPosToolbar' | 'exportCsvSite' | 'panelRelayQuota' | 'relayBalance' | 'relayNoQuota' | 'relayWindowUsed' | 'relayKindNewApi' | 'relayKindSub2Api' | 'relayKindUnknown';
|
|
3
3
|
export declare const NS = "usageBilling";
|
|
4
4
|
export declare const zh: Record<UsageBillingKey, string>;
|
|
5
5
|
export declare const en: Record<UsageBillingKey, string>;
|
|
@@ -165,9 +165,15 @@ export declare function isPeakHour(beijingHour: number): boolean;
|
|
|
165
165
|
*/
|
|
166
166
|
export declare function tierAt(timeMs: number | null | undefined): PriceTierId;
|
|
167
167
|
/**
|
|
168
|
-
*
|
|
168
|
+
* 当前峰谷档位与距下一切换的时长。导出供测试:纯函数。
|
|
169
|
+
*
|
|
170
|
+
* 下一切换点统一定义为档位真正变化的最近边界:自当前时刻起逐天扫描工作日的
|
|
171
|
+
* 09:00 / 12:00 / 14:00 / 18:00,候选时刻的档位由 {@link tierAt} 判定——
|
|
172
|
+
* 周末(周六/周日)北京全天低谷、没有边界,扫描自然跳过;工作日深夜跨周末
|
|
173
|
+
* 时落到周一 09:00 而非周末伪边界(issue #33)。
|
|
174
|
+
* 最坏情形(周五 18:00 后 → 周一 09:00)约 63h,7 天窗口必然覆盖。
|
|
169
175
|
* @param nowMs - 当前时刻(epoch 毫秒)。
|
|
170
|
-
* @returns
|
|
176
|
+
* @returns 当前档位与到下一切换边界的毫秒数。
|
|
171
177
|
*/
|
|
172
178
|
export declare function tierCountdown(nowMs: number): {
|
|
173
179
|
tier: PriceTierId;
|
|
@@ -82,8 +82,10 @@ export declare function saveSiteListPrefs(prefs: SiteListPrefs): void;
|
|
|
82
82
|
export interface LiveCostBarPrefs {
|
|
83
83
|
/** 是否显示输入框下方的即时代费条胶囊(默认 true:保持历史行为)。 */
|
|
84
84
|
show: boolean;
|
|
85
|
+
/** 胶囊位置:below = 输入框下方(默认);above = 输入框上方;toolbar = 工具行模型选择前的内联 chip。 */
|
|
86
|
+
position: 'below' | 'above' | 'toolbar';
|
|
85
87
|
}
|
|
86
|
-
/**
|
|
88
|
+
/** 默认即时代费条偏好:显示在输入框下方(升级用户零感知)。 */
|
|
87
89
|
export declare const DEFAULT_LIVE_COST_BAR_PREFS: LiveCostBarPrefs;
|
|
88
90
|
/** localStorage key(与其他 `dsh.ui-usage-billing.*` 偏好同命名空间)。 */
|
|
89
91
|
export declare const LIVE_COST_BAR_STORAGE_KEY = "dsh.ui-usage-billing.livecost";
|
package/lib/types/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
13
13
|
import type { Context } from '@deepseek-ai/cordis';
|
|
14
14
|
import type { CredentialProvider } from '@deepseek-ai/dsh-credentials';
|
|
15
|
-
import {
|
|
15
|
+
import type { SettingsProvider } from '@deepseek-ai/dsh-settings';
|
|
16
16
|
import { type UsageLedgerStore } from './aggregate.ts';
|
|
17
17
|
import type { CustomBalanceConfig, DeclaredEndpointConfig, SubscriptionPlanConfig } from './pricing-shared.ts';
|
|
18
18
|
import { type IdentifiedSubscriptionPlan, type SubscriptionKeys } from './subscriptions.ts';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenz1117/dsh-ui-usage-billing",
|
|
3
3
|
"description": "Usage billing dashboard for DeepSeek Harness: sidebar cost metrics plus a full dashboard modal, priced from a current multi-provider catalog with real usage aggregated from session logs.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.28",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"deepseek",
|
|
@@ -63,6 +63,12 @@
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsc -b tsconfig.json && tsdown",
|
|
68
|
+
"bundle": "tsdown",
|
|
69
|
+
"watch": "tsdown --watch",
|
|
70
|
+
"test": "vitest run"
|
|
71
|
+
},
|
|
66
72
|
"license": "MIT",
|
|
67
73
|
"engines": {
|
|
68
74
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -96,6 +102,7 @@
|
|
|
96
102
|
"@deepseek-ai/cordis": "*",
|
|
97
103
|
"@deepseek-ai/cordis-plugin-include": "^1.0.7",
|
|
98
104
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.3",
|
|
105
|
+
"@deepseek-ai/dsh-api-gateway": "0.1.2-rc.1",
|
|
99
106
|
"@deepseek-ai/dsh-api-remotes": "0.1.2-rc.1",
|
|
100
107
|
"@deepseek-ai/dsh-api-session-controller": "0.1.2-rc.1",
|
|
101
108
|
"@deepseek-ai/dsh-atomic-write": "0.1.2-rc.1",
|
|
@@ -103,6 +110,8 @@
|
|
|
103
110
|
"@deepseek-ai/dsh-client-store": "0.1.2-rc.1",
|
|
104
111
|
"@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1",
|
|
105
112
|
"@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
|
|
113
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
|
|
114
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.1.2-rc.1",
|
|
106
115
|
"@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
|
|
107
116
|
"@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
|
|
108
117
|
"@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1",
|
|
@@ -113,6 +122,7 @@
|
|
|
113
122
|
"@deepseek-ai/dsh-session-persistence": "0.1.2-rc.1",
|
|
114
123
|
"@deepseek-ai/dsh-settings": "0.1.2-rc.1",
|
|
115
124
|
"@deepseek-ai/dsh-tools": "0.1.2-rc.1",
|
|
125
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.2-rc.1",
|
|
116
126
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
117
127
|
"@testing-library/dom": "^10.4.1",
|
|
118
128
|
"@testing-library/react": "^16.1.0",
|
|
@@ -124,6 +134,7 @@
|
|
|
124
134
|
"react-dom": "^18.2.0",
|
|
125
135
|
"semver": "^7.7.2",
|
|
126
136
|
"tsdown": "^0.22.2",
|
|
137
|
+
"typescript": "^6.0.3",
|
|
127
138
|
"use-sync-external-store": "1.2.0",
|
|
128
139
|
"vitest": "^4.1.8"
|
|
129
140
|
},
|
|
@@ -133,10 +144,5 @@
|
|
|
133
144
|
"lib/client.js",
|
|
134
145
|
"lib/types/**/*.d.ts",
|
|
135
146
|
"cordis.patch.yml"
|
|
136
|
-
]
|
|
137
|
-
|
|
138
|
-
"bundle": "tsdown",
|
|
139
|
-
"watch": "tsdown --watch",
|
|
140
|
-
"test": "vitest run"
|
|
141
|
-
}
|
|
142
|
-
}
|
|
147
|
+
]
|
|
148
|
+
}
|