@linxin666/dsh-usage 0.4.4 → 0.4.6
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.i18n.yaml +2 -2
- package/README.md +21 -5
- package/README.zh.md +21 -5
- package/lib/client.js +501 -47
- package/lib/client.js.map +1 -1
- package/lib/index.js +364 -16
- package/lib/types/client/UsageSectionCard.d.ts +7 -0
- package/lib/types/client/locales.d.ts +8 -0
- package/lib/types/client/voucher.d.ts +27 -9
- package/lib/types/core/adapters.d.ts +28 -2
- package/lib/types/core/holidays.d.ts +25 -0
- package/lib/types/core/ledger.d.ts +8 -0
- package/lib/types/core/pricing.d.ts +34 -6
- package/lib/types/core/types.d.ts +26 -5
- package/lib/types/host/routes.d.ts +11 -0
- package/lib/types/host/usage-service.d.ts +9 -1
- package/package.json +15 -15
- package/src/client/UsageSectionCard.tsx +177 -29
- package/src/client/index.ts +10 -2
- package/src/client/locales.ts +20 -4
- package/src/client/usage.module.css +47 -0
- package/src/client/voucher.ts +33 -11
- package/src/core/adapters.ts +34 -3
- package/src/core/holidays.ts +248 -0
- package/src/core/ledger.ts +14 -5
- package/src/core/pricing.ts +75 -15
- package/src/core/types.ts +28 -1
- package/src/host/routes.ts +31 -0
- package/src/host/usage-service.ts +16 -2
- package/src/index.ts +3 -3
package/src/host/routes.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { Context } from '@deepseek-ai/cordis'
|
|
|
3
3
|
import type { WebRoute } from '@deepseek-ai/dsh-host-webserver'
|
|
4
4
|
import { isUsageAllowed } from './access.ts'
|
|
5
5
|
import { writeJson } from './http.ts'
|
|
6
|
+
import { isLedgerDateKey } from '../core/ledger.ts'
|
|
7
|
+
import type { UsageDayResponse } from '../core/types.ts'
|
|
6
8
|
import type { UsageService } from './usage-service.ts'
|
|
7
9
|
|
|
8
10
|
export const USAGE_API_PREFIX = '/api/dsh-usage'
|
|
@@ -31,6 +33,35 @@ export function makeUsageOverviewRoute(ctx: Context, service: UsageService): Web
|
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
/**
|
|
37
|
+
* One retained day route: `GET /api/dsh-usage/day?date=YYYY-MM-DD` answers
|
|
38
|
+
* that local day's per-provider and per-model totals, so the section's day
|
|
39
|
+
* pickers can show a past day instead of only today. Same trust fence as the
|
|
40
|
+
* overview route, and a key that is not a real local date is refused with 400
|
|
41
|
+
* rather than folded into a bogus day.
|
|
42
|
+
* @param ctx - host context; may expose remoteWebUiPairing.
|
|
43
|
+
* @param service - the usage service.
|
|
44
|
+
* @returns the route.
|
|
45
|
+
*/
|
|
46
|
+
export function makeUsageDayRoute(ctx: Context, service: UsageService): WebRoute {
|
|
47
|
+
return {
|
|
48
|
+
kind: 'exact',
|
|
49
|
+
path: USAGE_API_PREFIX + '/day',
|
|
50
|
+
handler: (req: IncomingMessage, res: ServerResponse): void => {
|
|
51
|
+
if (!isUsageAllowed(ctx, req)) {
|
|
52
|
+
writeJson(res, 403, { ok: false, error: 'forbidden: loopback-only' })
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
const date = new URL(req.url ?? '/', 'http://127.0.0.1').searchParams.get('date') ?? ''
|
|
56
|
+
if (!isLedgerDateKey(date)) {
|
|
57
|
+
writeJson(res, 400, { ok: false, error: 'date must be YYYY-MM-DD' })
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
writeJson(res, 200, { ok: true, day: service.day(date) } satisfies UsageDayResponse, { 'cache-control': 'no-store' })
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
34
65
|
/**
|
|
35
66
|
* Manual refresh: forces one probe cycle now and answers with the fresh
|
|
36
67
|
* overview. Same trust fence as the overview route.
|
|
@@ -20,7 +20,7 @@ import type { BalanceParse, PlanParse } from '../core/adapters.ts'
|
|
|
20
20
|
import { foldAliasRoutes } from '../core/provider-routes.ts'
|
|
21
21
|
import { deepseekModelSpend } from '../core/pricing.ts'
|
|
22
22
|
import { createLedgerDocument, deserializeLedger, foldUsage, ledgerDayKeys, localDateKey, pruneLedger, summarizeDays, totalTokens } from '../core/ledger.ts'
|
|
23
|
-
import type { BalanceView, CredentialKind, ObservedSpendView, PlanView, ProviderSnapshotState, ProviderSnapshotView, UsageLedgerDocument, UsageOverviewView, UsageTokenTotals } from '../core/types.ts'
|
|
23
|
+
import type { BalanceView, CredentialKind, ObservedSpendView, PlanView, ProviderSnapshotState, ProviderSnapshotView, UsageDayView, UsageLedgerDocument, UsageOverviewView, UsageTokenTotals } from '../core/types.ts'
|
|
24
24
|
import { addTotals, emptyTotals } from '../core/types.ts'
|
|
25
25
|
|
|
26
26
|
/**
|
|
@@ -350,6 +350,9 @@ export class UsageService {
|
|
|
350
350
|
totals: range.totals,
|
|
351
351
|
providers: range.providers,
|
|
352
352
|
},
|
|
353
|
+
// Every retained day, not just the trend window: the day picker's
|
|
354
|
+
// option list reaches back to the oldest day retention still keeps.
|
|
355
|
+
availableDays: allKeys,
|
|
353
356
|
all: {
|
|
354
357
|
from: allKeys[0] ?? todayKey,
|
|
355
358
|
to: allKeys[allKeys.length - 1] ?? todayKey,
|
|
@@ -393,8 +396,19 @@ export class UsageService {
|
|
|
393
396
|
}
|
|
394
397
|
}
|
|
395
398
|
|
|
399
|
+
/**
|
|
400
|
+
* One retained local day aggregated per provider and model, as the day
|
|
401
|
+
* route serves it. A day the ledger never held — pruned by retention, or
|
|
402
|
+
* simply without usage — reads as a zeroed day rather than an error, so a
|
|
403
|
+
* picker selection that aged out degrades to "no usage" instead of a broken
|
|
404
|
+
* panel. The route gates the key's shape before it gets here.
|
|
405
|
+
*/
|
|
406
|
+
day(dateKey: string): UsageDayView {
|
|
407
|
+
return this.daySummary(dateKey)
|
|
408
|
+
}
|
|
409
|
+
|
|
396
410
|
/** One local day aggregated per provider. */
|
|
397
|
-
private daySummary(dateKey: string):
|
|
411
|
+
private daySummary(dateKey: string): UsageDayView {
|
|
398
412
|
const day = this.ledger.days[dateKey] ?? {}
|
|
399
413
|
const { totals, providers } = summarizeDays([day])
|
|
400
414
|
return { date: dateKey, totals, providers }
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { SettingsNamespace } from '@deepseek-ai/dsh-settings'
|
|
|
4
4
|
import z from '@deepseek-ai/schemastery'
|
|
5
5
|
import { mountOnce } from './mount-once.ts'
|
|
6
6
|
import { UsageService, type UsageServiceOptions } from './host/usage-service.ts'
|
|
7
|
-
import { makeUsageOverviewRoute, makeUsageRefreshRoute } from './host/routes.ts'
|
|
7
|
+
import { makeUsageDayRoute, makeUsageOverviewRoute, makeUsageRefreshRoute } from './host/routes.ts'
|
|
8
8
|
|
|
9
9
|
declare module '@deepseek-ai/cordis' {
|
|
10
10
|
interface Events {
|
|
@@ -126,9 +126,9 @@ export const apply = mountOnce('@linxin666/dsh-usage', (ctx: Context, config?: U
|
|
|
126
126
|
/** True while this activation has a start queued behind a predecessor's flush. */
|
|
127
127
|
let queued = false
|
|
128
128
|
|
|
129
|
-
/** Register the service's
|
|
129
|
+
/** Register the service's routes; the returned disposer unregisters them. */
|
|
130
130
|
const mountRoutes = (next: UsageService): (() => void) => {
|
|
131
|
-
const disposers = [makeUsageOverviewRoute(ctx, next), makeUsageRefreshRoute(ctx, next)]
|
|
131
|
+
const disposers = [makeUsageOverviewRoute(ctx, next), makeUsageDayRoute(ctx, next), makeUsageRefreshRoute(ctx, next)]
|
|
132
132
|
.map((route) => ctx.webServer.register(route))
|
|
133
133
|
return () => {
|
|
134
134
|
for (const dispose of disposers) {
|