dsh-ds-balance 2.2.0 → 2.3.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/README.md +7 -3
- package/README_en.md +5 -1
- package/lib/adapters/domain-core-store.d.ts +46 -2
- package/lib/adapters/domain-core-store.js +142 -4
- package/lib/adapters/salt-file.d.ts +1 -1
- package/lib/adapters/salt-file.js +1 -1
- package/lib/config.d.ts +1 -1
- package/lib/config.js +1 -1
- package/lib/domain/balance.d.ts +20 -0
- package/lib/domain/balance.js +20 -1
- package/lib/http/handlers.d.ts +1 -1
- package/lib/http/handlers.js +1 -1
- package/lib/http/routes.d.ts +1 -1
- package/lib/http/routes.js +1 -1
- package/lib/http/wire.d.ts +1 -1
- package/lib/http/wire.js +1 -1
- package/lib/index.js +1 -1
- package/lib/ports/core-store.d.ts +16 -1
- package/lib/ports/core-store.js +1 -1
- package/lib/services/balance-source.d.ts +16 -3
- package/lib/services/balance-source.js +32 -6
- package/lib/services/source-readers.d.ts +2 -0
- package/lib/services/source-readers.js +14 -4
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +25 -25
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
|
|
102
102
|
### 前置
|
|
103
103
|
|
|
104
|
-
- **DSH**:版本范围以 [package.json](package.json)
|
|
104
|
+
- **DSH**:版本范围以 [package.json](package.json) 的 `engines.dsh` 为唯一真源(与全部 `@deepseek-ai/dsh-*` 声明逐字相同),本插件跟的是宿主当前那条 **RC 线**(dist-tag 是 `next`)。声明面同时承诺**两族**(RC 族与仍可用的旧 RC 族,用 `||` 显式枚举 —— npm 只在同一 `major.minor.patch` 的预发布之间匹配,单区间罩不住两族),具体值一律现查,本文件不抄那个会漂的值。
|
|
105
105
|
- **Node**:`>= 20`(同一处声明)。
|
|
106
106
|
|
|
107
107
|
装宿主时**要显式点名版本线**:`@deepseek-ai/dsh` 的 `alpha` 与 `latest` 都**不是**我们声明的那条线 —— 按默认方式装会落在声明范围之外。
|
|
@@ -132,6 +132,9 @@ dsh plugin --profile web add "$PWD"
|
|
|
132
132
|
|
|
133
133
|
与 npm 那条路一样,重启后生效。
|
|
134
134
|
|
|
135
|
+
⚠️ 上面那步 `npm install` 不能省:`link:` 装法下依赖由仓库自己提供(见 [AGENTS.md](AGENTS.md)),
|
|
136
|
+
漏装则插件显示「未运行」(`ERR_MODULE_NOT_FOUND`)。
|
|
137
|
+
|
|
135
138
|
### 发现与安装
|
|
136
139
|
|
|
137
140
|
- **npm**:[`dsh-ds-balance`](https://www.npmjs.com/package/dsh-ds-balance)
|
|
@@ -150,8 +153,9 @@ dsh plugin --profile web add "$PWD"
|
|
|
150
153
|
浏览器半边会整个不渲染 —— 圆环与浮层也一起消失)。插件不查宿主版本号,而是**当场探测那个能力**;
|
|
151
154
|
拿不到时浮层里会多一行英文提示,说明配置界面为什么不可用、该往哪儿升级。
|
|
152
155
|
技术细节见 [配置表单的能力探测](docs/ARCHITECTURE.md)。
|
|
153
|
-
- **兼容性不是推断出来的**:每周由 [compat.yml](.github/workflows/compat.yml) 在 `next`(RC
|
|
154
|
-
|
|
156
|
+
- **兼容性不是推断出来的**:每周由 [compat.yml](.github/workflows/compat.yml) 在 `next`(RC 线,承诺线)与
|
|
157
|
+
声明面里的**第二族**(旧 RC 族,记录线)上换包实跑一遍现有测试,另有一步单独判声明面罩不罩得住;
|
|
158
|
+
承诺线红了会开或更新一条固定标题的跟踪 issue。
|
|
155
159
|
当前结论与红了怎么办见 [兼容性](docs/PUBLISHING.md#兼容性)。
|
|
156
160
|
|
|
157
161
|
## 配置
|
package/README_en.md
CHANGED
|
@@ -103,7 +103,7 @@ The division of labour is fixed: **the ring answers "roughly how much is left",
|
|
|
103
103
|
|
|
104
104
|
### Requirements
|
|
105
105
|
|
|
106
|
-
- **DSH**: the range
|
|
106
|
+
- **DSH**: the range lives in `engines.dsh` in [package.json](package.json) — the single source of truth, character-for-character identical to every `@deepseek-ai/dsh-*` entry. This plugin follows the host's current **RC line** (the dist-tag is `next`). The declared surface commits to **two** prerelease families (the RC family and the still-usable older one, enumerated with `||` — npm only matches prereleases within the same `major.minor.patch`, so a single range cannot cover two families); the exact value drifts, so look it up rather than trusting this file.
|
|
107
107
|
- **Node**: `>= 20` (declared in the same place).
|
|
108
108
|
|
|
109
109
|
Install the host by **naming the version line explicitly**: neither `alpha` nor `latest` of `@deepseek-ai/dsh` is the line we declare — a default install lands outside the declared range.
|
|
@@ -134,6 +134,10 @@ dsh plugin --profile web add "$PWD"
|
|
|
134
134
|
|
|
135
135
|
Same as the npm route: it takes effect after a restart.
|
|
136
136
|
|
|
137
|
+
⚠️ The `npm install` above is not optional: under the `link:` form the repository supplies its
|
|
138
|
+
own dependencies (see [AGENTS.md](AGENTS.md)) — skip it and the plugin shows as "not running"
|
|
139
|
+
(`ERR_MODULE_NOT_FOUND`).
|
|
140
|
+
|
|
137
141
|
### Discovery
|
|
138
142
|
|
|
139
143
|
- **npm**: [`dsh-ds-balance`](https://www.npmjs.com/package/dsh-ds-balance)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 用 dsh 官方存储接缝实现 {@link CoreStore}。
|
|
3
3
|
*
|
|
4
|
-
* 关键约束(见 docs/
|
|
4
|
+
* 关键约束(见 docs/BACKEND-CONTRACTS.md §10):
|
|
5
5
|
* - `open` 每进程只能一次,重名抛 `already-open`;
|
|
6
6
|
* - **`close()` 必须由调用方在 `ctx.effect` 的 disposer 里调用**,否则热重挂会锁死;
|
|
7
7
|
* - 打开失败要**降级**而不是抛出去 —— 未观察的 rejection 曾把宿主整个拖下水。
|
|
8
8
|
* @module dsh-ds-balance/adapters/domain-core-store
|
|
9
9
|
*/
|
|
10
|
-
import type
|
|
10
|
+
import { type BalanceSnapshot, type BalanceSource } from '../domain/balance.js';
|
|
11
11
|
import type { CoreStore, StoreHealth } from '../ports/core-store.js';
|
|
12
12
|
import type { Logger } from '../ports/logger.js';
|
|
13
13
|
/** 域里的表名。 */
|
|
@@ -41,8 +41,11 @@ export declare const DS_BALANCE_DOMAIN: {
|
|
|
41
41
|
/** 表句柄的最小结构化面;测试用替身实现它。 */
|
|
42
42
|
export interface KvTableLike {
|
|
43
43
|
get(key: string): StoredSnapshot | undefined;
|
|
44
|
+
/** 一次迭代同时拿到 `[key, record]`;比 `keys()` +逐键 `get()` 少一轮查找。 */
|
|
45
|
+
entries(): IterableIterator<[string, StoredSnapshot]>;
|
|
44
46
|
keys(): IterableIterator<string>;
|
|
45
47
|
put(key: string, value: StoredSnapshot): Promise<void>;
|
|
48
|
+
delete(key: string): Promise<boolean>;
|
|
46
49
|
}
|
|
47
50
|
/** 域句柄的最小结构化面。 */
|
|
48
51
|
export interface DomainLike {
|
|
@@ -95,8 +98,49 @@ export declare class DomainCoreStore implements CoreStore {
|
|
|
95
98
|
*
|
|
96
99
|
* **按 `accountTag` 过滤**:凭据轮换后 tag 会变,旧快照不得混用。
|
|
97
100
|
* 用 `snapshotId`(单调可排序)决定新旧,不依赖 `fetchedAt`。
|
|
101
|
+
*
|
|
102
|
+
* **走 `entries()` 而不是 `keys()` + 逐键 `get()`**:后者是每条记录两次查找,
|
|
103
|
+
* 记录数涨上去(保留策略落地前实测过千条)时白费一大半;`entries()` 一次迭代给全。
|
|
98
104
|
*/
|
|
99
105
|
loadLatestSnapshot(accountTag: string): Promise<BalanceSnapshot | null>;
|
|
106
|
+
/**
|
|
107
|
+
* 只保留该账本最近 `keepN` 条,删掉更旧的(跨账本不动)。
|
|
108
|
+
*
|
|
109
|
+
* **批间让出事件循环的理由是摊平写放大,不是防限流**:宿主 json 后端默认 `single`
|
|
110
|
+
* 布局,每次 `delete` 都要原子重写整份单元文件。删几十条若挤在同一个 tick 上,
|
|
111
|
+
* 会连带把同一进程里的取数与轮询一起拖住。所以内部**分批 + 每批之后让出一次** ——
|
|
112
|
+
* 批次大小与间隔属于实现细节,不上端口(领域层只调一次,不需要知道分了几批)。
|
|
113
|
+
*
|
|
114
|
+
* **单次最多删 {@link PRUNE_MAX_PER_CALL} 条**:超出部分留给后续启动或写入路径,
|
|
115
|
+
* 保证任何一次调用的耗时都有确定上界(理由见那个常量)。
|
|
116
|
+
*
|
|
117
|
+
* **零写入的幂等**:没超期时一条都不删、返回 0(超期判定先做完再动手)。
|
|
118
|
+
*
|
|
119
|
+
* @param accountTag - 要修剪的账本。
|
|
120
|
+
* @param keepN - 保留条数(新到旧)。
|
|
121
|
+
* @returns 本次**实际**删掉的条数(可能小于超期总数,因为有单次上限)。
|
|
122
|
+
*/
|
|
123
|
+
pruneByTag(accountTag: string, keepN: number): Promise<number>;
|
|
124
|
+
/**
|
|
125
|
+
* 修剪一个账本的内部实现(不重复 `requireTable` 的开表开销)。
|
|
126
|
+
*/
|
|
127
|
+
private pruneOwned;
|
|
128
|
+
/**
|
|
129
|
+
* 启动时的一次性清理:把**所有**账本桶各修剪一次。
|
|
130
|
+
*
|
|
131
|
+
* 为什么扫全表而不是只清「当前那个账本」:启动这一刻还不知道会用哪个 `accountTag`
|
|
132
|
+
* (要等 reader 给),而首次升级的迁移诉求是「把所有攒下来的桶都收一遍」——
|
|
133
|
+
* 只清一个桶的话,凭据轮换出来的旧桶永远清不掉,文件就一直大。
|
|
134
|
+
*
|
|
135
|
+
* **失败绝不影响 open**:清理抛错只记一条 warn。这里抛出去会让
|
|
136
|
+
* `ensureOpen` 判成打开失败,整个存储被打成降级 —— 那比慢更糟。
|
|
137
|
+
* 删到一半也是安全的:清理幂等,下次启动继续。
|
|
138
|
+
*/
|
|
139
|
+
private pruneAllLedgersOnOpen;
|
|
140
|
+
/** 取出属于该账本的全部记录。 */
|
|
141
|
+
private collectOwned;
|
|
142
|
+
/** 分批删除,每批之后让出一次事件循环。 */
|
|
143
|
+
private deleteInBatches;
|
|
100
144
|
health(): Promise<StoreHealth>;
|
|
101
145
|
/** 幂等关闭。**必须挂在 `ctx.effect` 的 disposer 上。** */
|
|
102
146
|
close(): Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 用 dsh 官方存储接缝实现 {@link CoreStore}。
|
|
3
3
|
*
|
|
4
|
-
* 关键约束(见 docs/
|
|
4
|
+
* 关键约束(见 docs/BACKEND-CONTRACTS.md §10):
|
|
5
5
|
* - `open` 每进程只能一次,重名抛 `already-open`;
|
|
6
6
|
* - **`close()` 必须由调用方在 `ctx.effect` 的 disposer 里调用**,否则热重挂会锁死;
|
|
7
7
|
* - 打开失败要**降级**而不是抛出去 —— 未观察的 rejection 曾把宿主整个拖下水。
|
|
@@ -9,10 +9,35 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain';
|
|
11
11
|
import { z } from 'zod';
|
|
12
|
+
import { SNAPSHOT_KEEP_N, } from '../domain/balance.js';
|
|
12
13
|
import { StorageError, describeError } from '../domain/errors.js';
|
|
13
14
|
import { formatMoney, parseMoney } from '../domain/money.js';
|
|
14
15
|
/** 域里的表名。 */
|
|
15
16
|
export const SNAPSHOT_TABLE = 'snapshots';
|
|
17
|
+
/**
|
|
18
|
+
* 修剪时每批删多少条,之后让出一次事件循环。
|
|
19
|
+
*
|
|
20
|
+
* **实现细节,不上端口**:领域层只调一次 `pruneByTag`,不需要知道分了几批。
|
|
21
|
+
* 取值只是「别让一批太大」的粗界,真实瓶颈是单布局下的整份重写次数,
|
|
22
|
+
* 所以调大调小不影响正确性,只影响同进程别的活被挤住多久。
|
|
23
|
+
*/
|
|
24
|
+
const PRUNE_BATCH_SIZE = 40;
|
|
25
|
+
/**
|
|
26
|
+
* 单次修剪最多删多少条,**之后剩下的交给后续启动或写入路径**。
|
|
27
|
+
*
|
|
28
|
+
* **为什么需要上界**:`single` 布局下每次 `delete` 都要原子重写整份单元文件,
|
|
29
|
+
* 而「删掉一批」的开销随文件体积线性涨。首次升级时存量可能很大(本机实测 9000 条 /
|
|
30
|
+
* 3.8 MB),若一次性清空就是数千次整份重写,**会把宿主连同 Agent 一起卡死**
|
|
31
|
+
* —— 实测现象是侧栏无限转圈、Agent 停止响应,清空数据后立刻恢复。
|
|
32
|
+
*
|
|
33
|
+
* 这个数是**实现约束**不是领域策略:领域层只说「留几条」,「一次最多删多少」
|
|
34
|
+
* 属于「别把自己卡死」的工程判断,所以不上端口(与批次大小同理)。
|
|
35
|
+
*
|
|
36
|
+
* 取 500 的依据:配合 {@link PRUNE_BATCH_SIZE} 是 12.5 批,批次边界清晰;
|
|
37
|
+
* 收敛靠**多次启动叠加**而不是一次删完 —— 清到 20 条这个终态是幂等的,
|
|
38
|
+
* 每次启动删一批,未删完的下次继续。
|
|
39
|
+
*/
|
|
40
|
+
const PRUNE_MAX_PER_CALL = 500;
|
|
16
41
|
const storedBalanceSchema = z.object({
|
|
17
42
|
currency: z.string(),
|
|
18
43
|
total: z.string(),
|
|
@@ -127,6 +152,10 @@ export class DomainCoreStore {
|
|
|
127
152
|
this.handle = handle;
|
|
128
153
|
this.table = handle.table(SNAPSHOT_TABLE);
|
|
129
154
|
this.openError = undefined;
|
|
155
|
+
// 首次升级的一次性清理:必须在**任何读取之前**做,否则首次
|
|
156
|
+
// loadLatestSnapshot 仍要面对存量的大文件。等下一次抓取再清太晚 ——
|
|
157
|
+
// 那正是「保留策略只在稳态有效、首次升级要等两小时」那个盲区。
|
|
158
|
+
await this.pruneAllLedgersOnOpen(SNAPSHOT_KEEP_N);
|
|
130
159
|
}
|
|
131
160
|
/** 拿到可用表,或在降级状态下抛出可读错误。 */
|
|
132
161
|
async requireTable() {
|
|
@@ -149,19 +178,128 @@ export class DomainCoreStore {
|
|
|
149
178
|
*
|
|
150
179
|
* **按 `accountTag` 过滤**:凭据轮换后 tag 会变,旧快照不得混用。
|
|
151
180
|
* 用 `snapshotId`(单调可排序)决定新旧,不依赖 `fetchedAt`。
|
|
181
|
+
*
|
|
182
|
+
* **走 `entries()` 而不是 `keys()` + 逐键 `get()`**:后者是每条记录两次查找,
|
|
183
|
+
* 记录数涨上去(保留策略落地前实测过千条)时白费一大半;`entries()` 一次迭代给全。
|
|
152
184
|
*/
|
|
153
185
|
async loadLatestSnapshot(accountTag) {
|
|
154
186
|
const table = await this.requireTable();
|
|
155
187
|
let newest = null;
|
|
156
|
-
for (const
|
|
157
|
-
|
|
158
|
-
if (record === undefined || record.accountTag !== accountTag)
|
|
188
|
+
for (const [, record] of table.entries()) {
|
|
189
|
+
if (record.accountTag !== accountTag)
|
|
159
190
|
continue;
|
|
160
191
|
if (newest === null || record.snapshotId > newest.snapshotId)
|
|
161
192
|
newest = record;
|
|
162
193
|
}
|
|
163
194
|
return newest === null ? null : fromStored(newest);
|
|
164
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* 只保留该账本最近 `keepN` 条,删掉更旧的(跨账本不动)。
|
|
198
|
+
*
|
|
199
|
+
* **批间让出事件循环的理由是摊平写放大,不是防限流**:宿主 json 后端默认 `single`
|
|
200
|
+
* 布局,每次 `delete` 都要原子重写整份单元文件。删几十条若挤在同一个 tick 上,
|
|
201
|
+
* 会连带把同一进程里的取数与轮询一起拖住。所以内部**分批 + 每批之后让出一次** ——
|
|
202
|
+
* 批次大小与间隔属于实现细节,不上端口(领域层只调一次,不需要知道分了几批)。
|
|
203
|
+
*
|
|
204
|
+
* **单次最多删 {@link PRUNE_MAX_PER_CALL} 条**:超出部分留给后续启动或写入路径,
|
|
205
|
+
* 保证任何一次调用的耗时都有确定上界(理由见那个常量)。
|
|
206
|
+
*
|
|
207
|
+
* **零写入的幂等**:没超期时一条都不删、返回 0(超期判定先做完再动手)。
|
|
208
|
+
*
|
|
209
|
+
* @param accountTag - 要修剪的账本。
|
|
210
|
+
* @param keepN - 保留条数(新到旧)。
|
|
211
|
+
* @returns 本次**实际**删掉的条数(可能小于超期总数,因为有单次上限)。
|
|
212
|
+
*/
|
|
213
|
+
async pruneByTag(accountTag, keepN) {
|
|
214
|
+
const table = await this.requireTable();
|
|
215
|
+
return this.pruneOwned(table, accountTag, keepN);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* 修剪一个账本的内部实现(不重复 `requireTable` 的开表开销)。
|
|
219
|
+
*/
|
|
220
|
+
async pruneOwned(table, accountTag, keepN) {
|
|
221
|
+
if (keepN < 0)
|
|
222
|
+
return 0;
|
|
223
|
+
const owned = this.collectOwned(table, accountTag);
|
|
224
|
+
if (owned.length <= keepN)
|
|
225
|
+
return 0;
|
|
226
|
+
// 先按 snapshotId 排好序再决定删哪些:留下的必然是「最新的 keepN 条」,
|
|
227
|
+
// 而刚写入的那条 id 单调最大,所以永远在保留集里。
|
|
228
|
+
const ordered = owned
|
|
229
|
+
.slice()
|
|
230
|
+
.sort((a, b) => (a.snapshotId < b.snapshotId ? -1 : a.snapshotId > b.snapshotId ? 1 : 0));
|
|
231
|
+
const doomed = ordered.slice(0, ordered.length - keepN).slice(0, PRUNE_MAX_PER_CALL);
|
|
232
|
+
await this.deleteInBatches(table, doomed);
|
|
233
|
+
return doomed.length;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 启动时的一次性清理:把**所有**账本桶各修剪一次。
|
|
237
|
+
*
|
|
238
|
+
* 为什么扫全表而不是只清「当前那个账本」:启动这一刻还不知道会用哪个 `accountTag`
|
|
239
|
+
* (要等 reader 给),而首次升级的迁移诉求是「把所有攒下来的桶都收一遍」——
|
|
240
|
+
* 只清一个桶的话,凭据轮换出来的旧桶永远清不掉,文件就一直大。
|
|
241
|
+
*
|
|
242
|
+
* **失败绝不影响 open**:清理抛错只记一条 warn。这里抛出去会让
|
|
243
|
+
* `ensureOpen` 判成打开失败,整个存储被打成降级 —— 那比慢更糟。
|
|
244
|
+
* 删到一半也是安全的:清理幂等,下次启动继续。
|
|
245
|
+
*/
|
|
246
|
+
async pruneAllLedgersOnOpen(keepN) {
|
|
247
|
+
const table = this.table;
|
|
248
|
+
if (table === null)
|
|
249
|
+
return;
|
|
250
|
+
try {
|
|
251
|
+
const buckets = new Map();
|
|
252
|
+
for (const [, record] of table.entries()) {
|
|
253
|
+
const bucket = buckets.get(record.accountTag);
|
|
254
|
+
if (bucket === undefined)
|
|
255
|
+
buckets.set(record.accountTag, [record]);
|
|
256
|
+
else
|
|
257
|
+
bucket.push(record);
|
|
258
|
+
}
|
|
259
|
+
let removed = 0;
|
|
260
|
+
let remaining = 0;
|
|
261
|
+
for (const accountTag of buckets.keys()) {
|
|
262
|
+
removed += await this.pruneOwned(table, accountTag, keepN);
|
|
263
|
+
}
|
|
264
|
+
for (const bucket of buckets.values())
|
|
265
|
+
remaining += Math.min(bucket.length, keepN);
|
|
266
|
+
if (removed > 0) {
|
|
267
|
+
// 进度日志:删了多少、之后大约还剩多少(每次启动还会再删一批,直到收敛)。
|
|
268
|
+
// 没有它,使用者只会看到「数据怎么还在变多」,无从判断清理是否在推进。
|
|
269
|
+
this.options.logger?.debug('ds-balance: pruned stored snapshots on open', {
|
|
270
|
+
removed,
|
|
271
|
+
remainingApprox: remaining,
|
|
272
|
+
buckets: buckets.size,
|
|
273
|
+
keepN,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
// 刻意只记一条 warn:清理是尽力而为,不该把存储打成降级。
|
|
279
|
+
this.options.logger?.warn('ds-balance: startup snapshot prune failed, continuing', {
|
|
280
|
+
error: describeError(error),
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/** 取出属于该账本的全部记录。 */
|
|
285
|
+
collectOwned(table, accountTag) {
|
|
286
|
+
const owned = [];
|
|
287
|
+
for (const [, record] of table.entries()) {
|
|
288
|
+
if (record.accountTag === accountTag)
|
|
289
|
+
owned.push(record);
|
|
290
|
+
}
|
|
291
|
+
return owned;
|
|
292
|
+
}
|
|
293
|
+
/** 分批删除,每批之后让出一次事件循环。 */
|
|
294
|
+
async deleteInBatches(table, doomed) {
|
|
295
|
+
for (let offset = 0; offset < doomed.length; offset += PRUNE_BATCH_SIZE) {
|
|
296
|
+
const batch = doomed.slice(offset, offset + PRUNE_BATCH_SIZE);
|
|
297
|
+
for (const record of batch)
|
|
298
|
+
await table.delete(record.snapshotId);
|
|
299
|
+
// 让出一次事件循环:把同 tick 的连续原子重写摊开,别挤住同进程的别的活。
|
|
300
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
165
303
|
async health() {
|
|
166
304
|
if (this.closed)
|
|
167
305
|
return { ok: false, detail: 'closed' };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 服务端盐的读写。
|
|
3
3
|
*
|
|
4
|
-
* `accountTag` 由它派生,所以**它丢了账本就会孤立**:见 docs/
|
|
4
|
+
* `accountTag` 由它派生,所以**它丢了账本就会孤立**:见 docs/BACKEND-CONTRACTS.md §19 第 5 条。
|
|
5
5
|
* 文件权限 0600;路径由组装点用 `@deepseek-ai/dsh-home-paths` 的 `dshHomePath()` 拼出来。
|
|
6
6
|
* @module dsh-ds-balance/adapters/salt-file
|
|
7
7
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 服务端盐的读写。
|
|
3
3
|
*
|
|
4
|
-
* `accountTag` 由它派生,所以**它丢了账本就会孤立**:见 docs/
|
|
4
|
+
* `accountTag` 由它派生,所以**它丢了账本就会孤立**:见 docs/BACKEND-CONTRACTS.md §19 第 5 条。
|
|
5
5
|
* 文件权限 0600;路径由组装点用 `@deepseek-ai/dsh-home-paths` 的 `dshHomePath()` 拼出来。
|
|
6
6
|
* @module dsh-ds-balance/adapters/salt-file
|
|
7
7
|
*/
|
package/lib/config.d.ts
CHANGED
package/lib/config.js
CHANGED
package/lib/domain/balance.d.ts
CHANGED
|
@@ -46,6 +46,26 @@ export interface BalanceSnapshot {
|
|
|
46
46
|
/** 原始响应,审计用。 */
|
|
47
47
|
raw: unknown;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* 每个账本保留多少条快照。**历史保留深度,不是功能开关。**
|
|
51
|
+
*
|
|
52
|
+
* **为什么住在 domain**:写入路径(`SourceLedger.persist`)与启动清理
|
|
53
|
+
* (`DomainCoreStore.initialize`)都要读它,而 adapters 与 services 互相不能依赖 ——
|
|
54
|
+
* `domain/` 是两层共同的下游,是唯一能放这个常量的地方。
|
|
55
|
+
* (最初它住在 `balance-source.ts` 顶部,理由是「紧挨唯一调用点」;启动清理加入后
|
|
56
|
+
* 调用点变成两个,那条理由就不再成立。)
|
|
57
|
+
*
|
|
58
|
+
* - 当前**没有任何功能读第二条以后的记录**:唯一读点是 `loadLatestSnapshot` 取最新一条,
|
|
59
|
+
* 仓内没有 `listSnapshots`,也没有历史端点(`docs/ARCHITECTURE.md` 的阶段边界
|
|
60
|
+
* 明写不做图表)。所以这个数**不是**为功能服务的,不要因为「用不到」就调到 1。
|
|
61
|
+
* - 它真正的作用是**故障回退余量**:最新那条写失败、写坏、或落盘途中崩了时还能退到上一条,
|
|
62
|
+
* 覆盖连续两次失败就要 ≥ 3。
|
|
63
|
+
* - **刻意不进配置面**:配置字段用户看得见、改得动,而这个数没有用户可感知的语义 ——
|
|
64
|
+
* 改小它用户看不出界面变化,只会在某天丢快照时才后悔。给一个看不出区别的旋钮比不给更糟。
|
|
65
|
+
*
|
|
66
|
+
* 量级:一条快照约 0.9 KB,20 条约 18 KB —— 余量与存储成本之间没有取舍压力。
|
|
67
|
+
*/
|
|
68
|
+
export declare const SNAPSHOT_KEEP_N = 20;
|
|
49
69
|
/** 一个币种的预警 / 告急阈值。 */
|
|
50
70
|
export interface ThresholdPair {
|
|
51
71
|
warn: Units;
|
package/lib/domain/balance.js
CHANGED
|
@@ -4,5 +4,24 @@
|
|
|
4
4
|
* 这里定义的是**契约形状**:金额一律 `bigint` 最小单位,序列化时才变字符串。
|
|
5
5
|
* @module dsh-ds-balance/domain/balance
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* 每个账本保留多少条快照。**历史保留深度,不是功能开关。**
|
|
9
|
+
*
|
|
10
|
+
* **为什么住在 domain**:写入路径(`SourceLedger.persist`)与启动清理
|
|
11
|
+
* (`DomainCoreStore.initialize`)都要读它,而 adapters 与 services 互相不能依赖 ——
|
|
12
|
+
* `domain/` 是两层共同的下游,是唯一能放这个常量的地方。
|
|
13
|
+
* (最初它住在 `balance-source.ts` 顶部,理由是「紧挨唯一调用点」;启动清理加入后
|
|
14
|
+
* 调用点变成两个,那条理由就不再成立。)
|
|
15
|
+
*
|
|
16
|
+
* - 当前**没有任何功能读第二条以后的记录**:唯一读点是 `loadLatestSnapshot` 取最新一条,
|
|
17
|
+
* 仓内没有 `listSnapshots`,也没有历史端点(`docs/ARCHITECTURE.md` 的阶段边界
|
|
18
|
+
* 明写不做图表)。所以这个数**不是**为功能服务的,不要因为「用不到」就调到 1。
|
|
19
|
+
* - 它真正的作用是**故障回退余量**:最新那条写失败、写坏、或落盘途中崩了时还能退到上一条,
|
|
20
|
+
* 覆盖连续两次失败就要 ≥ 3。
|
|
21
|
+
* - **刻意不进配置面**:配置字段用户看得见、改得动,而这个数没有用户可感知的语义 ——
|
|
22
|
+
* 改小它用户看不出界面变化,只会在某天丢快照时才后悔。给一个看不出区别的旋钮比不给更糟。
|
|
23
|
+
*
|
|
24
|
+
* 量级:一条快照约 0.9 KB,20 条约 18 KB —— 余量与存储成本之间没有取舍压力。
|
|
25
|
+
*/
|
|
26
|
+
export const SNAPSHOT_KEEP_N = 20;
|
|
8
27
|
//# sourceMappingURL=balance.js.map
|
package/lib/http/handlers.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 依赖全部构造注入、**不碰 `ctx`** —— 因此它能脱离宿主直接测。
|
|
5
5
|
*
|
|
6
|
-
* 铁律(见 docs/
|
|
6
|
+
* 铁律(见 docs/BACKEND-CONTRACTS.md §19 第 3、4 条):
|
|
7
7
|
* - **任何 handler 都不许把异常抛出去**。抛出去会被宿主包成 500,前端就拿不到
|
|
8
8
|
* `error` 结构了。余额端点的业务错误一律走 `200 + state: error`。
|
|
9
9
|
* - **每次请求现读配置**,不许在闭包里捕获旧值。
|
package/lib/http/handlers.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 依赖全部构造注入、**不碰 `ctx`** —— 因此它能脱离宿主直接测。
|
|
5
5
|
*
|
|
6
|
-
* 铁律(见 docs/
|
|
6
|
+
* 铁律(见 docs/BACKEND-CONTRACTS.md §19 第 3、4 条):
|
|
7
7
|
* - **任何 handler 都不许把异常抛出去**。抛出去会被宿主包成 500,前端就拿不到
|
|
8
8
|
* `error` 结构了。余额端点的业务错误一律走 `200 + state: error`。
|
|
9
9
|
* - **每次请求现读配置**,不许在闭包里捕获旧值。
|
package/lib/http/routes.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 三层职责分得很开:本文件只管**注册**,`handlers.ts` 管**怎么做**,
|
|
5
5
|
* `wire.ts` 管**长什么样**。注册需要 `ctx`,所以只有本文件碰宿主。
|
|
6
6
|
*
|
|
7
|
-
* 契约要点(docs/
|
|
7
|
+
* 契约要点(docs/BACKEND-CONTRACTS.md §3.3 / §8.2):
|
|
8
8
|
* - `path` 写死精确值、不带尾随斜杠(实现是 Map 精确键匹配);
|
|
9
9
|
* - `requestBody` 必填;
|
|
10
10
|
* - 物理载体**已先做完信任与浏览器鉴权**,handler 不必再判一次。
|
package/lib/http/routes.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 三层职责分得很开:本文件只管**注册**,`handlers.ts` 管**怎么做**,
|
|
5
5
|
* `wire.ts` 管**长什么样**。注册需要 `ctx`,所以只有本文件碰宿主。
|
|
6
6
|
*
|
|
7
|
-
* 契约要点(docs/
|
|
7
|
+
* 契约要点(docs/BACKEND-CONTRACTS.md §3.3 / §8.2):
|
|
8
8
|
* - `path` 写死精确值、不带尾随斜杠(实现是 Map 精确键匹配);
|
|
9
9
|
* - `requestBody` 必填;
|
|
10
10
|
* - 物理载体**已先做完信任与浏览器鉴权**,handler 不必再判一次。
|
package/lib/http/wire.d.ts
CHANGED
package/lib/http/wire.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -313,7 +313,7 @@ export async function apply(ctx, refs) {
|
|
|
313
313
|
'deepseek-account',
|
|
314
314
|
new SourceLedger({
|
|
315
315
|
source: 'deepseek-account',
|
|
316
|
-
reader: accountReader({ account: () => account.current(), salt }),
|
|
316
|
+
reader: accountReader({ account: () => account.current(), salt, logger }),
|
|
317
317
|
store,
|
|
318
318
|
config: configService,
|
|
319
319
|
clock: systemClock,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 快照存储端口。实现走 dsh 官方存储接缝,见 docs/
|
|
2
|
+
* 快照存储端口。实现走 dsh 官方存储接缝,见 docs/BACKEND-CONTRACTS.md §10。
|
|
3
3
|
* @module dsh-ds-balance/ports/core-store
|
|
4
4
|
*/
|
|
5
5
|
import type { BalanceSnapshot } from '../domain/balance.js';
|
|
@@ -20,6 +20,21 @@ export interface CoreStore {
|
|
|
20
20
|
* @returns 最近一条快照,或 `null` 表示该账本还没有记录。
|
|
21
21
|
*/
|
|
22
22
|
loadLatestSnapshot(accountTag: string): Promise<BalanceSnapshot | null>;
|
|
23
|
+
/**
|
|
24
|
+
* 只保留该账本最近 `keepN` 条快照,删掉更旧的。
|
|
25
|
+
*
|
|
26
|
+
* 与 {@link loadLatestSnapshot} 同构:两个都是 **tag 作用域**的操作 ——
|
|
27
|
+
* 「按账本分桶、只动自己那个桶」这条语义由本端口统一承担,调用方不必知道记录怎么组织。
|
|
28
|
+
*
|
|
29
|
+
* **`keepN` 由调用方(领域层)传入**:端口只执行「留几条」,不决定该留几条 ——
|
|
30
|
+
* 那是领域层的策略。端口也不暴露实现步骤(分几批删、批间隔多久、怎么排序)。
|
|
31
|
+
*
|
|
32
|
+
* **只动 `accountTag` 这一个桶**:别的账本一条都不许碰。
|
|
33
|
+
* @param accountTag - 要修剪的账本。
|
|
34
|
+
* @param keepN - 该账本要保留的条数(新到旧)。
|
|
35
|
+
* @returns 实际删掉的条数;`0` 表示本来就没超期(**必须是零写入的幂等操作**)。
|
|
36
|
+
*/
|
|
37
|
+
pruneByTag(accountTag: string, keepN: number): Promise<number>;
|
|
23
38
|
/** 端口自检。 */
|
|
24
39
|
health(): Promise<StoreHealth>;
|
|
25
40
|
/** 释放存储句柄。**必须由调用方在 `ctx.effect` 的 disposer 里调用。** */
|
package/lib/ports/core-store.js
CHANGED
|
@@ -23,7 +23,13 @@ import type { ConfigService } from './config-service.js';
|
|
|
23
23
|
export interface SourceReader {
|
|
24
24
|
/** 这条路的凭据此刻可用吗。 */
|
|
25
25
|
available(): Promise<boolean>;
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* 这次快照的账本标识。
|
|
28
|
+
*
|
|
29
|
+
* **凭据 / 身份拿不到时抛错,由调用方吸收** —— 不能退化成某个占位标识:
|
|
30
|
+
* 账本键一旦落成别的值,那条快照就再也读不回来(`restore()` 会静默跳过,
|
|
31
|
+
* 下一轮拿到真标识后自愈)。
|
|
32
|
+
*/
|
|
27
33
|
tag(): Promise<string>;
|
|
28
34
|
/** 抓一次余额,连账本标识一起给。 */
|
|
29
35
|
read(): Promise<{
|
|
@@ -93,6 +99,8 @@ export declare class SourceLedger {
|
|
|
93
99
|
private inflight;
|
|
94
100
|
/** 落盘失败只报一次,避免每轮刷新都刷屏。 */
|
|
95
101
|
private persistWarned;
|
|
102
|
+
/** 修剪失败只报一次:与落盘分开记,两者是不同的降级事实。 */
|
|
103
|
+
private retentionWarned;
|
|
96
104
|
constructor(options: SourceLedgerOptions);
|
|
97
105
|
/** 这条路的 id。 */
|
|
98
106
|
source(): BalanceSource;
|
|
@@ -145,9 +153,14 @@ export declare class SourceLedger {
|
|
|
145
153
|
/** 跑一次真实抓取。**所有异常都在这里被吸收。** */
|
|
146
154
|
private fetchOnce;
|
|
147
155
|
/**
|
|
148
|
-
*
|
|
156
|
+
* 把快照落盘,并按账本执行保留策略。
|
|
157
|
+
*
|
|
158
|
+
* **两件事分别记账**:落盘失败与修剪失败**不能并进同一个 catch** ——
|
|
159
|
+
* 写已经成功时若把它报成「snapshot not persisted」,日志就在说谎,
|
|
160
|
+
* 而排查存储问题的人正是按这条日志判断写没写成功的。
|
|
149
161
|
*
|
|
150
|
-
*
|
|
162
|
+
* 落盘失败只记一次 warn(每轮刷新都会重来,不限流就刷屏);
|
|
163
|
+
* 修剪失败同样只记一次 —— 它让文件继续变大,是需要被看见的降级,但不该淹没主流程。
|
|
151
164
|
* @param snapshot - 刚归一化出来的快照。
|
|
152
165
|
*/
|
|
153
166
|
private persist;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module dsh-ds-balance/services/balance-source
|
|
9
9
|
*/
|
|
10
10
|
import { classify, describeError, parseRetryAfter, } from '../domain/errors.js';
|
|
11
|
+
import { SNAPSHOT_KEEP_N } from '../domain/balance.js';
|
|
11
12
|
import { normalize } from '../domain/normalize.js';
|
|
12
13
|
import { pickBalance } from '../domain/select.js';
|
|
13
14
|
import { severityOf, thresholdsFor } from '../domain/severity.js';
|
|
@@ -28,6 +29,8 @@ export class SourceLedger {
|
|
|
28
29
|
inflight = null;
|
|
29
30
|
/** 落盘失败只报一次,避免每轮刷新都刷屏。 */
|
|
30
31
|
persistWarned = false;
|
|
32
|
+
/** 修剪失败只报一次:与落盘分开记,两者是不同的降级事实。 */
|
|
33
|
+
retentionWarned = false;
|
|
31
34
|
constructor(options) {
|
|
32
35
|
this.options = options;
|
|
33
36
|
this.metrics = options.metrics ?? noopMetrics;
|
|
@@ -85,7 +88,11 @@ export class SourceLedger {
|
|
|
85
88
|
this.publishGauge();
|
|
86
89
|
}
|
|
87
90
|
catch (error) {
|
|
88
|
-
|
|
91
|
+
// **warn 而非 debug**:这条账本没能从存储恢复,是一条降级事实 ——
|
|
92
|
+
// 记在 debug 级别等于藏起来,使用者只会看到界面空着而无从判断原因。
|
|
93
|
+
// 仍**不设 state / errorCode**:恢复失败后前端自己会 getView() 走正常抓取,
|
|
94
|
+
// 在这里塞错误态会让「本来就没有快照」与「存储坏了」混成同一个处境。
|
|
95
|
+
this.options.logger?.warn('ds-balance: could not restore snapshot for this ledger', {
|
|
89
96
|
source: this.options.source,
|
|
90
97
|
error: describeError(error),
|
|
91
98
|
});
|
|
@@ -183,9 +190,14 @@ export class SourceLedger {
|
|
|
183
190
|
}
|
|
184
191
|
}
|
|
185
192
|
/**
|
|
186
|
-
*
|
|
193
|
+
* 把快照落盘,并按账本执行保留策略。
|
|
187
194
|
*
|
|
188
|
-
*
|
|
195
|
+
* **两件事分别记账**:落盘失败与修剪失败**不能并进同一个 catch** ——
|
|
196
|
+
* 写已经成功时若把它报成「snapshot not persisted」,日志就在说谎,
|
|
197
|
+
* 而排查存储问题的人正是按这条日志判断写没写成功的。
|
|
198
|
+
*
|
|
199
|
+
* 落盘失败只记一次 warn(每轮刷新都会重来,不限流就刷屏);
|
|
200
|
+
* 修剪失败同样只记一次 —— 它让文件继续变大,是需要被看见的降级,但不该淹没主流程。
|
|
189
201
|
* @param snapshot - 刚归一化出来的快照。
|
|
190
202
|
*/
|
|
191
203
|
async persist(snapshot) {
|
|
@@ -193,10 +205,24 @@ export class SourceLedger {
|
|
|
193
205
|
await this.options.store.saveSnapshot(snapshot);
|
|
194
206
|
}
|
|
195
207
|
catch (error) {
|
|
196
|
-
if (this.persistWarned)
|
|
208
|
+
if (!this.persistWarned) {
|
|
209
|
+
this.persistWarned = true;
|
|
210
|
+
this.options.logger?.warn('ds-balance: snapshot not persisted, continuing in memory', {
|
|
211
|
+
source: this.options.source,
|
|
212
|
+
error: describeError(error),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
// 落盘都没成功,谈不上修剪:这一轮不删任何东西。
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
await this.options.store.pruneByTag(snapshot.accountTag, SNAPSHOT_KEEP_N);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
if (this.retentionWarned)
|
|
197
223
|
return;
|
|
198
|
-
this.
|
|
199
|
-
this.options.logger?.warn('ds-balance: snapshot
|
|
224
|
+
this.retentionWarned = true;
|
|
225
|
+
this.options.logger?.warn('ds-balance: snapshot retention failed, ledger will keep growing', {
|
|
200
226
|
source: this.options.source,
|
|
201
227
|
error: describeError(error),
|
|
202
228
|
});
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { AccountSource } from '../ports/account.js';
|
|
9
9
|
import type { DeepSeekClient } from '../ports/deepseek-client.js';
|
|
10
|
+
import type { Logger } from '../ports/logger.js';
|
|
10
11
|
import type { SourceReader } from './balance-source.js';
|
|
11
12
|
import type { ConfigService } from './config-service.js';
|
|
12
13
|
import type { KeyResolver } from './key-resolver.js';
|
|
@@ -22,5 +23,6 @@ export declare function accountReader(options: {
|
|
|
22
23
|
/** 取当前端口实现;服务可能晚到,所以这里必须是函数而不是实例。 */
|
|
23
24
|
account: () => AccountSource | undefined;
|
|
24
25
|
salt: string;
|
|
26
|
+
logger?: Logger;
|
|
25
27
|
}): SourceReader;
|
|
26
28
|
//# sourceMappingURL=source-readers.d.ts.map
|
|
@@ -40,7 +40,7 @@ export function keyReader(options) {
|
|
|
40
40
|
}
|
|
41
41
|
/** 账号那条路:登录态 → 钱包查询 → 投影成官方余额端点同一个形状。 */
|
|
42
42
|
export function accountReader(options) {
|
|
43
|
-
const { account, salt } = options;
|
|
43
|
+
const { account, salt, logger } = options;
|
|
44
44
|
const require = () => {
|
|
45
45
|
const current = account();
|
|
46
46
|
if (current === undefined)
|
|
@@ -51,10 +51,20 @@ export function accountReader(options) {
|
|
|
51
51
|
* 账号账本的派生输入。
|
|
52
52
|
*
|
|
53
53
|
* 用 `account:` 前缀把两个键空间分开:账号 id 与明文密钥因此不可能撞到同一个账本。
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
*
|
|
55
|
+
* **拿不到账号 id 就抛错,不落盘**(兑现 `SourceReader.tag` 的契约):
|
|
56
|
+
* 宿主本进程还没读过 profile 时 id 会给不出值,而账本键一旦落成别的值,
|
|
57
|
+
* 那条快照就再也读不回来 —— 「重启后按同一个键读回自己那份快照」这条承诺会在那一刻落空。
|
|
58
|
+
* 抛错比换一个占位键诚实:`restore()` 会静默跳过,下一轮拿到真 id 后自然自愈。
|
|
56
59
|
*/
|
|
57
|
-
const tagOf = async () =>
|
|
60
|
+
const tagOf = async () => {
|
|
61
|
+
const id = await require().accountId();
|
|
62
|
+
if (id === null) {
|
|
63
|
+
logger?.debug('ds-balance: account identity unavailable, skipping this snapshot');
|
|
64
|
+
throw new NoKeyError('no credential: account identity is unavailable');
|
|
65
|
+
}
|
|
66
|
+
return computeAccountTag(salt, `account:${id}`);
|
|
67
|
+
};
|
|
58
68
|
return {
|
|
59
69
|
async available() {
|
|
60
70
|
const current = account();
|
package/lib/version.d.ts
CHANGED
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
/** 线上 schema 版本。响应里逐条回传,消费方据此判断兼容性。 */
|
|
9
9
|
export declare const SCHEMA_VERSION = 1;
|
|
10
10
|
/** 插件版本。与 `package.json` 的 `version` 一致(有测试兜底)。 */
|
|
11
|
-
export declare const PLUGIN_VERSION = "2.
|
|
11
|
+
export declare const PLUGIN_VERSION = "2.3.1";
|
|
12
12
|
//# sourceMappingURL=version.d.ts.map
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ds-balance",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"icon": "./icon.svg",
|
|
5
5
|
"description": "DSH plugin: shows the DeepSeek account balance at the bottom of the sidebar, with a configuration card on the Plugins page.",
|
|
6
6
|
"keywords": [
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"LICENSE"
|
|
48
48
|
],
|
|
49
49
|
"engines": {
|
|
50
|
-
"dsh": ">=0.2.0-rc.1",
|
|
50
|
+
"dsh": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
51
51
|
"node": ">=20"
|
|
52
52
|
},
|
|
53
53
|
"dsh": {
|
|
@@ -75,16 +75,16 @@
|
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"@deepseek-ai/cordis": "^4.0.3",
|
|
78
|
-
"@deepseek-ai/dsh-client-connection": ">=0.2.0-rc.1",
|
|
79
|
-
"@deepseek-ai/dsh-client-locale": ">=0.2.0-rc.1",
|
|
80
|
-
"@deepseek-ai/dsh-client-ui-primitives": ">=0.2.0-rc.1",
|
|
81
|
-
"@deepseek-ai/dsh-client-ui-settings": ">=0.2.0-rc.1",
|
|
82
|
-
"@deepseek-ai/dsh-client-ui-slots": ">=0.2.0-rc.1",
|
|
83
|
-
"@deepseek-ai/dsh-credentials": ">=0.2.0-rc.1",
|
|
84
|
-
"@deepseek-ai/dsh-deepseek-account-platform": ">=0.2.0-rc.1",
|
|
85
|
-
"@deepseek-ai/dsh-home-paths": ">=0.2.0-rc.1",
|
|
86
|
-
"@deepseek-ai/dsh-settings": ">=0.2.0-rc.1",
|
|
87
|
-
"@deepseek-ai/dsh-storage-domain": ">=0.2.0-rc.1",
|
|
78
|
+
"@deepseek-ai/dsh-client-connection": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
79
|
+
"@deepseek-ai/dsh-client-locale": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-primitives": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
82
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
83
|
+
"@deepseek-ai/dsh-credentials": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
84
|
+
"@deepseek-ai/dsh-deepseek-account-platform": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
85
|
+
"@deepseek-ai/dsh-home-paths": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
86
|
+
"@deepseek-ai/dsh-settings": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
87
|
+
"@deepseek-ai/dsh-storage-domain": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
88
88
|
"@deepseek-ai/schemastery": "^3.18.3",
|
|
89
89
|
"react": "^18.3.1",
|
|
90
90
|
"react-dom": "^18.3.1"
|
|
@@ -92,19 +92,19 @@
|
|
|
92
92
|
"devDependencies": {
|
|
93
93
|
"@deepseek-ai/cordis": "^4.0.3",
|
|
94
94
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.4",
|
|
95
|
-
"@deepseek-ai/dsh-client-connection": ">=0.2.0-rc.1",
|
|
96
|
-
"@deepseek-ai/dsh-client-locale": ">=0.2.0-rc.1",
|
|
97
|
-
"@deepseek-ai/dsh-client-ui-plugin-manager": ">=0.2.0-rc.1",
|
|
98
|
-
"@deepseek-ai/dsh-client-ui-primitives": ">=0.2.0-rc.1",
|
|
99
|
-
"@deepseek-ai/dsh-client-ui-renderer": ">=0.2.0-rc.1",
|
|
100
|
-
"@deepseek-ai/dsh-client-ui-settings": ">=0.2.0-rc.1",
|
|
101
|
-
"@deepseek-ai/dsh-client-ui-sidebar": ">=0.2.0-rc.1",
|
|
102
|
-
"@deepseek-ai/dsh-client-ui-slots": ">=0.2.0-rc.1",
|
|
103
|
-
"@deepseek-ai/dsh-credentials": ">=0.2.0-rc.1",
|
|
104
|
-
"@deepseek-ai/dsh-deepseek-account-platform": ">=0.2.0-rc.1",
|
|
105
|
-
"@deepseek-ai/dsh-home-paths": ">=0.2.0-rc.1",
|
|
106
|
-
"@deepseek-ai/dsh-settings": ">=0.2.0-rc.1",
|
|
107
|
-
"@deepseek-ai/dsh-storage-domain": ">=0.2.0-rc.1",
|
|
95
|
+
"@deepseek-ai/dsh-client-connection": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
96
|
+
"@deepseek-ai/dsh-client-locale": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
97
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
98
|
+
"@deepseek-ai/dsh-client-ui-primitives": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
99
|
+
"@deepseek-ai/dsh-client-ui-renderer": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
100
|
+
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
101
|
+
"@deepseek-ai/dsh-client-ui-sidebar": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
102
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
103
|
+
"@deepseek-ai/dsh-credentials": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
104
|
+
"@deepseek-ai/dsh-deepseek-account-platform": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
105
|
+
"@deepseek-ai/dsh-home-paths": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
106
|
+
"@deepseek-ai/dsh-settings": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
107
|
+
"@deepseek-ai/dsh-storage-domain": ">=0.1.7-rc.1 <0.1.8-0 || >=0.2.0-rc.1 <0.2.1-0",
|
|
108
108
|
"@deepseek-ai/schemastery": "^3.18.3",
|
|
109
109
|
"@eslint/js": "^10.0.1",
|
|
110
110
|
"@types/node": "^20.0.0",
|