@cassius0924/dsh-usage-dashboard 0.8.0 → 0.8.3
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 +55 -3
- package/lib/client.js +1085 -236
- package/lib/client.js.map +4 -4
- package/lib/index.js +506 -61
- package/lib/index.js.map +4 -4
- package/lib/types/breaker-gate.d.ts +94 -0
- package/lib/types/breaker-settings.d.ts +28 -0
- package/lib/types/breaker.d.ts +49 -0
- package/lib/types/client/api.d.ts +8 -1
- package/lib/types/client/breaker-pill.d.ts +21 -0
- package/lib/types/client/breaker-state.d.ts +8 -0
- package/lib/types/client/breaker.d.ts +60 -0
- package/lib/types/client/charts.d.ts +8 -0
- package/lib/types/client/i18n.d.ts +21 -0
- package/lib/types/client/locales.d.ts +33 -0
- package/lib/types/client/session-cost-pill.d.ts +15 -0
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/client/top-up.d.ts +13 -0
- package/lib/types/context.d.ts +36 -0
- package/lib/types/contract.d.ts +65 -0
- package/lib/types/index.d.ts +19 -16
- package/package.json +3 -2
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The peak-window breaker gate.
|
|
3
|
+
*
|
|
4
|
+
* DeepSeek's `agent/pre-step` is a waterfall: a listener receives the proposed
|
|
5
|
+
* step and either calls `next()` (let it proceed) or withholds it (park). This
|
|
6
|
+
* gate parks steps only when all conditions hold:
|
|
7
|
+
*
|
|
8
|
+
* - the master switch is on,
|
|
9
|
+
* - the step's agent (its session) opted in via its own switch,
|
|
10
|
+
* - a peak window is open,
|
|
11
|
+
* - the turn was started by a person (`source.kind === 'user'`).
|
|
12
|
+
*
|
|
13
|
+
* Parked steps resume when the window settles at 18:00, when the user lets them
|
|
14
|
+
* continue from the UI, or when the owning turn is cancelled (`signal` aborts —
|
|
15
|
+
* abort must let the loop unwind normally instead of throwing here).
|
|
16
|
+
*
|
|
17
|
+
* Steps after the first one in a turn can carry no messages, so the "was this a
|
|
18
|
+
* user turn" decision is remembered per `(session, turn)`. The same memory
|
|
19
|
+
* records a manual release so the user's "continue now" is not undone by the
|
|
20
|
+
* very next step of that turn. Both are bounded: sessions come and go, but the
|
|
21
|
+
* map must not grow forever on a long-lived host.
|
|
22
|
+
*
|
|
23
|
+
* Timer handling is split behind `BreakerTimerFace` so tests can drive a fake
|
|
24
|
+
* clock; the production face uses a real `setTimeout`, unrefs it (a parked turn
|
|
25
|
+
* must never keep the process alive) and re-arms long waits in chunks, because
|
|
26
|
+
* `setTimeout` cannot represent delays above `MAX_BREAKER_TIMER_DELAY_MS`.
|
|
27
|
+
*/
|
|
28
|
+
import { type BreakerSettings } from './contract.ts';
|
|
29
|
+
import { type BreakerMessageFace } from './breaker.ts';
|
|
30
|
+
/** The timer seam; production uses `systemBreakerTimers`, tests a fake clock. */
|
|
31
|
+
export interface BreakerTimerFace {
|
|
32
|
+
schedule(callback: () => void, delayMs: number): unknown;
|
|
33
|
+
cancel(handle: unknown): void;
|
|
34
|
+
}
|
|
35
|
+
/** `setTimeout`'s documented maximum delay; longer waits are re-armed. */
|
|
36
|
+
export declare const MAX_BREAKER_TIMER_DELAY_MS = 2147483647;
|
|
37
|
+
/** Production timers: a real unref'd `setTimeout`. */
|
|
38
|
+
export declare const systemBreakerTimers: BreakerTimerFace;
|
|
39
|
+
/** Clamp one wait into the range a single `setTimeout` can carry. */
|
|
40
|
+
export declare const clampBreakerTimerDelay: (delayMs: number) => number;
|
|
41
|
+
export interface BreakerGateOptions {
|
|
42
|
+
/** Settings to start from; the host loads persisted ones asynchronously. */
|
|
43
|
+
initialSettings?: BreakerSettings;
|
|
44
|
+
/** Clock seam, epoch ms; defaults to `Date.now`. */
|
|
45
|
+
now?: () => number;
|
|
46
|
+
/** Timer seam; defaults to the real clock. */
|
|
47
|
+
timers?: BreakerTimerFace;
|
|
48
|
+
}
|
|
49
|
+
export declare class BreakerGate {
|
|
50
|
+
#private;
|
|
51
|
+
constructor(options?: BreakerGateOptions);
|
|
52
|
+
get settings(): BreakerSettings;
|
|
53
|
+
/** Steps parked right now, across every session. */
|
|
54
|
+
get held(): number;
|
|
55
|
+
/** Session ids with at least one parked step, sorted for the wire projection. */
|
|
56
|
+
get heldSessions(): string[];
|
|
57
|
+
/**
|
|
58
|
+
* Park one proposed step until it may proceed. Resolves immediately when the
|
|
59
|
+
* step is not subject to the breaker (session not opted in, off-peak,
|
|
60
|
+
* machine-started turn, turn already released by hand) or when the turn is
|
|
61
|
+
* aborted/disposed.
|
|
62
|
+
*
|
|
63
|
+
* The listener wrapping this must call `next()` after it resolves — this
|
|
64
|
+
* method never throws and never returns a decision of its own.
|
|
65
|
+
*
|
|
66
|
+
* `sessionId` is the agent id from the payload; an agent without one cannot
|
|
67
|
+
* be matched against the opt-in list, so its steps pass.
|
|
68
|
+
*/
|
|
69
|
+
hold(sessionId: string | undefined, turn: number, messages: readonly BreakerMessageFace[], signal: AbortSignal): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Let parked steps continue now. `sessionId` narrows it to one session; no
|
|
72
|
+
* argument releases every parked step (the UI's "continue all" button). The
|
|
73
|
+
* released turns are remembered, because re-parking their next step would
|
|
74
|
+
* contradict the user's instruction.
|
|
75
|
+
* @returns how many parked steps were released.
|
|
76
|
+
*/
|
|
77
|
+
release(sessionId?: string): number;
|
|
78
|
+
/**
|
|
79
|
+
* Apply new settings. Turning off the master switch — or a session's own
|
|
80
|
+
* switch — releases the affected parked steps immediately and without
|
|
81
|
+
* remembering a bypass: that is the user's decision, not a request for one
|
|
82
|
+
* particular turn to run.
|
|
83
|
+
*/
|
|
84
|
+
update(settings: BreakerSettings): void;
|
|
85
|
+
/** Release everything and stop the timer; called when the plugin unloads. */
|
|
86
|
+
dispose(): void;
|
|
87
|
+
/** (Re)arm the single settle timer for the current peak window. */
|
|
88
|
+
private armTimer;
|
|
89
|
+
private onTimer;
|
|
90
|
+
private clearTimer;
|
|
91
|
+
private releaseRecord;
|
|
92
|
+
private markReleased;
|
|
93
|
+
private rememberTurn;
|
|
94
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type BreakerSettings } from './contract.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the settings file path from the environment. The env parameter is
|
|
4
|
+
* injectable so tests can exercise the `DSH_HOME` branch without touching the
|
|
5
|
+
* process environment.
|
|
6
|
+
* @param env - environment to read `DSH_HOME` from; defaults to `process.env`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function breakerSettingsPath(env?: NodeJS.ProcessEnv): string;
|
|
9
|
+
/** Shared IO options; the defaults are what the running plugin uses. */
|
|
10
|
+
export interface BreakerSettingsIoOptions {
|
|
11
|
+
/** Explicit file path, bypassing {@link breakerSettingsPath}. */
|
|
12
|
+
filePath?: string;
|
|
13
|
+
/** Warning sink, so tests can capture instead of printing. */
|
|
14
|
+
warn?: (message: string) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Read persisted settings. A missing file is the first-run case and returns
|
|
18
|
+
* the defaults silently; unreadable, malformed, or structurally invalid
|
|
19
|
+
* content warns and returns the defaults.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadBreakerSettings(options?: BreakerSettingsIoOptions): Promise<BreakerSettings>;
|
|
22
|
+
/**
|
|
23
|
+
* Persist settings atomically. The temp file is created beside the target and
|
|
24
|
+
* renamed over it, so readers only ever see a complete document.
|
|
25
|
+
* @param settings - master switch and session opt-ins; normalized before writing.
|
|
26
|
+
* @param options - explicit path, if any.
|
|
27
|
+
*/
|
|
28
|
+
export declare function saveBreakerSettings(settings: BreakerSettings, options?: Pick<BreakerSettingsIoOptions, 'filePath'>): Promise<void>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Peak-window breaker decisions — pure functions, no timers or IO.
|
|
3
|
+
*
|
|
4
|
+
* DeepSeek charges peak rates in Beijing time 09:00–12:00 and 14:00–18:00 on
|
|
5
|
+
* weekdays (see `pricing.ts`, which owns that definition). A session that opted
|
|
6
|
+
* in parks its user-started steps while a peak window is open and lets them
|
|
7
|
+
* continue at 18:00 — the end of the day's last peak window — so one pause
|
|
8
|
+
* covers both windows without a lunch-time exception.
|
|
9
|
+
*
|
|
10
|
+
* All time math is done in Beijing time (UTC+8) rather than the machine's local
|
|
11
|
+
* zone, matching the price table.
|
|
12
|
+
*/
|
|
13
|
+
import { type BreakerData, type BreakerSettings } from './contract.ts';
|
|
14
|
+
/** Peak windows in Beijing time, display-only copy shared with `pricingInfo`. */
|
|
15
|
+
export declare const BREAKER_PEAK_WINDOWS: readonly ["09:00–12:00", "14:00–18:00"];
|
|
16
|
+
/**
|
|
17
|
+
* The one field of a message this module inspects: its producer. Only
|
|
18
|
+
* `kind === 'user'` means a person started the turn in a chat; scheduled
|
|
19
|
+
* tasks, goal continuations and subagent relays all carry another kind and are
|
|
20
|
+
* therefore never parked.
|
|
21
|
+
*/
|
|
22
|
+
export interface BreakerMessageFace {
|
|
23
|
+
source?: {
|
|
24
|
+
kind?: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* When the current peak window settles: 18:00 Beijing time on the same day.
|
|
29
|
+
* Outside peak there is nothing to wait for, so the answer is `now`.
|
|
30
|
+
*
|
|
31
|
+
* A peak window can only be open between 09:00 and 18:00 on a weekday, so the
|
|
32
|
+
* same-day 18:00 instant is always in the future here. Converting through
|
|
33
|
+
* UTC+8 schedules the same instant on a host in another timezone.
|
|
34
|
+
*/
|
|
35
|
+
export declare function nextResumeAt(nowMs: number): number;
|
|
36
|
+
/**
|
|
37
|
+
* Whether these proposed-step messages belong to a turn a person started. A
|
|
38
|
+
* single user-produced message is enough: a step that carries it is part of
|
|
39
|
+
* the user's request, whatever else the batch holds.
|
|
40
|
+
*/
|
|
41
|
+
export declare function isUserTurn(messages: readonly BreakerMessageFace[]): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* A copy holding exactly the contract fields, with session ids deduplicated,
|
|
44
|
+
* filtered to the charset sessions can actually have, and sorted so persisted
|
|
45
|
+
* documents and API projections are stable.
|
|
46
|
+
*/
|
|
47
|
+
export declare function normalizeBreakerSettings(settings: BreakerSettings): BreakerSettings;
|
|
48
|
+
/** Project live gate state into the wire shape the client renders. */
|
|
49
|
+
export declare function breakerProject(settings: BreakerSettings, nowMs: number, holding: readonly string[]): BreakerData;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BalanceResponse, SessionUsageResponse, UsageResponse } from '../contract.ts';
|
|
1
|
+
import type { BalanceResponse, BreakerReleaseRequest, BreakerResponse, BreakerSettingsRequest, SessionUsageResponse, UsageResponse } from '../contract.ts';
|
|
2
2
|
/** Last cached value (possibly stale), for an instant first render. */
|
|
3
3
|
export declare const getCachedBalance: () => BalanceResponse | null;
|
|
4
4
|
export declare const getCachedUsage: () => UsageResponse | null;
|
|
@@ -10,3 +10,10 @@ export declare function fetchUsage(force?: boolean): Promise<UsageResponse>;
|
|
|
10
10
|
export declare const getCachedSessionUsage: (sessionId: string) => SessionUsageResponse | null;
|
|
11
11
|
export declare const subscribeSessionUsage: (sessionId: string, fn: () => void) => (() => void);
|
|
12
12
|
export declare function fetchSessionUsage(sessionId: string, force?: boolean): Promise<SessionUsageResponse>;
|
|
13
|
+
/** Current projection: opted-in sessions plus the live peak/holding status. */
|
|
14
|
+
export declare function fetchBreaker(): Promise<BreakerResponse>;
|
|
15
|
+
/** Write one switch: `sessionId` targets that conversation, omitting it targets
|
|
16
|
+
* the master switch. The host answers with the full projection. */
|
|
17
|
+
export declare function updateBreaker(request: BreakerSettingsRequest): Promise<BreakerResponse>;
|
|
18
|
+
/** Let parked work continue now; an omitted target means every parked session. */
|
|
19
|
+
export declare function releaseBreaker(request?: BreakerReleaseRequest): Promise<BreakerResponse>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 高峰暂停 pill in the composer dock, beside the host's own
|
|
3
|
+
* 「会话统计」/「Token 用量」pills and this plugin's 「会话费用」pill. It is the
|
|
4
|
+
* switch for the current conversation only: on means this session's
|
|
5
|
+
* user-started turns pause during Beijing-time peak pricing and resume at
|
|
6
|
+
* 18:00. The 额度 page lists every session that opted in and carries the
|
|
7
|
+
* master switch; this pill is where a single conversation opts in.
|
|
8
|
+
*
|
|
9
|
+
* The three states are told apart by three channels at once, never colour
|
|
10
|
+
* alone: copy (「高峰暂停 · 关」/「高峰暂停 · 开」/「已暂停 · 18:00」), glyph
|
|
11
|
+
* (outline / solid) and colour (neutral / primary / warn).
|
|
12
|
+
*
|
|
13
|
+
* Skin and geometry mirror `./session-cost-pill.tsx` (same slot, same
|
|
14
|
+
* `.dq-cost-pill`), so the dock reads as one row. The switch writes
|
|
15
|
+
* optimistically, then takes the host's returned projection as truth; a
|
|
16
|
+
* failure rolls back and is surfaced through `title`/`aria-label` only.
|
|
17
|
+
*/
|
|
18
|
+
import { type ReactElement } from 'react';
|
|
19
|
+
export declare function BreakerPill({ sessionId }: {
|
|
20
|
+
sessionId?: string;
|
|
21
|
+
}): ReactElement | null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BreakerData } from '../contract.ts';
|
|
2
|
+
/** Latest host projection, or null before the first read lands. */
|
|
3
|
+
export declare function getBreakerData(): BreakerData | null;
|
|
4
|
+
/** Replace the projection and notify both surfaces. */
|
|
5
|
+
export declare function publishBreakerData(data: BreakerData | null): void;
|
|
6
|
+
export declare function subscribeBreakerData(listener: () => void): () => void;
|
|
7
|
+
/** Subscribe the calling component to the shared projection. */
|
|
8
|
+
export declare function useBreakerData(): BreakerData | null;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side helpers for the session-scoped peak breaker: response-shape
|
|
3
|
+
* validation (the host half may not be restarted yet, so an absent or
|
|
4
|
+
* malformed payload must degrade to「状态暂不可用」instead of crashing),
|
|
5
|
+
* per-session projections for optimistic switches, and pure display
|
|
6
|
+
* formatting that is independent of the browser timezone.
|
|
7
|
+
*/
|
|
8
|
+
import { type BreakerData, type BreakerResponse } from '../contract.ts';
|
|
9
|
+
/**
|
|
10
|
+
* Narrow a response to one whose every field the UI dereferences exists.
|
|
11
|
+
* Mirrors `usageIsUsable` in ./api.ts: a stale host that does not serve the
|
|
12
|
+
* breaker route (404) or an older payload shape falls through to the neutral
|
|
13
|
+
* copy rather than being rendered as if it were current.
|
|
14
|
+
*/
|
|
15
|
+
export declare function breakerIsUsable(res: BreakerResponse | null | undefined): res is BreakerResponse & {
|
|
16
|
+
data: BreakerData;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* `HH:mm` in Beijing time for an ISO instant, or null when absent/unparseable.
|
|
20
|
+
*
|
|
21
|
+
* Computed through the fixed +08:00 offset rather than `Intl`/local Date
|
|
22
|
+
* getters: the resume clock is defined in Beijing time, and the browser may be
|
|
23
|
+
* in any timezone (same approach as ./budget.ts's month boundary).
|
|
24
|
+
*/
|
|
25
|
+
export declare function beijingClock(iso: string | null | undefined): string | null;
|
|
26
|
+
/** Whether `sessionId` itself opted in, ignoring the master switch. */
|
|
27
|
+
export declare function isSessionOptedIn(data: BreakerData | null, sessionId: string | null | undefined): boolean;
|
|
28
|
+
/** Whether `sessionId`'s opt-in is in force: its own switch and the master switch. */
|
|
29
|
+
export declare function isSessionEnabled(data: BreakerData | null, sessionId: string | null | undefined): boolean;
|
|
30
|
+
/** Switch a session's breaker can be in; `holding` means it is parked right now. */
|
|
31
|
+
export type SessionBreakerState = 'off' | 'on' | 'holding';
|
|
32
|
+
/**
|
|
33
|
+
* Per-session state for the composer pill. Holding is only reported for an
|
|
34
|
+
* enabled session: a release can race a stale projection, and a session the
|
|
35
|
+
* host no longer lists must read as off regardless of the holding list. With
|
|
36
|
+
* the master switch off nothing is in force, so the pill reads off too.
|
|
37
|
+
*/
|
|
38
|
+
export declare function sessionBreakerState(data: BreakerData | null, sessionId: string | null | undefined): SessionBreakerState;
|
|
39
|
+
/**
|
|
40
|
+
* Projection with one session switched on/off, used for the optimistic state
|
|
41
|
+
* before the host answers. Disabling also drops the session from `holding`:
|
|
42
|
+
* off means nothing is parked for it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function withSessionToggled(data: BreakerData | null, sessionId: string, enabled: boolean): BreakerData | null;
|
|
45
|
+
/**
|
|
46
|
+
* Projection with the master switch flipped, used for the optimistic state
|
|
47
|
+
* before the host answers. Off means nothing is parked — per-session switches
|
|
48
|
+
* are kept, so the 额度 page can still list and manage them.
|
|
49
|
+
*/
|
|
50
|
+
export declare function withMasterToggled(data: BreakerData | null, enabled: boolean): BreakerData | null;
|
|
51
|
+
/** Title fallback for a session whose title has not been read yet. */
|
|
52
|
+
export declare function shortSessionId(sessionId: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Which status row the 额度 page's breaker card shows. `unavailable` means the
|
|
55
|
+
* host route could not be read (or answered with a shape this client cannot
|
|
56
|
+
* use); `masterOff` outranks the peak phases because nothing pauses then. The
|
|
57
|
+
* peak phases depend only on the host clock and holding list.
|
|
58
|
+
*/
|
|
59
|
+
export type BreakerPhase = 'unavailable' | 'masterOff' | 'offPeak' | 'peak' | 'peakHolding';
|
|
60
|
+
export declare function breakerPhase(data: BreakerData | null): BreakerPhase;
|
|
@@ -58,4 +58,12 @@ export declare function Heatmap(props: {
|
|
|
58
58
|
}): ReactElement;
|
|
59
59
|
export declare const fmt: (v: string | number) => string;
|
|
60
60
|
export declare const fmtInt: (v: number | undefined) => string;
|
|
61
|
+
/**
|
|
62
|
+
* Compact token/count formatting. English delegates to `Intl` compact notation;
|
|
63
|
+
* Simplified Chinese keeps the 万/亿 scale words because "1.6万 tokens" reads
|
|
64
|
+
* faster than "16K tokens" to Chinese readers.
|
|
65
|
+
* @param v - raw number of tokens or calls.
|
|
66
|
+
* @param locale - active plugin locale; defaults to Simplified Chinese.
|
|
67
|
+
* @returns the compact string.
|
|
68
|
+
*/
|
|
61
69
|
export declare const fmtCompact: (v: number | undefined, locale?: LocaleId) => string;
|
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
import { type ReactElement, type ReactNode } from 'react';
|
|
2
2
|
import { type LocaleKey } from './locales.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Locales this plugin ships copy for: DSH's two built-in locales — Simplified
|
|
5
|
+
* Chinese (`zh`, the host's own id) and English (`en`). The plugin keeps the
|
|
6
|
+
* host's language catalog untouched on purpose: language selection stays a DSH
|
|
7
|
+
* setting, and contributing a plugin-private third language would leave the
|
|
8
|
+
* host UI and this dashboard speaking different languages, because no other
|
|
9
|
+
* package ships a dictionary for it.
|
|
10
|
+
*/
|
|
3
11
|
export type LocaleId = 'zh' | 'en';
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the `Intl` tag for a plugin locale.
|
|
14
|
+
* @param locale - active plugin locale.
|
|
15
|
+
* @returns the BCP 47 tag to pass to `Intl.*` / `toLocaleString`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function intlLocale(locale: LocaleId): string;
|
|
4
18
|
export type Translate = (key: LocaleKey, params?: Record<string, unknown>) => string;
|
|
5
19
|
export declare const fallbackT: Translate;
|
|
6
20
|
export declare function LocaleProvider(props: {
|
|
7
21
|
t: Translate;
|
|
8
22
|
children: ReactNode;
|
|
9
23
|
}): ReactElement;
|
|
24
|
+
/**
|
|
25
|
+
* Read the translate function plus the concrete locale it is bound to. The
|
|
26
|
+
* identity rides the dictionary itself (`meta.locale`), because slot components
|
|
27
|
+
* receive only the `t` seat, not the host's locale snapshot; every dictionary
|
|
28
|
+
* stores its own id there, so a language switch moves both together.
|
|
29
|
+
* @returns the active `t` and the plugin locale it resolves to.
|
|
30
|
+
*/
|
|
10
31
|
export declare function useI18n(): {
|
|
11
32
|
t: Translate;
|
|
12
33
|
locale: LocaleId;
|
|
@@ -189,6 +189,7 @@ export declare const zh: {
|
|
|
189
189
|
readonly 'balance.granted': "赠送额度 {amount}";
|
|
190
190
|
readonly 'balance.runway': "预计可用";
|
|
191
191
|
readonly 'balance.runwayBreakdownTitle': "查看预计可用天数说明";
|
|
192
|
+
readonly 'balance.topUpTitle': "余额偏低,前往 DeepSeek 开放平台充值(新标签页打开)";
|
|
192
193
|
readonly 'balance.days': "{count} 天";
|
|
193
194
|
readonly 'balance.overYear': "> 365 天";
|
|
194
195
|
readonly 'balance.noRunway': "近 {days} 天没有用量,无法估算";
|
|
@@ -227,6 +228,9 @@ export declare const zh: {
|
|
|
227
228
|
readonly 'settings.widgetTabs': "展示浮窗的页面";
|
|
228
229
|
readonly 'settings.widgetTabsHint': "列表会随已安装的页面自动更新。";
|
|
229
230
|
readonly 'settings.widgetTabsEmpty': "当前没有可配置的会话页面。";
|
|
231
|
+
readonly 'settings.peakPause': "高峰暂停";
|
|
232
|
+
readonly 'settings.peakPauseHint': "总开关;关闭后所有会话都不暂停。每个会话的开关会保留,可随时重新打开。";
|
|
233
|
+
readonly 'settings.peakPauseLoading': "正在读取总开关状态…";
|
|
230
234
|
readonly 'settings.lowBalance': "余额预警线";
|
|
231
235
|
readonly 'settings.lowBalanceOn': "余额低于 {amount} {currency} 时,这里和悬浮窗都会转为警示色。填 0 关闭。";
|
|
232
236
|
readonly 'settings.lowBalanceOff': "已关闭余额预警。填一个大于 0 的数开启。";
|
|
@@ -243,6 +247,32 @@ export declare const zh: {
|
|
|
243
247
|
readonly 'settings.tabDragAria': "拖动「额度」调整标签位置";
|
|
244
248
|
readonly 'settings.tabMoveEarlier': "前移";
|
|
245
249
|
readonly 'settings.tabMoveLater': "后移";
|
|
250
|
+
readonly 'breaker.title': "高峰暂停";
|
|
251
|
+
readonly 'breaker.desc': "在对话输入框旁按会话开启;高峰时段自动暂停该会话,18:00 继续。自动化任务不受影响;总开关在「设置」卡里。";
|
|
252
|
+
readonly 'breaker.pillOff': "高峰暂停 · 关";
|
|
253
|
+
readonly 'breaker.pillOn': "高峰暂停 · 开";
|
|
254
|
+
readonly 'breaker.pillHolding': "已暂停 · {clock}";
|
|
255
|
+
readonly 'breaker.pillAriaOff': "高峰暂停:本会话关闭,点击开启";
|
|
256
|
+
readonly 'breaker.pillAriaOn': "高峰暂停:本会话开启,{clock} 继续,点击关闭";
|
|
257
|
+
readonly 'breaker.pillAriaHolding': "高峰暂停:本会话正在暂停,{clock} 继续,点击关闭";
|
|
258
|
+
readonly 'breaker.pillAriaMasterOff': "高峰暂停:总开关已关闭,本会话不会暂停;点击只切换本会话开关";
|
|
259
|
+
readonly 'breaker.pillTitleOff': "高峰时段暂停本会话:当前关闭,点击开启";
|
|
260
|
+
readonly 'breaker.pillTitleOn': "高峰时段暂停本会话:当前开启,{clock} 继续;点击关闭";
|
|
261
|
+
readonly 'breaker.pillTitleHolding': "本会话正在暂停,{clock} 继续;点击关闭";
|
|
262
|
+
readonly 'breaker.pillTitleMasterOff': "高峰暂停的总开关已在额度页关闭,本会话不会暂停;点击只切换本会话开关";
|
|
263
|
+
readonly 'breaker.actionError': "操作失败:{error}";
|
|
264
|
+
readonly 'breaker.loading': "载入中…";
|
|
265
|
+
readonly 'breaker.statusUnavailable': "状态暂不可用";
|
|
266
|
+
readonly 'breaker.statusMasterOff': "总开关已关闭,所有会话都不暂停;可在「设置」卡重新打开";
|
|
267
|
+
readonly 'breaker.statusOffPeak': "闲时 · 不暂停会话";
|
|
268
|
+
readonly 'breaker.statusPeak': "高峰时段 · 开启的会话会自动暂停";
|
|
269
|
+
readonly 'breaker.statusHolding': "已暂停 {count} 个会话 · {clock} 继续";
|
|
270
|
+
readonly 'breaker.statusHoldingNoClock': "已暂停 {count} 个会话";
|
|
271
|
+
readonly 'breaker.release': "立即全部继续";
|
|
272
|
+
readonly 'breaker.releaseBusy': "正在继续…";
|
|
273
|
+
readonly 'breaker.empty': "还没有会话开启高峰暂停。在对话输入框旁的「高峰暂停」开关里开启。";
|
|
274
|
+
readonly 'breaker.rowDisableAria': "关闭「{title}」的高峰暂停";
|
|
275
|
+
readonly 'breaker.holding': "暂停中 · {clock}";
|
|
246
276
|
readonly 'panel.caption': "账户级 · 跨全部会话";
|
|
247
277
|
readonly 'panel.openAria': "打开额度页";
|
|
248
278
|
readonly 'widget.updatedNow': "刚刚更新";
|
|
@@ -262,6 +292,9 @@ export declare const zh: {
|
|
|
262
292
|
readonly 'widget.expandAria': "展开额度窗口";
|
|
263
293
|
readonly 'widget.collapseAria': "收起额度窗口";
|
|
264
294
|
readonly 'messageCost.label': "本轮费用";
|
|
295
|
+
readonly 'sessionCost.title': "会话费用";
|
|
296
|
+
readonly 'sessionCost.aria': "本会话估算费用 {amount}";
|
|
297
|
+
readonly 'sessionCost.active': "活跃时间";
|
|
265
298
|
readonly 'messageCost.tooltip': "由 dsh-usage-dashboard 提供 · 本轮对话费用为估算值";
|
|
266
299
|
readonly 'messageCost.aria': "{amount},本轮对话估算费用,由 dsh-usage-dashboard 提供";
|
|
267
300
|
readonly 'boundary.title': "额度面板出错了";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session cost pill in the composer dock (next to the host's「会话统计」and
|
|
3
|
+
* 「Token 用量」pills). It shows the estimated spend of the whole conversation
|
|
4
|
+
* and opens a small detail dialog above the dock — the same pill/dialog skin
|
|
5
|
+
* the host uses for its own composer statistics, so the three read as one row.
|
|
6
|
+
*
|
|
7
|
+
* The figure comes from the plugin's own session-usage API (the same endpoint
|
|
8
|
+
* the 「额度」tab's current-session card and the per-turn chip use), not from
|
|
9
|
+
* host projections: cost has to follow this plugin's pricing table.
|
|
10
|
+
*/
|
|
11
|
+
import { type ReactElement } from 'react';
|
|
12
|
+
/** One session cost reading plus its click-open breakdown. */
|
|
13
|
+
export declare function SessionCostPill({ sessionId }: {
|
|
14
|
+
sessionId?: string;
|
|
15
|
+
}): ReactElement | null;
|