@jxgame2020/dsh-token-quota 0.1.11 → 0.1.13

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.
@@ -225,6 +225,8 @@ export class TokenQuotaService extends Service {
225
225
  checkUpdates = true;
226
226
  /** Cached upgrade availability; recomputed by {@link refreshUpgrade}. */
227
227
  upgrade = null;
228
+ /** Error message from the most recent update check; null = no error. */
229
+ upgradeError = null;
228
230
  /** Timer for the periodic update check. */
229
231
  updateTimer;
230
232
  /** In-flight update check, to coalesce the periodic and manual triggers. */
@@ -243,6 +245,8 @@ export class TokenQuotaService extends Service {
243
245
  disposeRouteLog;
244
246
  /** Disposer for the optional manual-check route (`POST /token-quota/check-updates`). */
245
247
  disposeRouteCheck;
248
+ /** Disposer for the optional clear-log route (`POST /token-quota/clear-log`). */
249
+ disposeRouteClear;
246
250
  /** Disposer for the webServer-arrival watcher when the service mounts later. */
247
251
  disposeRouteWatcher;
248
252
  /**
@@ -376,6 +380,37 @@ export class TokenQuotaService extends Service {
376
380
  });
377
381
  },
378
382
  });
383
+ this.disposeRouteClear = server.register({
384
+ kind: 'exact',
385
+ path: '/token-quota/clear-log',
386
+ handler: (req, res) => {
387
+ if (req.method !== 'POST') {
388
+ res.writeHead(405);
389
+ res.end();
390
+ return;
391
+ }
392
+ const chunks = [];
393
+ req.on('data', (chunk) => { chunks.push(Buffer.from(chunk)); });
394
+ req.on('end', () => {
395
+ let before = '';
396
+ try {
397
+ const raw = Buffer.concat(chunks).toString('utf8');
398
+ if (raw.length > 0) {
399
+ const payload = JSON.parse(raw);
400
+ before = typeof payload.before === 'string' ? payload.before : '';
401
+ }
402
+ }
403
+ catch {
404
+ res.writeHead(400, { 'content-type': 'application/json' });
405
+ res.end(JSON.stringify({ error: 'bad payload' }));
406
+ return;
407
+ }
408
+ const updated = this.clearLogBefore(before);
409
+ res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' });
410
+ res.end(JSON.stringify(updated));
411
+ });
412
+ },
413
+ });
379
414
  }
380
415
  /** Query the npm registry and rebuild the cached upgrade info (coalesced). */
381
416
  refreshUpgrade() {
@@ -394,6 +429,7 @@ export class TokenQuotaService extends Service {
394
429
  const current = __TOKEN_QUOTA_VERSION__;
395
430
  if (current === undefined || current === '') {
396
431
  this.upgrade = null;
432
+ this.upgradeError = null;
397
433
  return;
398
434
  }
399
435
  let latest;
@@ -404,18 +440,26 @@ export class TokenQuotaService extends Service {
404
440
  });
405
441
  if (!response.ok) {
406
442
  this.upgrade = null;
443
+ this.upgradeError = `registry responded ${response.status} ${response.statusText}`;
407
444
  return;
408
445
  }
409
446
  const manifest = await response.json();
410
447
  latest = typeof manifest.version === 'string' ? manifest.version : undefined;
411
448
  }
412
- catch {
413
- // Offline / registry failure: keep showing nothing rather than erroring.
449
+ catch (err) {
414
450
  this.upgrade = null;
451
+ const msg = err instanceof Error ? err.message : String(err);
452
+ // Common Node fetch error shapes include the URL; surface a short reason.
453
+ this.upgradeError = msg.includes('fetch failed')
454
+ ? '无法访问 npm registry(网络超时或被墙)'
455
+ : msg.includes('aborted')
456
+ ? '请求超时(10s)'
457
+ : msg;
415
458
  return;
416
459
  }
417
460
  if (latest === undefined || !isNewer(latest, current)) {
418
461
  this.upgrade = null;
462
+ this.upgradeError = null;
419
463
  return;
420
464
  }
421
465
  const profile = findProfile();
@@ -429,6 +473,7 @@ export class TokenQuotaService extends Service {
429
473
  commands,
430
474
  installKind: profile?.installKind ?? 'registry',
431
475
  };
476
+ this.upgradeError = null;
432
477
  }
433
478
  /** Start or stop the periodic update check to match the current setting. */
434
479
  syncUpdateChecking() {
@@ -470,6 +515,25 @@ export class TokenQuotaService extends Service {
470
515
  entries.sort((left, right) => right.day.localeCompare(left.day) || left.key.localeCompare(right.key));
471
516
  return { entries };
472
517
  }
518
+ /**
519
+ * Remove all historical cycles whose day portion strictly precedes `before`
520
+ * (format `YYYY-MM-DD`). The current cycle is never cleared. Returns the
521
+ * trimmed log so the client can refresh immediately.
522
+ */
523
+ clearLogBefore(before) {
524
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(before))
525
+ return this.readLog();
526
+ const kept = {};
527
+ for (const [cycle, usageByKey] of Object.entries(this.history)) {
528
+ const dayPart = cycle.slice(0, 10);
529
+ if (dayPart >= before) {
530
+ kept[cycle] = usageByKey;
531
+ }
532
+ }
533
+ this.history = kept;
534
+ this.scheduleWrite();
535
+ return this.readLog();
536
+ }
473
537
  /** Read the current snapshot (also used by tests and inspection). */
474
538
  readSnapshot() {
475
539
  this.rollCycleIfNeeded();
@@ -488,7 +552,7 @@ export class TokenQuotaService extends Service {
488
552
  });
489
553
  }
490
554
  entries.sort((left, right) => left.key.localeCompare(right.key));
491
- return { day: this.cycle, entries, upgrade: this.upgrade };
555
+ return { day: this.cycle, entries, upgrade: this.upgrade, upgradeError: this.upgradeError };
492
556
  }
493
557
  /** Today's used tokens for one model key, or `0`. */
494
558
  usedToday(key) {
@@ -728,6 +792,8 @@ export class TokenQuotaService extends Service {
728
792
  this.disposeRouteLog();
729
793
  if (this.disposeRouteCheck !== undefined)
730
794
  this.disposeRouteCheck();
795
+ if (this.disposeRouteClear !== undefined)
796
+ this.disposeRouteClear();
731
797
  this.flush();
732
798
  }
733
799
  safeSplit(key) {
@@ -125,6 +125,8 @@ export interface TokenQuotaSnapshot {
125
125
  entries: TokenQuotaEntry[];
126
126
  /** Upgrade availability; `null` when no newer version is known. */
127
127
  upgrade: TokenQuotaUpgrade | null;
128
+ /** Error from the most recent update check; `null` = no error (or never checked). */
129
+ upgradeError: string | null;
128
130
  }
129
131
  /** One historical daily usage record, shown in the log dialog. */
130
132
  export interface TokenQuotaLogEntry {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jxgame2020/dsh-token-quota",
3
3
  "description": "Daily per-model token quota accounting, enforcement, and a floating quota panel for the DeepSeek Harness — a fully self-contained plugin (no core-Harness wiring, no generated RPC contract)",
4
- "version": "0.1.11",
4
+ "version": "0.1.13",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },