@goodandready/dsh-usage-guard 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +28 -62
- package/lib/index.js +6 -1
- package/lib/patch.js +7 -4
- package/lib/usage.js +4 -4
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -136,7 +136,7 @@ window.__ModuleLoader__.load({
|
|
|
136
136
|
const badgeClass = isRepairActive ? 'ug-badge ug-badge-green' : 'ug-badge ug-badge-yellow';
|
|
137
137
|
|
|
138
138
|
return React.createElement('div', { className: 'ug-card' },
|
|
139
|
-
React.createElement('style',
|
|
139
|
+
React.createElement('style', {'data-dsh-plugin': 'dsh-usage-guard'}, `
|
|
140
140
|
.ug-card { border:1px solid var(--dsw-alias-border-l2); background:var(--dsw-alias-bg-layer-3); border-radius:12px; list-style:none }
|
|
141
141
|
.ug-head { appearance:none; width:100%; font:inherit; color:inherit; text-align:left; cursor:pointer; background:0 0; border:0; border-radius:12px; display:flex; align-items:center; gap:12px; padding:14px 16px }
|
|
142
142
|
.ug-title { color:var(--dsw-alias-label-primary); font-size:15px; font-weight:600; line-height:1.4 }
|
|
@@ -226,80 +226,46 @@ window.__ModuleLoader__.load({
|
|
|
226
226
|
report: 'Log diagnostic warnings',
|
|
227
227
|
reportDesc: 'Log a line naming the turn, step, and raw sample whenever damaged usage arrives.',
|
|
228
228
|
},
|
|
229
|
-
ru: {
|
|
230
|
-
title: 'Защита истории токенов',
|
|
231
|
-
subtitle: 'Санитайзер расхода токенов и защита от сбоев истории',
|
|
232
|
-
active: 'АКТИВЕН',
|
|
233
|
-
reportOnly: 'ТОЛЬКО ЛОГ',
|
|
234
|
-
saved: 'Сохранено',
|
|
235
|
-
loading: 'Загрузка…',
|
|
236
|
-
unavailable: 'Пространство настроек недоступно',
|
|
237
|
-
save: 'Сохранить',
|
|
238
|
-
saving: 'Сохранение…',
|
|
239
|
-
repair: 'Автоматическое исправление расхода',
|
|
240
|
-
repairDesc: 'Подставлять ноль вместо повреждённых счётчиков до сложения для предотвращения блокировки истории диалогов.',
|
|
241
|
-
report: 'Диагностические предупреждения',
|
|
242
|
-
reportDesc: 'Логировать номер хода, шага и исходные данные при обнаружении битой порции расхода.',
|
|
243
|
-
},
|
|
244
|
-
zh: {
|
|
245
|
-
title: '用量与会话防崩守卫',
|
|
246
|
-
subtitle: 'Token 用量数据清洗与历史记录防白屏保护',
|
|
247
|
-
active: '已激活',
|
|
248
|
-
reportOnly: '仅记录',
|
|
249
|
-
saved: '已保存',
|
|
250
|
-
loading: '加载中…',
|
|
251
|
-
unavailable: '设置作用域不可用',
|
|
252
|
-
save: '保存',
|
|
253
|
-
saving: '保存中…',
|
|
254
|
-
repair: '自动修复用量计数',
|
|
255
|
-
repairDesc: '在累加前将缺失或非数字计数置为 0,彻底防止 NaN 导致会话历史永久白屏。',
|
|
256
|
-
report: '记录诊断告警',
|
|
257
|
-
reportDesc: '当检测到异常用量数据包时,在控制台记录轮次、步骤和原始数据。',
|
|
258
|
-
},
|
|
259
229
|
});
|
|
260
230
|
} catch (_) {}
|
|
261
231
|
|
|
262
|
-
|
|
232
|
+
// Try to register in the settings.plugin.item slot (DSH Settings → Plugins tab).
|
|
233
|
+
// The slot key must match the settings namespace (NS) for the tab to find our card.
|
|
234
|
+
// If the slot is not available (older DSH build), fall back to a top-level settings.section.
|
|
235
|
+
let primaryRegistered = false;
|
|
263
236
|
try {
|
|
264
|
-
if (ctx.slots && typeof ctx.slots.
|
|
265
|
-
ctx.slots.
|
|
266
|
-
|
|
237
|
+
if (ctx.slots && typeof ctx.slots.register === 'function') {
|
|
238
|
+
ctx.slots.register(
|
|
239
|
+
{
|
|
240
|
+
name: 'settings.plugin.item',
|
|
241
|
+
key: NS,
|
|
242
|
+
locale: NS,
|
|
243
|
+
inject: () => ({ ctx }),
|
|
244
|
+
},
|
|
245
|
+
PluginCard,
|
|
246
|
+
);
|
|
247
|
+
primaryRegistered = true;
|
|
248
|
+
}
|
|
249
|
+
} catch (_) {
|
|
250
|
+
// slot not available in this build — fall through to section fallback
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!primaryRegistered) {
|
|
254
|
+
try {
|
|
255
|
+
if (ctx.slots && typeof ctx.slots.register === 'function') {
|
|
267
256
|
ctx.slots.register(
|
|
268
257
|
{
|
|
269
|
-
name: 'settings.
|
|
270
|
-
|
|
258
|
+
name: 'settings.section',
|
|
259
|
+
id: '@goodandready/dsh-usage-guard',
|
|
260
|
+
order: 50,
|
|
271
261
|
locale: NS,
|
|
262
|
+
label: () => (ctx.locale && ctx.locale.bind ? ctx.locale.bind(NS)('title') : 'Usage Guard'),
|
|
272
263
|
inject: () => ({ ctx }),
|
|
273
264
|
},
|
|
274
265
|
PluginCard,
|
|
275
266
|
);
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
} catch (_) {}
|
|
279
|
-
|
|
280
|
-
const timer = setTimeout(() => {
|
|
281
|
-
if (placed) return;
|
|
282
|
-
try {
|
|
283
|
-
if (ctx.slots && typeof ctx.slots.inject === 'function') {
|
|
284
|
-
ctx.slots.inject('settings.section', () => {
|
|
285
|
-
ctx.slots.register(
|
|
286
|
-
{
|
|
287
|
-
name: 'settings.section',
|
|
288
|
-
id: '@goodandready/dsh-usage-guard',
|
|
289
|
-
order: 50,
|
|
290
|
-
locale: NS,
|
|
291
|
-
label: () => (ctx.locale && ctx.locale.bind ? ctx.locale.bind(NS)('title') : 'Usage Guard'),
|
|
292
|
-
inject: () => ({ ctx }),
|
|
293
|
-
},
|
|
294
|
-
PluginCard,
|
|
295
|
-
);
|
|
296
|
-
});
|
|
297
267
|
}
|
|
298
268
|
} catch (_) {}
|
|
299
|
-
}, 3000);
|
|
300
|
-
|
|
301
|
-
if (typeof ctx.effect === 'function') {
|
|
302
|
-
ctx.effect(() => () => clearTimeout(timer), 'dsh-usage-guard: fallback section');
|
|
303
269
|
}
|
|
304
270
|
};
|
|
305
271
|
|
package/lib/index.js
CHANGED
|
@@ -48,7 +48,12 @@ export const Config = z.object({
|
|
|
48
48
|
})
|
|
49
49
|
|
|
50
50
|
export function apply(ctx, config) {
|
|
51
|
-
let live
|
|
51
|
+
let live
|
|
52
|
+
try {
|
|
53
|
+
live = Config(structuredClone(config ?? {})) ?? config
|
|
54
|
+
} catch (_) {
|
|
55
|
+
live = Config({}) ?? {}
|
|
56
|
+
}
|
|
52
57
|
|
|
53
58
|
ctx.inject(['settings'], (sctx) => {
|
|
54
59
|
const scope = sctx.settings.register(name, Config, { base: config })
|
package/lib/patch.js
CHANGED
|
@@ -58,16 +58,19 @@ export function patchRegistry(registry, guard) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// Заведутся позже — поймаем на укладке в карту.
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
// Save the current map.set (may be the prototype method or a previous patched version).
|
|
62
|
+
// We restore via explicit assignment — relying on `delete` to restore the prototype method
|
|
63
|
+
// is fragile when the environment mutates Map.prototype.
|
|
64
|
+
const originalSet = map.set
|
|
65
|
+
if (!originalSet[PATCHED]) {
|
|
63
66
|
const patched = function (key, value) {
|
|
64
67
|
if (value && value.def) undo.push(wrapApply(value.def, guard))
|
|
65
|
-
return
|
|
68
|
+
return originalSet.call(this, key, value)
|
|
66
69
|
}
|
|
67
70
|
patched[PATCHED] = true
|
|
68
71
|
map.set = patched
|
|
69
72
|
undo.push(() => {
|
|
70
|
-
if (map.set === patched)
|
|
73
|
+
if (map.set === patched) map.set = originalSet
|
|
71
74
|
})
|
|
72
75
|
}
|
|
73
76
|
|
package/lib/usage.js
CHANGED
|
@@ -194,16 +194,16 @@ function safeStringify(value) {
|
|
|
194
194
|
try {
|
|
195
195
|
return String(value)
|
|
196
196
|
} catch {
|
|
197
|
-
return '[
|
|
197
|
+
return '[not serializable]'
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
/** Короткая строка для журнала: по ней видно, кого звать к ответу. */
|
|
203
203
|
export function complaint(found, bad) {
|
|
204
|
-
const where =
|
|
204
|
+
const where = `turn ${String(found.turn)}, step ${String(found.step)}`
|
|
205
205
|
const taken = bad
|
|
206
|
-
.map((field) => (found.usage && borrowed(found.usage, field) !== undefined ? `${field}
|
|
206
|
+
.map((field) => (found.usage && borrowed(found.usage, field) !== undefined ? `${field} recovered via alias` : `${field} zeroed`))
|
|
207
207
|
.join('; ')
|
|
208
|
-
return
|
|
208
|
+
return `malformed usage sample — ${where}; received: ${safeStringify(found.usage)}; ${taken}`
|
|
209
209
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-usage-guard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Keeps a malformed token-usage sample from taking a whole session history down with it: a missing or non-numeric counter becomes zero before the harness adds it up, so the fold never yields NaN and already-broken sessions open again.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -54,4 +54,4 @@
|
|
|
54
54
|
"scripts": {
|
|
55
55
|
"test": "node --test test/*.test.mjs"
|
|
56
56
|
}
|
|
57
|
-
}
|
|
57
|
+
}
|