its0din-ai/harness-accountant ↗★ 0

harness-accountant

DeepSeek balance and spend accounting for the DSH Web GUI: a sidebar card showing today's spend, the account's currency, and whether the current hour is billed at the peak or off-peak rate, plus a Settings breakdown for 1 day / 7 days / 1 month. 适合需要监控DeepSeek余额、按峰谷时段查看API费用及历史消费的用户。

패키지
harness-accountant
호환성
미검증
버전
1.0.1
라이선스
MIT
최근 업데이트
2026. 9. 28.

설치

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:its0din-ai/harness-accountant

Configuration

The schema lives in lib/index.js and the defaults are written out in cordis.patch.yml, so the settings page shows real, editable values.

keydefaultnotes
enabledtruefalse stops all background probing; routes still serve the ledger, and an explicit refresh still probes
poll_interval_sec60the healthy cadence, clamped to >= 30 regardless of what is configured. Consecutive failures stretch it: see below
retain_days400clamped to [7, 730]
accounting_utc_offset_minutes480the day boundary, as a fixed offset from UTC. 480 is UTC+08:00, DeepSeek's billing day. Set 0 for UTC or your own offset for a local day. It is an offset, not a timezone: it does not model daylight saving
currencyautowhich wallet to follow. auto takes the first usable one in the order the balance response lists the wallets - right for an account that reports a single currency. Set CNY or USD to pin it; read case-insensitively, and anything else means auto
api_key_envDEEPSEEK_API_KEYa credential reference, resolved per probe
api_base_urlhttps://api.deepseek.commust be a bare https: origin; plain http: is refused
max_response_bytes65536ceiling on the bytes the probe will buffer from a response body, schema-bounded to [1024, 1048576]. A balance payload is a few hundred bytes, so this is pure headroom; it exists so a hostile or broken origin cannot allocate without bound

The API key comes from ctx.credentials (i.e. ~/.dsh/.credentials.yaml or the launch environment). It is resolved fresh for every probe and never cached.

A ledger day is a calendar day at accounting_utc_offset_minutes, not at the host's timezone, so the service and any other process bucket the same instant into the same day. The default follows DeepSeek's billing day rather than the operator's local one, because the bill being accounted for is DeepSeek's.

The file carries a schema version and the accounting_utc_offset_minutes in force when it was last written, so it says for itself how its day keys were bucketed. A file at an older version is upgraded in place on load, and /overview reports that as ledger_notice. A file this build cannot read - a newer version, or a shape that does not validate - is never guessed at and never overwritten: it is moved aside to ledger.json.incompatible in the same 0700 directory with the same 0600 mode, the plugin starts a fresh ledger, and the reason arrives in ledger_notice.

A ledger tracks one currency at a time, and the two it knows are never mixed: CNY and USD micro-units are not comparable, so a change of currency discards the day series rather than summing across them. Which one that is comes from the balance response. Under the default auto the plugin takes the first usable wallet in the order the response lists them - there is no built-in preference for either, because an account that reports both is exactly where a guess would decide what gets recorded. An account that reports a single currency always follows it, CNY included. Set currency to CNY or USD to pin the choice instead; a pin the account stops reporting fails the probe by name (account reports no CNY wallet) rather than quietly following the other wallet, because that substitution is precisely what would throw the history away. The plugin holds no exchange rate and never converts: a 4.58 balance is rendered with the sign of whichever currency the response reported, and the number itself is passed through untouched.

A failing probe does not retry forever at the same rate. The first three consecutive failures keep the configured cadence, because a service that blinks once should be retried normally; after that each further failure doubles the wait, up to sixteen times the configured interval. One successful reading clears the streak and the wait goes straight back to the base. The backing-off wait is not configurable - the three numbers are constants in lib/index.js. The current streak and the pending wait are both in the /overview payload (consecutive_failures, next_probe_in_sec) if you want to see the state the loop is in.