@kenz1117/dsh-ui-usage-billing 1.4.7 → 1.4.9

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
@@ -298,16 +298,22 @@ function isPeakHour(beijingHour) {
298
298
  /**
299
299
  * 由时刻(epoch 毫秒)推断计费时段;时刻未知/非法时按高峰计(保守:未知
300
300
  * 时刻不低估成本,与社区 dsh-usage-chart 的 tierAt 语义一致)。
301
- * 周末(北京时间周六/周日)全天不区分峰谷,统一按低谷价。
301
+ * 周末(北京时间周六/周日)与中国法定节假日全天不区分峰谷,统一按低谷价
302
+ * (官方 2026-09-19 口径:调休上班的周末、法定节假日全天均按空闲时段计费;
303
+ * 调休上班日全部落在周末,故由周末分支覆盖,无需单列)。
302
304
  * @param timeMs - Unix epoch 毫秒;null/undefined/NaN 视为未知。
303
305
  */
304
306
  function tierAt(timeMs) {
305
307
  if (timeMs === null || timeMs === void 0 || !Number.isFinite(timeMs)) return "peak";
306
- return tierAtWithBounds(timeMs, TIER_BOUNDARY_MINUTES);
308
+ return tierAtWithBounds(timeMs, TIER_BOUNDARY_MINUTES, true);
307
309
  }
308
- /** 按给定峰段边界判档:周末全天低谷;工作日分钟数落在任一 [b(i), b(i+1)) 峰段即为高峰。 */
309
- function tierAtWithBounds(timeMs, bounds) {
310
- if (isBeijingWeekend(timeMs)) return "offPeak";
310
+ /**
311
+ * 按给定峰段边界判档:低谷日全天低谷;工作日分钟数落在任一 [b(i), b(i+1)) 峰段即为高峰。
312
+ * @param holidays - 是否把中国法定节假日并入低谷日。该官方口径只适用于 DeepSeek 按量分时;
313
+ * 智谱 Coding Plan 的积分口径未见节假日说明,故默认不启用,由调用点显式传入。
314
+ */
315
+ function tierAtWithBounds(timeMs, bounds, holidays = false) {
316
+ if (isBeijingWeekend(timeMs) || holidays && isBeijingHoliday(timeMs)) return "offPeak";
311
317
  const minute = Math.floor(beijingMillisOfDay(timeMs) / 6e4);
312
318
  for (let i = 0; i + 1 < bounds.length; i += 2) {
313
319
  const start = bounds[i] ?? 0;
@@ -318,9 +324,31 @@ function tierAtWithBounds(timeMs, bounds) {
318
324
  }
319
325
  /** 时刻是否落在北京时间周末(周六/周日)。 */
320
326
  function isBeijingWeekend(timeMs) {
321
- const day = new Date(timeMs + 8 * 36e5).getUTCDay();
327
+ const day = new Date(timeMs + 288e5).getUTCDay();
322
328
  return day === 0 || day === 6;
323
329
  }
330
+ /**
331
+ * 中国法定节假日 —— **生成块,勿手改**。数据源
332
+ * [NateScarlet/holiday-cn](https://github.com/NateScarlet/holiday-cn)(逐年抓取国务院公告;
333
+ * 2026 年依据 https://www.gov.cn/zhengce/zhengceku/202511/content_7047091.htm)。
334
+ *
335
+ * 只收录峰谷计费起点({@link WEEKEND_OFFPEAK_START_MS},北京时间 2026-08-23 00:00)之后的日期:
336
+ * 更早的调用由 v1 规则定价,不涉及节假日。表中没有的年份退化为「仅周末低谷」,
337
+ * 国务院公布下一年安排后在此追加一条即可。
338
+ *
339
+ * 编码为空格分隔的 `YYYYMMDD` 列表(按年分组、同年日期连续排列,便于逐年追加):
340
+ * 每个键长度固定为 8,故 `includes` 不会跨键误匹配;相比「区间 + 运行时展开」
341
+ * 省掉一段解析代码——client / index 两个 face 都要过 256 KiB 单文件上限。
342
+ */
343
+ const CHINA_HOLIDAYS = "20260925 20260926 20260927 20261001 20261002 20261003 20261004 20261005 20261006 20261007";
344
+ /** 北京日期键 YYYYMMDD(与宿主时区无关)。 */
345
+ function beijingDateKey(timeMs) {
346
+ return new Date(timeMs + 288e5).toISOString().slice(0, 10).replace(/-/g, "");
347
+ }
348
+ /** 时刻是否落在北京时间法定节假日(仅覆盖 {@link CHINA_HOLIDAYS} 内已收录的年份)。 */
349
+ function isBeijingHoliday(timeMs) {
350
+ return CHINA_HOLIDAYS.includes(beijingDateKey(timeMs));
351
+ }
324
352
  /** 峰谷切换边界(北京时间的当日分钟数):09:00 / 12:00 / 14:00 / 18:00。 */
325
353
  const TIER_BOUNDARY_MINUTES = [
326
354
  540,
@@ -330,7 +358,7 @@ const TIER_BOUNDARY_MINUTES = [
330
358
  ];
331
359
  /** 北京时间的当日毫秒数(0–86,400,000)。 */
332
360
  function beijingMillisOfDay(timeMs) {
333
- return ((timeMs + 8 * 36e5) % 864e5 + 864e5) % 864e5;
361
+ return ((timeMs + 288e5) % 864e5 + 864e5) % 864e5;
334
362
  }
335
363
  /**
336
364
  * 内置目录与别名表的运行时注入态:数据本体在 src/builtin-catalog.ts(仅随 node
@@ -3275,8 +3303,8 @@ function createUsageAggregator(persistence, options = {}) {
3275
3303
  };
3276
3304
  const toModelDayRecord = (map) => Object.fromEntries([...map].map(([day, models]) => [day, Object.fromEntries(models)]));
3277
3305
  const toModelDaySiteRecord = (map) => Object.fromEntries([...map].map(([day, models]) => [day, Object.fromEntries([...models].map(([model, sites]) => [model, Object.fromEntries(sites)]))]));
3278
- const modelKeys = new Set([...perfModel.keys(), ...perfModelDigest.keys()]);
3279
- const hourKeys = new Set([...perfHourModel.keys(), ...perfHourDigest.keys()]);
3306
+ const modelKeys = /* @__PURE__ */ new Set([...perfModel.keys(), ...perfModelDigest.keys()]);
3307
+ const hourKeys = /* @__PURE__ */ new Set([...perfHourModel.keys(), ...perfHourDigest.keys()]);
3280
3308
  const perf = modelKeys.size === 0 && hourKeys.size === 0 ? void 0 : {
3281
3309
  byModel: Object.fromEntries([...modelKeys].map((model) => {
3282
3310
  const live = perfModel.get(model);
@@ -3287,7 +3315,7 @@ function createUsageAggregator(persistence, options = {}) {
3287
3315
  byHourModel: Object.fromEntries([...hourKeys].sort().map((hour) => {
3288
3316
  const liveModels = perfHourModel.get(hour);
3289
3317
  const digestModels = perfHourDigest.get(hour);
3290
- const modelKeysInHour = new Set([...liveModels?.keys() ?? [], ...digestModels?.keys() ?? []]);
3318
+ const modelKeysInHour = /* @__PURE__ */ new Set([...liveModels?.keys() ?? [], ...digestModels?.keys() ?? []]);
3291
3319
  return [hour, Object.fromEntries([...modelKeysInHour].map((model) => {
3292
3320
  const live = liveModels?.get(model);
3293
3321
  const restored = digestModels?.get(model);
@@ -3491,7 +3519,11 @@ function tc3Authorization(secretId, secretKey, payload, timestamp) {
3491
3519
  const canonicalRequest = `POST\n/\n\n${`content-type:application/json; charset=utf-8\nhost:${TOKENHUB_HOST}\n`}\ncontent-type;host\n${createHash("sha256").update(payload).digest("hex")}`;
3492
3520
  const hashedCanonical = createHash("sha256").update(canonicalRequest).digest("hex");
3493
3521
  const stringToSign = `TC3-HMAC-SHA256\n${String(timestamp)}\n${date}/${TOKENHUB_SERVICE}/tc3_request\n${hashedCanonical}`;
3494
- return `TC3-HMAC-SHA256 Credential=${secretId}/${date}/${TOKENHUB_SERVICE}/tc3_request, SignedHeaders=content-type;host, Signature=${createHmac("sha256", createHmac("sha256", createHmac("sha256", createHmac("sha256", date).update(secretKey).digest()).update(TOKENHUB_SERVICE).digest()).update("tc3_request").digest()).update(stringToSign).digest("hex")}`;
3522
+ const kDate = createHmac("sha256", date).update(secretKey).digest();
3523
+ const kService = createHmac("sha256", kDate).update(TOKENHUB_SERVICE).digest();
3524
+ const kSigning = createHmac("sha256", kService).update("tc3_request").digest();
3525
+ const signature = createHmac("sha256", kSigning).update(stringToSign).digest("hex");
3526
+ return `TC3-HMAC-SHA256 Credential=${secretId}/${date}/${TOKENHUB_SERVICE}/tc3_request, SignedHeaders=content-type;host, Signature=${signature}`;
3495
3527
  }
3496
3528
  /**
3497
3529
  * 调用一次 TokenHub 管控面接口:TC3 签名 + 超时保护,返回响应 JSON 的 `Response`。
@@ -4255,7 +4287,7 @@ const declaredGate = createCooldownGate({
4255
4287
  cooldownMs: 6e4
4256
4288
  });
4257
4289
  /** 会触及原型链的路径段:一律拒绝(读结果不是文档自带字段)。 */
4258
- const FORBIDDEN_SEGMENTS = new Set([
4290
+ const FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set([
4259
4291
  "__proto__",
4260
4292
  "constructor",
4261
4293
  "prototype"
@@ -4833,7 +4865,7 @@ function asPrice(value) {
4833
4865
  /** models.dev 常把同一模型收录在多个 provider 下(厂商 + 转售)。这些 id 是模型
4834
4866
  * 自身的厂商,其报价视为权威:转售可能加价,也可能省略 cache_read 而落到下方
4835
4867
  * `input * 0.1` 的猜测值。去重时优先采用。 */
4836
- const FIRST_PARTY_PRICE_SOURCES = new Set([
4868
+ const FIRST_PARTY_PRICE_SOURCES = /* @__PURE__ */ new Set([
4837
4869
  "anthropic",
4838
4870
  "openai",
4839
4871
  "google",
@@ -4862,7 +4894,7 @@ const FIRST_PARTY_PRICE_SOURCES = new Set([
4862
4894
  */
4863
4895
  function buildExtraModels(data) {
4864
4896
  if (data === null || typeof data !== "object") return [];
4865
- const catalogKeys = new Set([...BUILTIN_MODEL_CATALOG.map((entry) => entry.key.toLowerCase()), ...Object.keys(BUILTIN_MODEL_KEY_ALIASES).map((key) => BUILTIN_MODEL_KEY_ALIASES[key]?.toLowerCase() ?? key.toLowerCase())]);
4897
+ const catalogKeys = /* @__PURE__ */ new Set([...BUILTIN_MODEL_CATALOG.map((entry) => entry.key.toLowerCase()), ...Object.keys(BUILTIN_MODEL_KEY_ALIASES).map((key) => BUILTIN_MODEL_KEY_ALIASES[key]?.toLowerCase() ?? key.toLowerCase())]);
4866
4898
  const offers = /* @__PURE__ */ new Map();
4867
4899
  const familyVendorVotes = /* @__PURE__ */ new Map();
4868
4900
  for (const [providerId, providerDoc] of Object.entries(data)) {
@@ -5785,7 +5817,7 @@ const relayGate = createCooldownGate({
5785
5817
  /** Abort a relay fetch when the upstream hangs beyond this budget. */
5786
5818
  const FETCH_TIMEOUT_MS = 8e3;
5787
5819
  /** 指纹识别缓存 TTL(毫秒):识别结果低频变化,5 分钟内同 origin 不再重复探测。 */
5788
- const FINGERPRINT_TTL_MS = 300 * 1e3;
5820
+ const FINGERPRINT_TTL_MS = 3e5;
5789
5821
  /** 每 origin 的识别结果缓存:`kind` 是识别出的中转站程序,`at` 是探测时刻。 */
5790
5822
  const fingerprintCache = /* @__PURE__ */ new Map();
5791
5823
  /** Number, or null when the value is not a finite number (nor numeric string). */
@@ -5921,7 +5953,7 @@ function originOf(baseURL) {
5921
5953
  * 子路径只会得到 404/非中转站格式,因而面板应排除它们,避免误判为
5922
5954
  * 「未识别」。中转站面板只列真正的第三方中转程序。
5923
5955
  */
5924
- const OFFICIAL_HOSTS = new Set([
5956
+ const OFFICIAL_HOSTS = /* @__PURE__ */ new Set([
5925
5957
  "api.deepseek.com",
5926
5958
  "api.openai.com",
5927
5959
  "open.bigmodel.cn",
@@ -6191,13 +6223,13 @@ const usageBillingSettingsNs = validateSettingsNamespace(BILLING_SETTINGS_NAMESP
6191
6223
  /** 该命名空间的 wire schema:`enableUsageStatsTool` 布尔,默认关闭(issue 诉求)。 */
6192
6224
  const UsageBillingSettingsSchema = z.object({ [ENABLE_USAGE_STATS_TOOL_FIELD]: z.boolean().default(false) });
6193
6225
  /** 实时定价的后台刷新间隔(毫秒):汇率/模型价低频变化,6 小时一次足够。 */
6194
- const PRICING_REFRESH_INTERVAL_MS = 360 * 60 * 1e3;
6226
+ const PRICING_REFRESH_INTERVAL_MS = 216e5;
6195
6227
  /** 历史回放预热的延迟:等宿主启动高峰(插件加载 / 路由挂载)过去再全量折叠,
6196
6228
  * 避免抢启动期的 CPU;纯延迟不阻塞任何请求,面板提前打开也只会提前聚合。 */
6197
6229
  const WARMUP_DELAY_MS = 3e3;
6198
6230
  /** 订阅套餐额度缓存时长(毫秒):上游配额 API 低频变化,5 分钟足够。 */
6199
- const SUBSCRIPTION_CACHE_MS = 300 * 1e3;
6200
- const BALANCE_CACHE_MS = 300 * 1e3;
6231
+ const SUBSCRIPTION_CACHE_MS = 3e5;
6232
+ const BALANCE_CACHE_MS = 3e5;
6201
6233
  /** DeepSeek 余额查询的默认凭据引用(与 llm-deepseek 的默认引用一致)。 */
6202
6234
  const DEFAULT_BALANCE_API_KEY_ENV = "DEEPSEEK_API_KEY";
6203
6235
  /**
@@ -6214,7 +6246,7 @@ const SNAPSHOT_INTERVAL_MS = 3e5;
6214
6246
  * 轻量用户暖缓存下的聚合是毫秒级,预算几乎不会触发。 */
6215
6247
  const RESPONSE_BUDGET_MS = 1500;
6216
6248
  /** 鉴权失败告警冷却(毫秒):同一 provider 在窗口内只提示一次,避免 30 秒轮询刷屏。 */
6217
- const AUTH_WARN_COOLDOWN_MS = 1800 * 1e3;
6249
+ const AUTH_WARN_COOLDOWN_MS = 18e5;
6218
6250
  /**
6219
6251
  * 鉴权失败分类告警(P1-5):余额 / 订阅查询返回 unauthorized 时,按
6220
6252
  * `source:provider` 去重并冷却告警,提示检查 llm-pi-ai 里该 provider 的 apiKeyEnv。
@@ -6565,7 +6597,8 @@ function apply(ctx, config = {}) {
6565
6597
  })();
6566
6598
  const persistReconcileRef = () => {
6567
6599
  if (reconcileRef === null) return;
6568
- writeFileAtomic(reconcilePath, JSON.stringify({ ref: reconcileRef }), {
6600
+ const payload = JSON.stringify({ ref: reconcileRef });
6601
+ writeFileAtomic(reconcilePath, payload, {
6569
6602
  mode: 384,
6570
6603
  dirMode: 448
6571
6604
  }).catch(() => {});
@@ -6692,7 +6725,9 @@ function apply(ctx, config = {}) {
6692
6725
  }
6693
6726
  }), "usage-billing: notify-claim route");
6694
6727
  ctx.inject(["settings"], (sctx) => {
6695
- const scope = sctx.settings.register(usageBillingSettingsNs, UsageBillingSettingsSchema, { base: { enableUsageStatsTool: config.enableUsageStatsTool ?? false } });
6728
+ const legacy = sctx.settings;
6729
+ if (typeof legacy.register !== "function") return;
6730
+ const scope = legacy.register(usageBillingSettingsNs, UsageBillingSettingsSchema, { base: { enableUsageStatsTool: config.enableUsageStatsTool ?? false } });
6696
6731
  usageSettingsScope = scope;
6697
6732
  ctx.inject(["tools"], (toolsCtx) => {
6698
6733
  if (scope.get().enableUsageStatsTool) toolsCtx.tools.register(defineTool({
@@ -10,7 +10,8 @@
10
10
  * Google-style two-band billing is modeled per model: Gemini's Flex tier
11
11
  * prices spare-capacity traffic at -50%; DeepSeek splits peak
12
12
  * (weekdays 09:00-12:00 / 14:00-18:00 Beijing) at 2x the off-peak rate —
13
- * weekends (Sat/Sun, Beijing) are charged at the off-peak rate all day.
13
+ * weekends (Sat/Sun, Beijing) and Chinese statutory holidays are charged at
14
+ * the off-peak rate all day (see {@link CHINA_HOLIDAYS}).
14
15
  * The estimator mixes both bands by a configured peak share ({@link DEFAULT_PEAK_SHARE}).
15
16
  *
16
17
  * Time-limited launch promos ({@link PricePromo}) never mutate the catalog:
@@ -182,7 +183,9 @@ export declare function isPeakHour(beijingHour: number): boolean;
182
183
  /**
183
184
  * 由时刻(epoch 毫秒)推断计费时段;时刻未知/非法时按高峰计(保守:未知
184
185
  * 时刻不低估成本,与社区 dsh-usage-chart 的 tierAt 语义一致)。
185
- * 周末(北京时间周六/周日)全天不区分峰谷,统一按低谷价。
186
+ * 周末(北京时间周六/周日)与中国法定节假日全天不区分峰谷,统一按低谷价
187
+ * (官方 2026-09-19 口径:调休上班的周末、法定节假日全天均按空闲时段计费;
188
+ * 调休上班日全部落在周末,故由周末分支覆盖,无需单列)。
186
189
  * @param timeMs - Unix epoch 毫秒;null/undefined/NaN 视为未知。
187
190
  */
188
191
  export declare function tierAt(timeMs: number | null | undefined): PriceTierId;
@@ -191,9 +194,12 @@ export declare function tierAt(timeMs: number | null | undefined): PriceTierId;
191
194
  *
192
195
  * 下一切换点统一定义为档位真正变化的最近边界:自当前时刻起逐天扫描工作日的
193
196
  * 09:00 / 12:00 / 14:00 / 18:00,候选时刻的档位由 {@link tierAt} 判定——
194
- * 周末(周六/周日)北京全天低谷、没有边界,扫描自然跳过;工作日深夜跨周末
195
- * 时落到周一 09:00 而非周末伪边界(issue #33)。
196
- * 最坏情形(周五 18:00 后 → 周一 09:00)约 63h,7 天窗口必然覆盖。
197
+ * 周末(周六/周日)与法定节假日北京全天低谷、没有边界,扫描自然跳过;工作日
198
+ * 深夜跨周末时落到周一 09:00 而非周末伪边界(issue #33)。
199
+ * 最坏情形是「长假 + 相邻周末」:最长连休 8 天(2025-10-01 ~ 10-08 型)时,
200
+ * 假期前最后一个工作日深夜的下一切换可落在约 9.4 天之后——故扫描窗口取
201
+ * 10 天。原实现取 7 天(注释记「最坏 63h」),在连休下会扫不到真边界而落进
202
+ * 兜底伪边界。
197
203
  * @param nowMs - 当前时刻(epoch 毫秒)。
198
204
  * @returns 当前档位与到下一切换边界的毫秒数。
199
205
  */
@@ -15,10 +15,18 @@
15
15
  import type { IncomingMessage, ServerResponse } from 'node:http';
16
16
  import type { Context } from '@deepseek-ai/cordis';
17
17
  import type { CredentialProvider } from '@deepseek-ai/dsh-credentials';
18
- import type { SettingsProvider } from '@deepseek-ai/dsh-settings';
19
18
  import { type UsageLedgerStore, type UsagePersistence } from './aggregate.ts';
20
19
  import type { CustomBalanceConfig, DeclaredEndpointConfig, SubscriptionPlanConfig } from './pricing-shared.ts';
21
20
  import { type IdentifiedSubscriptionPlan, type SubscriptionKeys } from './subscriptions.ts';
21
+ /** 设置服务的读取面:两代宿主运行时都提供 describe(条目键为 profile entry id)。 */
22
+ interface SettingsReader {
23
+ describe(options?: {
24
+ redactSecrets?: boolean;
25
+ }): readonly {
26
+ ns: string;
27
+ value: unknown;
28
+ }[];
29
+ }
22
30
  /** 校验 Host 头是本机回环(精确 127.0.0.0/8 / ::1 / localhost 或空,供 curl 不带 Host 的极简请求)。
23
31
  * 拒绝 `127.0.0.1.attacker.com` 这类以 `127.` 开头但解析到外部的 DNS rebinding 域名:
24
32
  * 只用 `startsWith('127.')` 会被它穿透,必须精确匹配回环 IP 的字面量。 */
@@ -127,7 +135,7 @@ export interface PiAiProviderRoute {
127
135
  * @param settings - the settings service (reads the llm-pi-ai namespace).
128
136
  * @returns `<route> → { baseURL? }`;命名空间不可读时返回空。
129
137
  */
130
- export declare function readPiAiProviderRoutes(settings: SettingsProvider): Readonly<Record<string, {
138
+ export declare function readPiAiProviderRoutes(settings: SettingsReader): Readonly<Record<string, {
131
139
  baseURL?: string;
132
140
  }>>;
133
141
  /**
@@ -137,7 +145,7 @@ export declare function readPiAiProviderRoutes(settings: SettingsProvider): Read
137
145
  * @param settings - the settings service (reads the llm-pi-ai namespace).
138
146
  * @param credentials - the credentials service (resolves the env refs).
139
147
  */
140
- export declare function resolveSubscriptionKeys(settings: SettingsProvider, credentials: CredentialProvider): Promise<{
148
+ export declare function resolveSubscriptionKeys(settings: SettingsReader, credentials: CredentialProvider): Promise<{
141
149
  keys: SubscriptionKeys;
142
150
  identified: IdentifiedSubscriptionPlan[];
143
151
  }>;
@@ -159,4 +167,5 @@ export declare function adaptSessionPersistence(raw: unknown): UsagePersistence;
159
167
  * @param config - optional statsPath override.
160
168
  */
161
169
  export declare function apply(ctx: Context, config?: UsageBillingConfig): void;
170
+ export {};
162
171
  //# sourceMappingURL=index.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.4.7",
4
+ "version": "1.4.9",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "deepseek",
@@ -66,8 +66,12 @@
66
66
  "0.1.5-alpha.2": "compatible",
67
67
  "0.1.5-rc.1": "compatible",
68
68
  "0.1.5-rc.2": "compatible",
69
+ "0.1.5-rc.3": "compatible",
69
70
  "0.1.6-alpha.1": "compatible",
70
- "0.1.6-alpha.2": "compatible"
71
+ "0.1.6-alpha.2": "compatible",
72
+ "0.1.7-alpha.1": "compatible",
73
+ "0.1.7-alpha.2": "compatible",
74
+ "0.1.7-rc.1": "compatible"
71
75
  }
72
76
  }
73
77
  },
@@ -112,43 +116,69 @@
112
116
  "@deepseek-ai/cordis": "*",
113
117
  "@deepseek-ai/cordis-plugin-include": "^1.0.7",
114
118
  "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
115
- "@deepseek-ai/dsh-api-gateway": "0.1.2-rc.1",
116
- "@deepseek-ai/dsh-api-remotes": "0.1.2-rc.1",
117
- "@deepseek-ai/dsh-api-session-controller": "0.1.2-rc.1",
118
- "@deepseek-ai/dsh-atomic-write": "0.1.2-rc.1",
119
- "@deepseek-ai/dsh-client-locale": "0.1.2-rc.1",
120
- "@deepseek-ai/dsh-client-store": "0.1.2-rc.1",
121
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1",
122
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
123
- "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
124
- "@deepseek-ai/dsh-client-ui-sidebar": "0.1.2-rc.1",
125
- "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
126
- "@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
127
- "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1",
128
- "@deepseek-ai/dsh-home-paths": "0.1.2-rc.1",
129
- "@deepseek-ai/dsh-invariants": "0.1.2-rc.1",
130
- "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
131
- "@deepseek-ai/dsh-scope": "0.1.2-rc.1",
132
- "@deepseek-ai/dsh-session": "0.1.2-rc.1",
133
- "@deepseek-ai/dsh-session-persistence": "0.1.2-rc.1",
134
- "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
135
- "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
136
- "@deepseek-ai/dsh-typert-protocol": "0.1.2-rc.1",
119
+ "@deepseek-ai/dsh-api-gateway": "0.1.7-rc.1",
120
+ "@deepseek-ai/dsh-api-remotes": "0.1.7-rc.1",
121
+ "@deepseek-ai/dsh-api-session-controller": "0.1.7-rc.1",
122
+ "@deepseek-ai/dsh-atomic-write": "0.1.7-rc.1",
123
+ "@deepseek-ai/dsh-agent": "0.1.7-rc.1",
124
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
125
+ "@deepseek-ai/dsh-client-store": "0.1.7-rc.1",
126
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.1",
127
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
128
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
129
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.1.7-rc.1",
130
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
131
+ "@deepseek-ai/dsh-credentials": "0.1.7-rc.1",
132
+ "@deepseek-ai/dsh-host-webserver": "0.1.7-rc.1",
133
+ "@deepseek-ai/dsh-home-paths": "0.1.7-rc.1",
134
+ "@deepseek-ai/dsh-invariants": "0.1.7-rc.1",
135
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.1",
136
+ "@deepseek-ai/dsh-ptc-runtime": "0.1.7-rc.1",
137
+ "@deepseek-ai/dsh-sandbox": "0.1.7-rc.1",
138
+ "@deepseek-ai/dsh-sandbox-policy": "0.1.7-rc.1",
139
+ "@deepseek-ai/dsh-scope": "0.1.7-rc.1",
140
+ "@deepseek-ai/dsh-session": "0.1.7-rc.1",
141
+ "@deepseek-ai/dsh-session-persistence": "0.1.7-rc.1",
142
+ "@deepseek-ai/dsh-settings": "0.1.7-rc.1",
143
+ "@deepseek-ai/dsh-system-prompt": "0.1.7-rc.1",
144
+ "@deepseek-ai/dsh-tools": "0.1.7-rc.1",
145
+ "@deepseek-ai/dsh-typert-protocol": "0.1.7-rc.1",
146
+ "@deepseek-ai/dsh-user-approval": "0.1.7-rc.1",
147
+ "@deepseek-ai/dsh-util-workspace-path": "0.1.7-rc.1",
137
148
  "@deepseek-ai/schemastery": "^3.18.2",
149
+ "@shikijs/langs": "^4.3.1",
138
150
  "@testing-library/dom": "^10.4.1",
139
151
  "@testing-library/react": "^16.1.0",
140
152
  "@types/node": "^22.20.0",
141
153
  "@types/react": "~18.3.1",
142
154
  "@types/react-dom": "^19.2.7",
155
+ "immer": "^10.1.1",
156
+ "anser": "^2.3.5",
157
+ "diff": "^9.0.0",
143
158
  "jsdom": "^26.1.0",
159
+ "katex": "^0.16.47",
144
160
  "lightningcss": "^1.32.0",
161
+ "mdast-util-from-markdown": "^2.0.3",
162
+ "mdast-util-gfm": "^3.1.0",
163
+ "mdast-util-math": "^3.0.0",
164
+ "micromark-core-commonmark": "^2.0.3",
165
+ "micromark-extension-gfm": "^3.0.0",
166
+ "micromark-extension-math": "^3.1.0",
167
+ "micromark-factory-space": "^2.0.1",
168
+ "micromark-util-character": "^2.1.1",
169
+ "micromark-util-classify-character": "^2.0.1",
170
+ "micromark-util-sanitize-uri": "^2.0.1",
171
+ "micromark-util-symbol": "^2.0.1",
145
172
  "react": "^18.2.0",
146
173
  "react-dom": "^18.2.0",
147
174
  "semver": "^7.7.2",
175
+ "shiki": "^4.3.1",
176
+ "simple-icons": "^16.32.0",
148
177
  "tsdown": "^0.22.2",
149
178
  "typescript": "^6.0.3",
150
179
  "use-sync-external-store": "1.2.0",
151
- "vitest": "^4.1.8"
180
+ "vitest": "^4.1.8",
181
+ "zustand": "~4.4.7"
152
182
  },
153
183
  "files": [
154
184
  "lib/index.js",