dsh-ds-balance 2.1.0 → 2.1.2
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 +5 -1
- package/README_en.md +5 -1
- package/lib/adapters/console-logger.js +12 -4
- package/lib/adapters/domain-core-store.js +9 -3
- package/lib/adapters/http-deepseek-client.js +14 -4
- package/lib/adapters/salt-file.js +6 -2
- package/lib/client.js +410 -196
- package/lib/config.js +7 -1
- package/lib/domain/errors.js +10 -5
- package/lib/domain/select.js +5 -5
- package/lib/http/handlers.js +55 -13
- package/lib/index.js +34 -9
- package/lib/services/balance-service.js +13 -5
- package/lib/services/scheduler.js +6 -2
- package/lib/types/client/locales.d.ts +1 -0
- package/lib/types/client/settings/fields.d.ts +3 -3
- package/lib/types/client/sidebar/BalancePopover.d.ts +16 -3
- package/lib/types/client/sidebar/PercentRing.d.ts +1 -1
- package/lib/types/client/sidebar/SidebarBalance.d.ts +6 -4
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +10 -1
package/lib/config.js
CHANGED
|
@@ -135,7 +135,13 @@ export const Config = z.object({
|
|
|
135
135
|
* 两边各存一份是因为**宿主与浏览器两个半体不许值导入**;约定本身由测试对着本表兜底。
|
|
136
136
|
*/
|
|
137
137
|
export const THRESHOLD_PAIRS = [
|
|
138
|
-
{
|
|
138
|
+
{
|
|
139
|
+
currency: 'CNY',
|
|
140
|
+
warn: 'cnyWarn',
|
|
141
|
+
critical: 'cnyCritical',
|
|
142
|
+
defaultWarn: 10,
|
|
143
|
+
defaultCritical: 5,
|
|
144
|
+
},
|
|
139
145
|
{ currency: 'USD', warn: 'usdWarn', critical: 'usdCritical', defaultWarn: 2, defaultCritical: 1 },
|
|
140
146
|
];
|
|
141
147
|
/**
|
package/lib/domain/errors.js
CHANGED
|
@@ -39,11 +39,16 @@ export class NetworkError extends AppError {
|
|
|
39
39
|
/** 把 HTTP 状态码映射到错误码。 */
|
|
40
40
|
export function upstreamCodeOf(status) {
|
|
41
41
|
switch (status) {
|
|
42
|
-
case 401:
|
|
43
|
-
|
|
44
|
-
case
|
|
45
|
-
|
|
46
|
-
case
|
|
42
|
+
case 401:
|
|
43
|
+
return 'UPSTREAM_401';
|
|
44
|
+
case 402:
|
|
45
|
+
return 'UPSTREAM_402';
|
|
46
|
+
case 422:
|
|
47
|
+
return 'UPSTREAM_422';
|
|
48
|
+
case 429:
|
|
49
|
+
return 'UPSTREAM_429';
|
|
50
|
+
case 503:
|
|
51
|
+
return 'UPSTREAM_503';
|
|
47
52
|
default:
|
|
48
53
|
if (status >= 500)
|
|
49
54
|
return 'UPSTREAM_5XX';
|
package/lib/domain/select.js
CHANGED
|
@@ -42,10 +42,10 @@ export function pickBalance(balances, preferred) {
|
|
|
42
42
|
if (hit !== undefined)
|
|
43
43
|
return hit;
|
|
44
44
|
}
|
|
45
|
-
return ordered.find((item) => item.currency === 'CNY' && !isZeroMoney(item.total))
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
return (ordered.find((item) => item.currency === 'CNY' && !isZeroMoney(item.total)) ??
|
|
46
|
+
ordered.find((item) => !isZeroMoney(item.total)) ??
|
|
47
|
+
ordered.find((item) => item.currency === 'CNY') ??
|
|
48
|
+
ordered[0] ??
|
|
49
|
+
null);
|
|
50
50
|
}
|
|
51
51
|
//# sourceMappingURL=select.js.map
|
package/lib/http/handlers.js
CHANGED
|
@@ -96,7 +96,10 @@ export async function handleBalance(request, deps) {
|
|
|
96
96
|
return json(toWireBalanceView(view, deps.service.accountTag8() ?? '', requestId));
|
|
97
97
|
}
|
|
98
98
|
catch (error) {
|
|
99
|
-
deps.logger?.error('ds-balance: balance handler failed', {
|
|
99
|
+
deps.logger?.error('ds-balance: balance handler failed', {
|
|
100
|
+
requestId,
|
|
101
|
+
error: describeError(error),
|
|
102
|
+
});
|
|
100
103
|
return json(failureView(requestId, error));
|
|
101
104
|
}
|
|
102
105
|
}
|
|
@@ -111,7 +114,10 @@ export async function handleRefresh(request, deps) {
|
|
|
111
114
|
return json({ requestId, schemaVersion: SCHEMA_VERSION, ...result, error: null });
|
|
112
115
|
}
|
|
113
116
|
catch (error) {
|
|
114
|
-
deps.logger?.error('ds-balance: refresh handler failed', {
|
|
117
|
+
deps.logger?.error('ds-balance: refresh handler failed', {
|
|
118
|
+
requestId,
|
|
119
|
+
error: describeError(error),
|
|
120
|
+
});
|
|
115
121
|
return json({
|
|
116
122
|
requestId,
|
|
117
123
|
schemaVersion: SCHEMA_VERSION,
|
|
@@ -145,7 +151,10 @@ async function credentialInfo(deps, ref) {
|
|
|
145
151
|
};
|
|
146
152
|
}
|
|
147
153
|
catch (error) {
|
|
148
|
-
deps.logger?.debug('ds-balance: credential describe failed', {
|
|
154
|
+
deps.logger?.debug('ds-balance: credential describe failed', {
|
|
155
|
+
ref,
|
|
156
|
+
error: describeError(error),
|
|
157
|
+
});
|
|
149
158
|
return null;
|
|
150
159
|
}
|
|
151
160
|
}
|
|
@@ -169,7 +178,10 @@ export async function handleConfigGet(_request, deps) {
|
|
|
169
178
|
return json(await configBody(requestId, deps));
|
|
170
179
|
}
|
|
171
180
|
catch (error) {
|
|
172
|
-
deps.logger?.error('ds-balance: config read handler failed', {
|
|
181
|
+
deps.logger?.error('ds-balance: config read handler failed', {
|
|
182
|
+
requestId,
|
|
183
|
+
error: describeError(error),
|
|
184
|
+
});
|
|
173
185
|
return json({ requestId, schemaVersion: SCHEMA_VERSION, error: toWireError(classify(error)) });
|
|
174
186
|
}
|
|
175
187
|
}
|
|
@@ -180,10 +192,20 @@ export async function handleConfigUpdate(request, deps) {
|
|
|
180
192
|
try {
|
|
181
193
|
const body = await readJsonObject(request);
|
|
182
194
|
if (body === null)
|
|
183
|
-
return json({
|
|
195
|
+
return json({
|
|
196
|
+
requestId,
|
|
197
|
+
error: { code: 'VALIDATION', message: 'body must be a JSON object', retryable: false },
|
|
198
|
+
}, 422);
|
|
184
199
|
const unknown = Object.keys(body).filter((key) => !CONFIG_FIELDS.some((field) => field === key));
|
|
185
200
|
if (unknown.length > 0) {
|
|
186
|
-
return json({
|
|
201
|
+
return json({
|
|
202
|
+
requestId,
|
|
203
|
+
error: {
|
|
204
|
+
code: 'VALIDATION',
|
|
205
|
+
message: `unknown config field: ${unknown.join(', ')}`,
|
|
206
|
+
retryable: false,
|
|
207
|
+
},
|
|
208
|
+
}, 422);
|
|
187
209
|
}
|
|
188
210
|
patch = body;
|
|
189
211
|
if (Object.keys(patch).length > 0) {
|
|
@@ -197,7 +219,10 @@ export async function handleConfigUpdate(request, deps) {
|
|
|
197
219
|
}
|
|
198
220
|
}
|
|
199
221
|
catch (error) {
|
|
200
|
-
deps.logger?.warn('ds-balance: config write rejected', {
|
|
222
|
+
deps.logger?.warn('ds-balance: config write rejected', {
|
|
223
|
+
requestId,
|
|
224
|
+
error: describeError(error),
|
|
225
|
+
});
|
|
201
226
|
return json({ requestId, error: toWireError(classify(error)) }, 422);
|
|
202
227
|
}
|
|
203
228
|
// 写完立刻回读,让调用方看到落盘后的真实值(掩码同上)。
|
|
@@ -205,7 +230,10 @@ export async function handleConfigUpdate(request, deps) {
|
|
|
205
230
|
return json(await configBody(requestId, deps));
|
|
206
231
|
}
|
|
207
232
|
catch (error) {
|
|
208
|
-
deps.logger?.error('ds-balance: config readback failed', {
|
|
233
|
+
deps.logger?.error('ds-balance: config readback failed', {
|
|
234
|
+
requestId,
|
|
235
|
+
error: describeError(error),
|
|
236
|
+
});
|
|
209
237
|
return json({ requestId, schemaVersion: SCHEMA_VERSION, error: toWireError(classify(error)) });
|
|
210
238
|
}
|
|
211
239
|
}
|
|
@@ -216,9 +244,13 @@ export async function handleTestConnection(request, deps) {
|
|
|
216
244
|
const body = await readJsonObject(request);
|
|
217
245
|
const config = deps.config.current();
|
|
218
246
|
const rawBaseUrl = body?.baseUrl;
|
|
219
|
-
const baseUrl = typeof rawBaseUrl === 'string' && rawBaseUrl.trim() !== ''
|
|
247
|
+
const baseUrl = typeof rawBaseUrl === 'string' && rawBaseUrl.trim() !== ''
|
|
248
|
+
? rawBaseUrl.trim()
|
|
249
|
+
: endpointOf(config);
|
|
220
250
|
const rawTimeout = Number(body?.timeoutMs);
|
|
221
|
-
const timeoutMs = Number.isFinite(rawTimeout) &&
|
|
251
|
+
const timeoutMs = Number.isFinite(rawTimeout) &&
|
|
252
|
+
rawTimeout >= TEST_TIMEOUT_RANGE.min &&
|
|
253
|
+
rawTimeout <= TEST_TIMEOUT_RANGE.max
|
|
222
254
|
? Math.trunc(rawTimeout)
|
|
223
255
|
: deps.config.timeoutMs();
|
|
224
256
|
const rawApiKey = body?.apiKey;
|
|
@@ -231,16 +263,26 @@ export async function handleTestConnection(request, deps) {
|
|
|
231
263
|
return json({ requestId, schemaVersion: SCHEMA_VERSION, ...result });
|
|
232
264
|
}
|
|
233
265
|
catch (error) {
|
|
234
|
-
deps.logger?.warn('ds-balance: test-connection handler failed', {
|
|
266
|
+
deps.logger?.warn('ds-balance: test-connection handler failed', {
|
|
267
|
+
requestId,
|
|
268
|
+
error: describeError(error),
|
|
269
|
+
});
|
|
235
270
|
const info = classify(error);
|
|
236
|
-
return json({
|
|
271
|
+
return json({
|
|
272
|
+
requestId,
|
|
273
|
+
schemaVersion: SCHEMA_VERSION,
|
|
274
|
+
ok: false,
|
|
275
|
+
latencyMs: 0,
|
|
276
|
+
code: info.code,
|
|
277
|
+
message: info.message,
|
|
278
|
+
});
|
|
237
279
|
}
|
|
238
280
|
}
|
|
239
281
|
/** `GET /api/v1/healthz`。 */
|
|
240
282
|
export async function handleHealthz(_request, deps) {
|
|
241
283
|
const requestId = newRequestId();
|
|
242
284
|
const status = deps.service.status();
|
|
243
|
-
let store
|
|
285
|
+
let store;
|
|
244
286
|
try {
|
|
245
287
|
const health = await deps.store.health();
|
|
246
288
|
store = { ok: health.ok, detail: health.detail ?? null };
|
package/lib/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { randomBytes } from 'node:crypto';
|
|
|
11
11
|
import { credentialRef } from '@deepseek-ai/dsh-credentials';
|
|
12
12
|
import { dshHomePath } from '@deepseek-ai/dsh-home-paths';
|
|
13
13
|
import { ENTRY_ID, THRESHOLD_PAIRS, readConfig, resolveThresholdPairs, } from './config.js';
|
|
14
|
-
import { DomainCoreStore } from './adapters/domain-core-store.js';
|
|
14
|
+
import { DomainCoreStore, } from './adapters/domain-core-store.js';
|
|
15
15
|
import { HttpDeepSeekClient } from './adapters/http-deepseek-client.js';
|
|
16
16
|
import { SALT_BYTES, SALT_ENCODING, loadOrCreateSalt } from './adapters/salt-file.js';
|
|
17
17
|
import { createConsoleLogger } from './adapters/console-logger.js';
|
|
@@ -65,7 +65,9 @@ async function resolveSalt(logger) {
|
|
|
65
65
|
return await loadOrCreateSalt({ path: dshHomePath(SALT_FILE_NAME) });
|
|
66
66
|
}
|
|
67
67
|
catch (error) {
|
|
68
|
-
logger.error('ds-balance: salt file unavailable, using an ephemeral salt', {
|
|
68
|
+
logger.error('ds-balance: salt file unavailable, using an ephemeral salt', {
|
|
69
|
+
error: describeError(error),
|
|
70
|
+
});
|
|
69
71
|
return randomBytes(SALT_BYTES).toString(SALT_ENCODING);
|
|
70
72
|
}
|
|
71
73
|
}
|
|
@@ -97,7 +99,9 @@ function createConfigSource(ctx, refs, logger) {
|
|
|
97
99
|
if (warned.has(pair.currency))
|
|
98
100
|
continue;
|
|
99
101
|
warned.add(pair.currency);
|
|
100
|
-
logger.warn('ds-balance: ' +
|
|
102
|
+
logger.warn('ds-balance: ' +
|
|
103
|
+
pair.currency +
|
|
104
|
+
' critical must stay below warn; this pair fell back to its defaults', {
|
|
101
105
|
warn: pair.defaultWarn,
|
|
102
106
|
critical: pair.defaultCritical,
|
|
103
107
|
});
|
|
@@ -143,7 +147,9 @@ function credentialsPort(ctx) {
|
|
|
143
147
|
async resolve(ref) {
|
|
144
148
|
const resolved = await service.resolve(credentialRef(ref));
|
|
145
149
|
// 未配置时官方回 `undefined`;端口的约定是「值可能为空串」,交给上游判空。
|
|
146
|
-
return resolved === undefined
|
|
150
|
+
return resolved === undefined
|
|
151
|
+
? { value: '', source: 'unset' }
|
|
152
|
+
: { value: resolved.value, source: resolved.source };
|
|
147
153
|
},
|
|
148
154
|
describe: (ref) => service.describe(credentialRef(ref)),
|
|
149
155
|
};
|
|
@@ -166,14 +172,16 @@ function connectStorage(ctx) {
|
|
|
166
172
|
ctx.inject(['storageDomain'], (storageCtx) => {
|
|
167
173
|
storageCtx.effect(() => {
|
|
168
174
|
handle.facility = storageCtx.storageDomain;
|
|
169
|
-
return () => {
|
|
175
|
+
return () => {
|
|
176
|
+
handle.facility = undefined;
|
|
177
|
+
};
|
|
170
178
|
}, 'ds-balance: storage facility');
|
|
171
179
|
});
|
|
172
180
|
return async (spec) => {
|
|
173
181
|
const facility = handle.facility;
|
|
174
182
|
if (facility === undefined)
|
|
175
183
|
throw new Error('storageDomain service is unavailable');
|
|
176
|
-
return await facility.open(spec);
|
|
184
|
+
return (await facility.open(spec));
|
|
177
185
|
};
|
|
178
186
|
}
|
|
179
187
|
/**
|
|
@@ -207,7 +215,14 @@ export async function apply(ctx, refs) {
|
|
|
207
215
|
// 由 healthz 的 metrics 段暴露(见 .agents/notes 的决策)。
|
|
208
216
|
const metrics = new MemoryMetrics();
|
|
209
217
|
const service = new BalanceService({
|
|
210
|
-
client,
|
|
218
|
+
client,
|
|
219
|
+
store,
|
|
220
|
+
keys,
|
|
221
|
+
config: configService,
|
|
222
|
+
clock: systemClock,
|
|
223
|
+
salt,
|
|
224
|
+
logger,
|
|
225
|
+
metrics,
|
|
211
226
|
});
|
|
212
227
|
const scheduler = new Scheduler({ target: service, logger });
|
|
213
228
|
ctx.effect(() => {
|
|
@@ -235,9 +250,19 @@ export async function apply(ctx, refs) {
|
|
|
235
250
|
ctx.inject(['connection'], (connectionCtx) => {
|
|
236
251
|
connectionCtx.effect(() => {
|
|
237
252
|
const disposeRoutes = registerHttpRoutes(connectionCtx, {
|
|
238
|
-
service,
|
|
253
|
+
service,
|
|
254
|
+
config: configService,
|
|
255
|
+
keys,
|
|
256
|
+
client,
|
|
257
|
+
store,
|
|
258
|
+
scheduler,
|
|
259
|
+
logger,
|
|
260
|
+
metrics,
|
|
261
|
+
credentials,
|
|
239
262
|
});
|
|
240
|
-
return () => {
|
|
263
|
+
return () => {
|
|
264
|
+
void disposeRoutes();
|
|
265
|
+
};
|
|
241
266
|
}, 'ds-balance: http routes');
|
|
242
267
|
});
|
|
243
268
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* **对外永不抛错**:一切失败都变成 `view.state` 与 `view.error`。
|
|
6
6
|
* @module dsh-ds-balance/services/balance-service
|
|
7
7
|
*/
|
|
8
|
-
import { classify, describeError, parseRetryAfter } from '../domain/errors.js';
|
|
8
|
+
import { classify, describeError, parseRetryAfter, } from '../domain/errors.js';
|
|
9
9
|
import { normalize } from '../domain/normalize.js';
|
|
10
10
|
import { pickBalance } from '../domain/select.js';
|
|
11
11
|
import { severityOf, thresholdsFor } from '../domain/severity.js';
|
|
@@ -67,7 +67,9 @@ export class BalanceService {
|
|
|
67
67
|
this.publishGauge();
|
|
68
68
|
}
|
|
69
69
|
catch (error) {
|
|
70
|
-
this.options.logger?.debug('ds-balance: no snapshot restored', {
|
|
70
|
+
this.options.logger?.debug('ds-balance: no snapshot restored', {
|
|
71
|
+
error: describeError(error),
|
|
72
|
+
});
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
/**
|
|
@@ -80,7 +82,9 @@ export class BalanceService {
|
|
|
80
82
|
return this.inflight;
|
|
81
83
|
if (options.force !== true && this.canServeCache())
|
|
82
84
|
return this.toView(options.currency);
|
|
83
|
-
const run = this.fetchOnce(options.currency).finally(() => {
|
|
85
|
+
const run = this.fetchOnce(options.currency).finally(() => {
|
|
86
|
+
this.inflight = null;
|
|
87
|
+
});
|
|
84
88
|
this.inflight = run;
|
|
85
89
|
return run;
|
|
86
90
|
}
|
|
@@ -165,7 +169,9 @@ export class BalanceService {
|
|
|
165
169
|
if (this.persistWarned)
|
|
166
170
|
return;
|
|
167
171
|
this.persistWarned = true;
|
|
168
|
-
this.options.logger?.warn('ds-balance: snapshot not persisted, continuing in memory', {
|
|
172
|
+
this.options.logger?.warn('ds-balance: snapshot not persisted, continuing in memory', {
|
|
173
|
+
error: describeError(error),
|
|
174
|
+
});
|
|
169
175
|
}
|
|
170
176
|
}
|
|
171
177
|
/** 记录一次失败,并把状态推到 `stale` 或 `error`。 */
|
|
@@ -197,7 +203,9 @@ export class BalanceService {
|
|
|
197
203
|
state: this.state,
|
|
198
204
|
stale: this.state === 'stale',
|
|
199
205
|
fetchedAt: this.snapshot?.fetchedAt ?? null,
|
|
200
|
-
ageMs: this.snapshot === null
|
|
206
|
+
ageMs: this.snapshot === null
|
|
207
|
+
? null
|
|
208
|
+
: Math.max(0, this.options.clock.now() - this.snapshot.fetchedAt),
|
|
201
209
|
isAvailable: this.snapshot?.isAvailable ?? null,
|
|
202
210
|
balances: this.snapshot?.balances ?? [],
|
|
203
211
|
selected: selected === null ? null : { currency: selected.currency, total: selected.total },
|
|
@@ -60,7 +60,9 @@ export function nextDelayMs(status, options = {}) {
|
|
|
60
60
|
/** 真正的默认定时器。 */
|
|
61
61
|
const defaultTimers = {
|
|
62
62
|
set: (callback, ms) => setTimeout(callback, ms),
|
|
63
|
-
clear: (handle) => {
|
|
63
|
+
clear: (handle) => {
|
|
64
|
+
clearTimeout(handle);
|
|
65
|
+
},
|
|
64
66
|
};
|
|
65
67
|
/**
|
|
66
68
|
* 刷新调度器。
|
|
@@ -118,7 +120,9 @@ export class Scheduler {
|
|
|
118
120
|
if (!this.running)
|
|
119
121
|
return;
|
|
120
122
|
this.nextAt = this.now() + delay;
|
|
121
|
-
this.handle = this.timers.set(() => {
|
|
123
|
+
this.handle = this.timers.set(() => {
|
|
124
|
+
void this.tick();
|
|
125
|
+
}, delay);
|
|
122
126
|
}
|
|
123
127
|
/** 跑一轮并按状态排下一轮。 */
|
|
124
128
|
async tick() {
|
|
@@ -22,6 +22,7 @@ export declare const zh: {
|
|
|
22
22
|
readonly 'popover.mismatch': "未找到 {wanted} 余额,当前显示 {shown}";
|
|
23
23
|
readonly 'popover.action.useShown': "改用 {shown}";
|
|
24
24
|
readonly 'popover.action.openPlugins': "打开插件页";
|
|
25
|
+
readonly 'popover.action.openPluginConfig': "打开插件配置页";
|
|
25
26
|
readonly 'state.unavailable': "账户不可用";
|
|
26
27
|
readonly 'state.stale': "数据已过期";
|
|
27
28
|
readonly 'state.error': "读取失败";
|
|
@@ -123,10 +123,10 @@ export interface DetailsGroupProps {
|
|
|
123
123
|
children: ReactNode;
|
|
124
124
|
}
|
|
125
125
|
/**
|
|
126
|
-
*
|
|
126
|
+
* 渲染二级折叠「自定义设置」。
|
|
127
127
|
*
|
|
128
|
-
* **受控但跟手**:`open` 由 state
|
|
129
|
-
*
|
|
128
|
+
* **受控但跟手**:`open` 由 state 持有,官方 DisclosureRow 的展开体只在 open 时条件渲染,
|
|
129
|
+
* 卡片每次重渲染(每敲一个字都会)不会把用户展开的块弹回去。
|
|
130
130
|
* @param props - 标题、初始状态与内容。
|
|
131
131
|
* @returns 折叠块元素。
|
|
132
132
|
*/
|
|
@@ -46,10 +46,23 @@ export interface BalancePopoverProps {
|
|
|
46
46
|
*/
|
|
47
47
|
useShownDisabled?: boolean;
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
49
|
+
* 右上角图标:点下去做什么、落到哪里。宿主没提供 layout 服务时给 undefined ——
|
|
50
|
+
* 这时那个按钮**不渲染**:不留按不动的死按钮。
|
|
51
51
|
*/
|
|
52
|
-
|
|
52
|
+
pluginsAction?: PluginsAction;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 浮层右上角图标点下去会发生什么。
|
|
56
|
+
*
|
|
57
|
+
* 落点由**宿主能力**决定:宿主提供跨插件深链服务时直达本插件的配置格,
|
|
58
|
+
* 服务缺席时只能到 Plugins 列表。所以措辞跟着落点分档 ——
|
|
59
|
+
* 「打开插件配置页」与「打开插件页」各说自己那一件事,不许用一句含糊的话盖住两种落点。
|
|
60
|
+
*/
|
|
61
|
+
export interface PluginsAction {
|
|
62
|
+
/** 执行跳转;「先关浮层」由父组件包在里面。 */
|
|
63
|
+
open: () => void;
|
|
64
|
+
/** 落点是不是本插件的配置格。 */
|
|
65
|
+
reachesConfig: boolean;
|
|
53
66
|
}
|
|
54
67
|
/**
|
|
55
68
|
* 渲染余额浮层。
|
|
@@ -38,4 +38,4 @@ export interface PercentRingProps {
|
|
|
38
38
|
* @param props - 状态、边长与可选提示。
|
|
39
39
|
* @returns 圆环 svg;自身 aria-hidden,语义名由调用方的 aria-label 提供。
|
|
40
40
|
*/
|
|
41
|
-
export declare function PercentRing({ state, marker, ratio, size, title }: PercentRingProps): JSX.Element;
|
|
41
|
+
export declare function PercentRing({ state, marker, ratio, size, title, }: PercentRingProps): JSX.Element;
|
|
@@ -13,14 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { type LocaleKey } from '../locales.ts';
|
|
15
15
|
import { type ConfigSlotProbe } from '../config-slot.ts';
|
|
16
|
+
import { type PluginsAction } from './BalancePopover.tsx';
|
|
16
17
|
/**
|
|
17
|
-
*
|
|
18
|
+
* 「打开本插件配置」这个**可选**入口。宿主没提供 layout 服务时整条链不存在,
|
|
18
19
|
* 浮层右上角的图标因此不渲染 —— 不留按不动的死按钮。
|
|
19
20
|
* 形态与 `configSlotProbe` 一样是个可订阅的小对象:服务可能晚到,图标要能自己出现、也能自己消失。
|
|
21
|
+
* 快照里带**落点**(见 BalancePopover 的 PluginsAction):深链服务在不在,措辞跟着分档。
|
|
20
22
|
*/
|
|
21
23
|
export interface PluginsNavigation {
|
|
22
|
-
/**
|
|
23
|
-
getSnapshot: () =>
|
|
24
|
+
/** 当前可用的动作与落点;宿主没有 layout 服务时是 undefined。 */
|
|
25
|
+
getSnapshot: () => PluginsAction | undefined;
|
|
24
26
|
/**
|
|
25
27
|
* 订阅它的出现与消失。
|
|
26
28
|
* @param listener - 变化回调。
|
|
@@ -41,7 +43,7 @@ export interface SidebarBalanceProps {
|
|
|
41
43
|
*/
|
|
42
44
|
configSlotProbe: ConfigSlotProbe;
|
|
43
45
|
/**
|
|
44
|
-
*
|
|
46
|
+
* 「打开本插件配置」的可选入口:有它才渲染浮层右上角那个图标按钮。
|
|
45
47
|
* 宿主缺 layout 服务时整条链不存在,图标就不出现。
|
|
46
48
|
*/
|
|
47
49
|
pluginsNavigation?: PluginsNavigation;
|
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.1.
|
|
11
|
+
export declare const PLUGIN_VERSION = "2.1.2";
|
|
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.1.
|
|
3
|
+
"version": "2.1.2",
|
|
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": [
|
|
@@ -67,6 +67,9 @@
|
|
|
67
67
|
"test": "npm run build && vitest run",
|
|
68
68
|
"test:watch": "vitest",
|
|
69
69
|
"test:contract": "vitest run --config vitest.contract.config.ts",
|
|
70
|
+
"lint": "eslint .",
|
|
71
|
+
"format": "prettier --write .",
|
|
72
|
+
"format:check": "prettier --check .",
|
|
70
73
|
"check:release": "node scripts/check-release.mjs",
|
|
71
74
|
"check:declaration": "node scripts/check-declaration.mjs"
|
|
72
75
|
},
|
|
@@ -101,14 +104,20 @@
|
|
|
101
104
|
"@deepseek-ai/dsh-settings": ">=0.1.7-alpha.1",
|
|
102
105
|
"@deepseek-ai/dsh-storage-domain": ">=0.1.7-alpha.1",
|
|
103
106
|
"@deepseek-ai/schemastery": "^3.18.3",
|
|
107
|
+
"@eslint/js": "^10.0.1",
|
|
104
108
|
"@types/node": "^20.0.0",
|
|
105
109
|
"@types/react": "~18.3.1",
|
|
106
110
|
"@types/react-dom": "~18.3.1",
|
|
107
111
|
"clsx": "^2.1.1",
|
|
108
112
|
"esbuild": "^0.21.5",
|
|
113
|
+
"eslint": "^10.11.0",
|
|
114
|
+
"eslint-config-prettier": "^10.1.8",
|
|
115
|
+
"globals": "^17.12.0",
|
|
116
|
+
"prettier": "^3.9.9",
|
|
109
117
|
"react": "^18.3.1",
|
|
110
118
|
"react-dom": "^18.3.1",
|
|
111
119
|
"typescript": "^5.4.0",
|
|
120
|
+
"typescript-eslint": "^8.70.1",
|
|
112
121
|
"vitest": "^2.0.0"
|
|
113
122
|
},
|
|
114
123
|
"dependencies": {
|