@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 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', null, `
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
- let placed = false;
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.inject === 'function') {
265
- ctx.slots.inject('settings.plugin.item', () => {
266
- placed = true;
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.plugin.item',
270
- key: NS,
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 = Config(structuredClone(config ?? {})) ?? config
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
- const set = map.set
62
- if (!set[PATCHED]) {
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 set.call(this, key, value)
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) delete map.set
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 = `ход ${String(found.turn)}, шаг ${String(found.step)}`
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} взят по синониму` : `${field} обнулён`))
206
+ .map((field) => (found.usage && borrowed(found.usage, field) !== undefined ? `${field} recovered via alias` : `${field} zeroed`))
207
207
  .join('; ')
208
- return `порция расхода не по форме — ${where}; пришло: ${safeStringify(found.usage)}; ${taken}`
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",
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
+ }