@kenz1117/dsh-ui-usage-billing 1.1.4 → 1.1.6

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/index.js CHANGED
@@ -266,7 +266,7 @@ function tierAt(timeMs) {
266
266
  }
267
267
  /** 时刻是否落在北京时间周末(周六/周日)。 */
268
268
  function isBeijingWeekend(timeMs) {
269
- const day = new Date(timeMs + 8 * 36e5).getUTCDay();
269
+ const day = new Date(timeMs + 288e5).getUTCDay();
270
270
  return day === 0 || day === 6;
271
271
  }
272
272
  /**
@@ -1809,12 +1809,6 @@ function foldUsage(acc, usage, key, subscription, timeMs, official = false) {
1809
1809
  }
1810
1810
  }
1811
1811
  /**
1812
- * 联网搜索辅助请求的单次费用估算默认值(人民币元)。DeepSeek 官方对搜索请求
1813
- * (web_search 服务端工具注入上下文)照常计费,实测每次约 0.01~0.03 元,取中值;
1814
- * 部署可在插件配置 `searchCallEstimateCny` 覆盖(设 0 关闭估算)。
1815
- */
1816
- const DEFAULT_SEARCH_CALL_ESTIMATE_CNY = .02;
1817
- /**
1818
1812
  * Fold one auxiliary web-search LLM request (issue #15) into an accumulator.
1819
1813
  * 这类调用绕过对话通道直连官方端点,日志只记请求(无响应/用量事件),token
1820
1814
  * 不可知:按「每次估值」计入费用并单独累计 `searchCalls`,不产生 token 维度。
@@ -2021,26 +2015,22 @@ function turnState(turns, turn) {
2021
2015
  turns.set(turn, fresh);
2022
2016
  return fresh;
2023
2017
  }
2024
- /**
2025
- * Fold one session's events into a {@link SessionFold}. 每个 LLM 调用归属到
2026
- * 其 `assistant/message` 自带 `message.source` 记录的模型(agent-loop 落盘时从
2027
- * 当次请求复制,每个调用一条,不依赖稀疏的 request/header);source 缺失时
2028
- * 兜底到最近一次 request/header 的状态。同时提取最新会话标题、最后活跃时间,
2029
- * 并按轮次折叠每轮费用明细(turn/start → turn/end;调用按 (turn) 归组)。
2030
- * @param events - the session's persisted events in log order.
2031
- * @param subscriptionProviders - provider ids billed through subscription plans.
2032
- * @param officialProviderIds - provider ids treated as the official DeepSeek channel
2033
- * (default: any `deepseek`-prefixed id). Others count as third-party.
2034
- * @param routes - 当前 provider 路由视图(中转站归组)。
2035
- * @param searchCallEstimateCny - 联网搜索请求的单次费用估算(人民币元;0 关闭估算)。
2036
- * @param seedLength - 持久化的 fork 血缘边界(`SessionHeader.seedLength`,缺省 0):
2037
- * fork 子会话日志里 `seq < seedLength` 的事件拷贝自父会话、已在父会话计费,
2038
- * 折叠时跳过以避免重复计费;resume 续写复用同一会话日志(每次续写追加一个
2039
- * `session/end-seed` 标记)但 header 保持原 fork 值,历史段照常计费。
2040
- * @returns the per-session fold (cached by the incremental aggregator).
2041
- */
2042
- function foldSession(events, subscriptionProviders, officialProviderIds, routes = {}, searchCallEstimateCny = DEFAULT_SEARCH_CALL_ESTIMATE_CNY, seedLength = 0) {
2043
- const fold = {
2018
+ /** 空白折叠状态机(与全量折叠的初值逐字一致)。 */
2019
+ function freshMachine() {
2020
+ return {
2021
+ key: "other",
2022
+ subscription: false,
2023
+ official: false,
2024
+ siteBucket: "unknown",
2025
+ turns: /* @__PURE__ */ new Map(),
2026
+ steps: /* @__PURE__ */ new Map(),
2027
+ lastOpenStepKey: void 0,
2028
+ toolSeen: /* @__PURE__ */ new Set()
2029
+ };
2030
+ }
2031
+ /** 空白 fold 的统一构造:foldSession 与聚合器的分片/增量路径共用同一字段初值。 */
2032
+ function freshFold() {
2033
+ return {
2044
2034
  total: emptyUsage(),
2045
2035
  byModel: /* @__PURE__ */ new Map(),
2046
2036
  byDay: /* @__PURE__ */ new Map(),
@@ -2061,14 +2051,28 @@ function foldSession(events, subscriptionProviders, officialProviderIds, routes
2061
2051
  },
2062
2052
  lastActive: 0
2063
2053
  };
2064
- let key = "other";
2065
- let subscription = false;
2066
- let official = false;
2067
- let siteBucket = "unknown";
2068
- const turns = /* @__PURE__ */ new Map();
2069
- const steps = /* @__PURE__ */ new Map();
2070
- let lastOpenStepKey;
2071
- const toolSeen = /* @__PURE__ */ new Set();
2054
+ }
2055
+ /**
2056
+ * 把一批事件折进既有 fold(状态机跨批次延续)。全量折叠、超大会话的分片折叠、
2057
+ * 活跃会话的 seq 增量折叠共用同一段循环体,保证三种路径的字段语义逐字一致。
2058
+ * @param fold - 累计目标(原地修改)。
2059
+ * @param machine - 折叠状态机:进入时为上一批次末态,返回时为本批次末态。
2060
+ * @param events - 本批次事件(日志顺序)。
2061
+ * @param subscriptionProviders - provider ids billed through subscription plans.
2062
+ * @param officialProviderIds - 官方直连 provider 集合(undefined = 按 deepseek 前缀判定)。
2063
+ * @param routes - 当前 provider 路由视图(中转站归组)。
2064
+ * @param searchCallEstimateCny - 联网搜索请求的单次费用估算(人民币元)。
2065
+ * @param seedLength - fork 血缘边界(seq 低于它的事件是父会话种子,跳过)。
2066
+ */
2067
+ function foldInto(fold, machine, events, subscriptionProviders, officialProviderIds, routes, searchCallEstimateCny, seedLength) {
2068
+ let key = machine.key;
2069
+ let subscription = machine.subscription;
2070
+ let official = machine.official;
2071
+ let siteBucket = machine.siteBucket;
2072
+ const turns = machine.turns;
2073
+ const steps = machine.steps;
2074
+ let lastOpenStepKey = machine.lastOpenStepKey;
2075
+ const toolSeen = machine.toolSeen;
2072
2076
  for (const event of events) {
2073
2077
  if (seedLength > 0 && typeof event.seq === "number" && Number.isFinite(event.seq) && event.seq < seedLength) continue;
2074
2078
  fold.lastActive = Math.max(fold.lastActive, event.time);
@@ -2216,7 +2220,15 @@ function foldSession(events, subscriptionProviders, officialProviderIds, routes
2216
2220
  }
2217
2221
  }
2218
2222
  }
2219
- fold.turns = [...turns.values()].filter((state) => state.input > 0 || state.output > 0).sort((a, b) => a.turn - b.turn).map((state) => ({
2223
+ machine.key = key;
2224
+ machine.subscription = subscription;
2225
+ machine.official = official;
2226
+ machine.siteBucket = siteBucket;
2227
+ machine.lastOpenStepKey = lastOpenStepKey;
2228
+ }
2229
+ /** 从轮次状态派生 fold.turns(每批次结束后重派生;半开轮次跨批次保留)。 */
2230
+ function refreshTurns(fold, machine) {
2231
+ fold.turns = [...machine.turns.values()].filter((state) => state.input > 0 || state.output > 0).sort((a, b) => a.turn - b.turn).map((state) => ({
2220
2232
  turn: state.turn,
2221
2233
  model: state.model,
2222
2234
  input: state.input,
@@ -2227,7 +2239,6 @@ function foldSession(events, subscriptionProviders, officialProviderIds, routes
2227
2239
  startedAt: state.startedAt === Number.MAX_SAFE_INTEGER ? fold.lastActive : state.startedAt,
2228
2240
  ...state.endedAt === void 0 ? {} : { endedAt: state.endedAt }
2229
2241
  }));
2230
- return fold;
2231
2242
  }
2232
2243
  /**
2233
2244
  * 生成一个 step 的性能样本;无效 / 超出 sane 上限(15 分钟)时返回 undefined,
@@ -2307,6 +2318,23 @@ function createUsageAggregator(persistence, options = {}) {
2307
2318
  /** 每次聚合取最新的 provider 路由视图(中转站零配置发现);缺省按空处理(全部未知路由)。 */
2308
2319
  const routesOf = () => options.resolveRoutes?.() ?? {};
2309
2320
  const searchEstimate = options.searchCallEstimateCny ?? .02;
2321
+ const FOLD_CHUNK_EVENTS = 8e3;
2322
+ /** 把一份(全量或增量)折叠结果登记进 durable ledger;内容无变化时不落盘。 */
2323
+ const recordLedger = (id, cwd, stamp, fold) => {
2324
+ if (options.ledger === void 0) return;
2325
+ const entry = {
2326
+ id,
2327
+ ...cwd === void 0 ? {} : { cwd },
2328
+ ...stamp === null ? {} : { stamp },
2329
+ foldVersion: 6,
2330
+ fold: serializeFold(fold)
2331
+ };
2332
+ const row = ledger.get(id);
2333
+ 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)) {
2334
+ ledger.set(id, entry);
2335
+ ledgerNeedsSave = true;
2336
+ }
2337
+ };
2310
2338
  const ensureLedgerLoaded = async () => {
2311
2339
  if (ledgerLoaded || options.ledger === void 0) return;
2312
2340
  ledgerLoaded = true;
@@ -2368,34 +2396,78 @@ function createUsageAggregator(persistence, options = {}) {
2368
2396
  continue;
2369
2397
  }
2370
2398
  try {
2399
+ const cwd = meta.cwd;
2400
+ const ledgerRow = ledger.get(id);
2401
+ if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 6) {
2402
+ const fold = deserializeFold(ledgerRow.fold);
2403
+ cache.set(id, {
2404
+ stamp,
2405
+ fold
2406
+ });
2407
+ folds.push({
2408
+ id,
2409
+ ...cwd === void 0 ? {} : { cwd },
2410
+ fold
2411
+ });
2412
+ included.add(id);
2413
+ continue;
2414
+ }
2415
+ const previous = cache.get(id);
2416
+ if (previous?.machine !== void 0 && previous.lastSeq !== void 0) {
2417
+ const from = previous.lastSeq + 1;
2418
+ const { events } = await persistence.readFrom(meta.id, from);
2419
+ const after = await stampOf(meta);
2420
+ if (stamp !== null && after !== stamp) {
2421
+ cache.delete(id);
2422
+ continue;
2423
+ }
2424
+ const first = events[0]?.seq;
2425
+ if (events.length === 0 || first !== from) {
2426
+ cache.delete(id);
2427
+ continue;
2428
+ }
2429
+ foldInto(previous.fold, previous.machine, events, subscriptionProviders, officialProviderIds, routesOf(), searchEstimate, meta.seedLength ?? 0);
2430
+ refreshTurns(previous.fold, previous.machine);
2431
+ const last = events[events.length - 1];
2432
+ previous.lastSeq = typeof last.seq === "number" ? last.seq : previous.lastSeq;
2433
+ previous.stamp = stamp;
2434
+ cache.delete(id);
2435
+ cache.set(id, previous);
2436
+ folds.push({
2437
+ id,
2438
+ ...cwd === void 0 ? {} : { cwd },
2439
+ fold: previous.fold
2440
+ });
2441
+ included.add(id);
2442
+ recordLedger(id, cwd, stamp, previous.fold);
2443
+ continue;
2444
+ }
2371
2445
  const { events } = await persistence.readFrom(meta.id, 0);
2372
2446
  const after = await stampOf(meta);
2373
2447
  if (stamp !== null && after !== stamp) continue;
2374
- const fold = foldSession(events, subscriptionProviders, officialProviderIds, routesOf(), searchEstimate, meta.seedLength ?? 0);
2448
+ const fold = freshFold();
2449
+ const machine = freshMachine();
2450
+ for (let start = 0; start < events.length; start += FOLD_CHUNK_EVENTS) {
2451
+ foldInto(fold, machine, events.slice(start, start + FOLD_CHUNK_EVENTS), subscriptionProviders, officialProviderIds, routesOf(), searchEstimate, meta.seedLength ?? 0);
2452
+ if (start + FOLD_CHUNK_EVENTS < events.length) await new Promise((resolve) => {
2453
+ setImmediate(resolve);
2454
+ });
2455
+ }
2456
+ refreshTurns(fold, machine);
2457
+ const last = events[events.length - 1];
2375
2458
  cache.set(id, {
2376
2459
  stamp,
2377
- fold
2460
+ fold,
2461
+ machine,
2462
+ ...last !== void 0 && typeof last.seq === "number" ? { lastSeq: last.seq } : {}
2378
2463
  });
2379
2464
  folds.push({
2380
2465
  id,
2381
- ...meta.cwd === void 0 ? {} : { cwd: meta.cwd },
2466
+ ...cwd === void 0 ? {} : { cwd },
2382
2467
  fold
2383
2468
  });
2384
2469
  included.add(id);
2385
- if (options.ledger !== void 0) {
2386
- const entry = {
2387
- id,
2388
- ...meta.cwd === void 0 ? {} : { cwd: meta.cwd },
2389
- ...stamp === null ? {} : { stamp },
2390
- foldVersion: 6,
2391
- fold: serializeFold(fold)
2392
- };
2393
- const previous = ledger.get(id);
2394
- 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)) {
2395
- ledger.set(id, entry);
2396
- ledgerNeedsSave = true;
2397
- }
2398
- }
2470
+ recordLedger(id, cwd, stamp, fold);
2399
2471
  } catch (error) {
2400
2472
  skipped.push(id);
2401
2473
  console.warn("[usage-billing] skip unreadable session", id, error);
@@ -3222,7 +3294,7 @@ const declaredGate = createCooldownGate({
3222
3294
  cooldownMs: 6e4
3223
3295
  });
3224
3296
  /** 会触及原型链的路径段:一律拒绝(读结果不是文档自带字段)。 */
3225
- const FORBIDDEN_SEGMENTS = new Set([
3297
+ const FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set([
3226
3298
  "__proto__",
3227
3299
  "constructor",
3228
3300
  "prototype"
@@ -3772,7 +3844,7 @@ function asPrice(value) {
3772
3844
  */
3773
3845
  function buildExtraModels(data) {
3774
3846
  if (data === null || typeof data !== "object") return [];
3775
- const catalogKeys = new Set([...MODEL_CATALOG.map((entry) => entry.key.toLowerCase()), ...Object.keys(MODEL_KEY_ALIASES).map((key) => MODEL_KEY_ALIASES[key]?.toLowerCase() ?? key.toLowerCase())]);
3847
+ const catalogKeys = /* @__PURE__ */ new Set([...MODEL_CATALOG.map((entry) => entry.key.toLowerCase()), ...Object.keys(MODEL_KEY_ALIASES).map((key) => MODEL_KEY_ALIASES[key]?.toLowerCase() ?? key.toLowerCase())]);
3776
3848
  const extras = [];
3777
3849
  for (const [providerId, providerDoc] of Object.entries(data)) {
3778
3850
  if (providerDoc === null || typeof providerDoc !== "object") continue;
@@ -4446,7 +4518,7 @@ const relayGate = createCooldownGate({
4446
4518
  /** Abort a relay fetch when the upstream hangs beyond this budget. */
4447
4519
  const FETCH_TIMEOUT_MS = 8e3;
4448
4520
  /** 指纹识别缓存 TTL(毫秒):识别结果低频变化,5 分钟内同 origin 不再重复探测。 */
4449
- const FINGERPRINT_TTL_MS = 300 * 1e3;
4521
+ const FINGERPRINT_TTL_MS = 3e5;
4450
4522
  /** 每 origin 的识别结果缓存:`kind` 是识别出的中转站程序,`at` 是探测时刻。 */
4451
4523
  const fingerprintCache = /* @__PURE__ */ new Map();
4452
4524
  /** Number, or null when the value is not a finite number (nor numeric string). */
@@ -4582,7 +4654,7 @@ function originOf(baseURL) {
4582
4654
  * 子路径只会得到 404/非中转站格式,因而面板应排除它们,避免误判为
4583
4655
  * 「未识别」。中转站面板只列真正的第三方中转程序。
4584
4656
  */
4585
- const OFFICIAL_HOSTS = new Set([
4657
+ const OFFICIAL_HOSTS = /* @__PURE__ */ new Set([
4586
4658
  "api.deepseek.com",
4587
4659
  "api.openai.com",
4588
4660
  "open.bigmodel.cn",
@@ -4832,10 +4904,10 @@ const usageBillingSettingsNs = validateSettingsNamespace(BILLING_SETTINGS_NAMESP
4832
4904
  /** 该命名空间的 wire schema:`enableUsageStatsTool` 布尔,默认关闭(issue 诉求)。 */
4833
4905
  const UsageBillingSettingsSchema = z.object({ [ENABLE_USAGE_STATS_TOOL_FIELD]: z.boolean().default(false) });
4834
4906
  /** 实时定价的后台刷新间隔(毫秒):汇率/模型价低频变化,6 小时一次足够。 */
4835
- const PRICING_REFRESH_INTERVAL_MS = 360 * 60 * 1e3;
4907
+ const PRICING_REFRESH_INTERVAL_MS = 216e5;
4836
4908
  /** 订阅套餐额度缓存时长(毫秒):上游配额 API 低频变化,5 分钟足够。 */
4837
- const SUBSCRIPTION_CACHE_MS = 300 * 1e3;
4838
- const BALANCE_CACHE_MS = 300 * 1e3;
4909
+ const SUBSCRIPTION_CACHE_MS = 3e5;
4910
+ const BALANCE_CACHE_MS = 3e5;
4839
4911
  /** DeepSeek 余额查询的默认凭据引用(与 llm-deepseek 的默认引用一致)。 */
4840
4912
  const DEFAULT_BALANCE_API_KEY_ENV = "DEEPSEEK_API_KEY";
4841
4913
  /**
@@ -4846,7 +4918,7 @@ const PACKAGE_VERSION = createRequire(import.meta.url)("../package.json").versio
4846
4918
  /** 统计快照的落盘节流(毫秒):前端 30 秒轮询,快照最多每 30 秒写一次。 */
4847
4919
  const SNAPSHOT_INTERVAL_MS = 3e4;
4848
4920
  /** 鉴权失败告警冷却(毫秒):同一 provider 在窗口内只提示一次,避免 30 秒轮询刷屏。 */
4849
- const AUTH_WARN_COOLDOWN_MS = 1800 * 1e3;
4921
+ const AUTH_WARN_COOLDOWN_MS = 18e5;
4850
4922
  /**
4851
4923
  * 鉴权失败分类告警(P1-5):余额 / 订阅查询返回 unauthorized 时,按
4852
4924
  * `source:provider` 去重并冷却告警,提示检查 llm-pi-ai 里该 provider 的 apiKeyEnv。
@@ -5090,7 +5162,8 @@ function apply(ctx, config = {}) {
5090
5162
  })();
5091
5163
  const persistReconcileRef = () => {
5092
5164
  if (reconcileRef === null) return;
5093
- writeFileAtomic(reconcilePath, JSON.stringify({ ref: reconcileRef }), {
5165
+ const payload = JSON.stringify({ ref: reconcileRef });
5166
+ writeFileAtomic(reconcilePath, payload, {
5094
5167
  mode: 384,
5095
5168
  dirMode: 448
5096
5169
  }).catch(() => {});
@@ -479,7 +479,7 @@ export declare function messageTextLength(message: unknown): number;
479
479
  export declare function foldSession(events: readonly {
480
480
  type: string;
481
481
  time: number;
482
- data: never;
482
+ data: unknown;
483
483
  seq?: number;
484
484
  }[], subscriptionProviders: ReadonlySet<string>, officialProviderIds?: ReadonlySet<string>, routes?: Readonly<Record<string, ProviderRouteView>>, searchCallEstimateCny?: number, seedLength?: number): SessionFold;
485
485
  /**
@@ -32,6 +32,6 @@ export declare function UsageHeatmap({ days, currency, now, t, range }: {
32
32
  currency: CostCurrency;
33
33
  now?: Date;
34
34
  range?: 'month' | 'year';
35
- t: (key: 'billing.costAbbr' | 'billing.noData' | 'billing.heatmapLess' | 'billing.heatmapMore') => string;
35
+ t: (key: 'costAbbr' | 'noData' | 'heatmapLess' | 'heatmapMore') => string;
36
36
  }): React.ReactNode;
37
37
  //# sourceMappingURL=heatmap.d.ts.map
@@ -32,6 +32,6 @@ export declare function RoundCostChart({ rounds, flags, currency, t }: {
32
32
  rounds: readonly RoundChartRow[];
33
33
  flags: readonly AnomalyFlag[];
34
34
  currency: CostCurrency;
35
- t: (key: 'billing.model' | 'billing.costAbbr') => string;
35
+ t: (key: 'model' | 'costAbbr') => string;
36
36
  }): React.ReactNode;
37
37
  //# sourceMappingURL=round-chart.d.ts.map
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.1.4",
4
+ "version": "1.1.6",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -34,13 +34,13 @@
34
34
  },
35
35
  "client": {
36
36
  "inject": [
37
- "@deepseek-ai/dsh-client-runtime",
38
37
  "@deepseek-ai/dsh-client-locale",
39
38
  "@deepseek-ai/dsh-client-ui-conversation"
40
39
  ],
41
40
  "platform": "web"
42
41
  },
43
42
  "compatibility": {
43
+ "dsh": ">=0.1.0-rc.8 <0.1.1-0 || >=0.1.1-rc.1 <0.1.2-0",
44
44
  "dshReleases": {
45
45
  "0.1.0-rc.8": "compatible",
46
46
  "0.1.1-rc.1": "compatible",
@@ -48,28 +48,23 @@
48
48
  }
49
49
  }
50
50
  },
51
- "scripts": {
52
- "bundle": "tsdown",
53
- "watch": "tsdown --watch",
54
- "test": "vitest run",
55
- "build": "bash build.sh"
56
- },
57
51
  "license": "MIT",
58
52
  "engines": {
59
53
  "node": "^22.19.0 || >=24.0.0"
60
54
  },
61
55
  "peerDependencies": {
56
+ "@deepseek-ai/cordis": "*",
62
57
  "@deepseek-ai/dsh-api-remotes": "*",
58
+ "@deepseek-ai/dsh-api-session-controller": "*",
63
59
  "@deepseek-ai/dsh-atomic-write": "*",
64
- "@deepseek-ai/dsh-client-connection": "*",
65
- "@deepseek-ai/dsh-host-webserver": "*",
66
60
  "@deepseek-ai/dsh-client-locale": "*",
67
- "@deepseek-ai/dsh-client-runtime": "*",
61
+ "@deepseek-ai/dsh-client-store": "*",
68
62
  "@deepseek-ai/dsh-client-ui-conversation": "*",
69
- "@deepseek-ai/dsh-client-ui-sidebar": "*",
70
63
  "@deepseek-ai/dsh-client-ui-primitives": "*",
64
+ "@deepseek-ai/dsh-client-ui-sidebar": "*",
71
65
  "@deepseek-ai/dsh-client-ui-slots": "*",
72
66
  "@deepseek-ai/dsh-credentials": "*",
67
+ "@deepseek-ai/dsh-host-webserver": "*",
73
68
  "@deepseek-ai/dsh-invariants": "*",
74
69
  "@deepseek-ai/dsh-llm": "*",
75
70
  "@deepseek-ai/dsh-session": "*",
@@ -77,28 +72,45 @@
77
72
  "@deepseek-ai/dsh-settings": "*",
78
73
  "@deepseek-ai/dsh-tools": "*",
79
74
  "@deepseek-ai/schemastery": "*",
80
- "@deepseek-ai/cordis": "*",
81
75
  "react": "^18.2.0"
82
76
  },
83
77
  "dependencies": {
84
78
  "clsx": "^2.1.1"
85
79
  },
86
80
  "devDependencies": {
87
- "@deepseek-ai/dsh-client-locale": "*",
88
- "@deepseek-ai/dsh-client-runtime": "*",
89
- "@deepseek-ai/dsh-client-test-runtime": "*",
90
- "@deepseek-ai/dsh-client-ui-conversation": "*",
91
- "@deepseek-ai/dsh-client-ui-primitives": "*",
92
- "@deepseek-ai/dsh-client-ui-slots": "*",
93
- "@deepseek-ai/dsh-invariants": "*",
81
+ "typescript": "^5.9.2",
82
+ "@deepseek-ai/cordis": "*",
83
+ "@deepseek-ai/cordis-plugin-include": "^1.0.7",
84
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
85
+ "@deepseek-ai/dsh-api-remotes": "0.1.2-alpha.5",
86
+ "@deepseek-ai/dsh-api-session-controller": "0.1.2-alpha.5",
87
+ "@deepseek-ai/dsh-atomic-write": "0.1.2-alpha.5",
88
+ "@deepseek-ai/dsh-client-locale": "0.1.2-alpha.5",
89
+ "@deepseek-ai/dsh-client-store": "0.1.2-alpha.5",
90
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-alpha.5",
91
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-alpha.5",
92
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-alpha.5",
93
+ "@deepseek-ai/dsh-credentials": "0.1.2-alpha.5",
94
+ "@deepseek-ai/dsh-host-webserver": "0.1.2-alpha.5",
95
+ "@deepseek-ai/dsh-invariants": "0.1.2-alpha.5",
96
+ "@deepseek-ai/dsh-llm": "0.1.2-alpha.5",
97
+ "@deepseek-ai/dsh-scope": "0.1.2-alpha.5",
98
+ "@deepseek-ai/dsh-session": "0.1.2-alpha.5",
99
+ "@deepseek-ai/dsh-session-persistence": "0.1.2-alpha.5",
100
+ "@deepseek-ai/dsh-settings": "0.1.2-alpha.5",
101
+ "@deepseek-ai/dsh-tools": "0.1.2-alpha.5",
102
+ "@deepseek-ai/schemastery": "^3.18.2",
103
+ "@testing-library/dom": "^10.4.1",
94
104
  "@testing-library/react": "^16.1.0",
95
- "@types/react": "~18.3.1",
96
105
  "@types/node": "^22.20.0",
97
- "@deepseek-ai/cordis": "*",
106
+ "@types/react": "~18.3.1",
107
+ "jsdom": "^26.1.0",
108
+ "lightningcss": "^1.32.0",
98
109
  "react": "^18.2.0",
99
110
  "react-dom": "^18.2.0",
100
- "lightningcss": "^1.32.0",
101
- "tsdown": "^0.22.2"
111
+ "tsdown": "^0.22.2",
112
+ "use-sync-external-store": "1.2.0",
113
+ "vitest": "^4.1.8"
102
114
  },
103
115
  "files": [
104
116
  "lib/index.js",
@@ -106,5 +118,10 @@
106
118
  "lib/client.js",
107
119
  "lib/types/**/*.d.ts",
108
120
  "cordis.patch.yml"
109
- ]
110
- }
121
+ ],
122
+ "scripts": {
123
+ "bundle": "tsdown",
124
+ "watch": "tsdown --watch",
125
+ "test": "vitest run"
126
+ }
127
+ }