Pauses long-running tasks and model requests during user-defined time windows, achieving zero charges during peak hours for time-of-use pricing APIs like DeepSeek. Includes balance display and consumption bar charts with 10-minute granularity.
- Language
- JavaScript
- License
- MIT
- Branch
- main
Install
$ dsh plugin --profile web add dsh-save-moneyRun the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial
Install via your agent
Install the DeepSeek Harness plugin zhu168/dsh-save-money/plugin for me: review the repository at https://github.com/zhu168/dsh-save-money first, then run the install command and verify the plugin loads successfully.
Paste this instruction to the DSH Web GUI assistant — it will install and verify for you.
One-Line Description
Automatically pause running DSH long-running tasks and all model requests within user-defined time windows, then automatically resume after the window ends—ensuring zero charges from time-of-use pricing APIs like DeepSeek during peak hours; optionally displays official account balance and an 8-hour consumption bar chart.
Core Features
- Customizable multiple pause/resume windows, supporting overnight periods (e.g., 23:00–08:00) and day-of-week filtering
- Auto-pause at scheduled time: Running tasks are safely "frozen" (progress preserved as-is), auto-resumed after window ends—no pause if no tasks running
- Request-level gate: During pause, AI sends zero new requests to model services, zero billing; manual interactions always pass through
- Per-model tier savings control: Official flash/pro, OpenCode flash/pro—4 independent toggles; unrecognized models (old names like
chat/reasoner, third-party, etc.) are always exempt - One-time "End This Savings Session" button: Skips only the current pause window; master "enabled" toggle unaffected; next window still applies
- Top floating banner (pending pause: light yellow / paused: light red) + session header persistent colored status text (color changes with state), 10-language UI (zh/zh-TW/en/de/fr/es/it/pt/ja/ko, auto-follows browser)
- Optional DeepSeek official account balance display + 8-hour consumption bar chart with 10-minute granularity (external consumption distinguished by warning color)
- Config persisted to workspace
save-money.config.json, survives uninstall/reinstall/restart; auto-written to repo directory on first install to avoid polluting DSH installation directory
Technical Implementation
- Language: TypeScript (source), built as ESM JavaScript (
scripts/build.jsinlinessrc/*.tsinto single filesplugin/index.jsandplugin/client.js) - Key dependencies:
@deepseek-ai/dsh-client-ui-primitives(runtime detection, falls back to self-rendered buttons if missing); DSH built-in services (timer/slots/agents/goals/fs/sandboxPolicy/webServer/tools/credentials/settings/subprocess); no third-party runtime dependencies - Architecture pattern: Dual-half host plugin (Host + Client), same source → official bundle three install forms (
--patchoverlay / tgz bundle / link+HMR)- Host half (
src/host.ts): Intercepts all model calls at request level viactx.on('llm/stream', ...)(gate), freezes/resumes long-running tasks viactx.get('goals')+ctx.get('agents'),ctx.timer.intervaldrives state machine every 30 seconds (NORMAL → WARN → PAUSED), dynamic submodulessrc/config.ts/src/state.ts/src/host-goals.ts/src/balance-host.ts/src/gate.ts/src/host-tools.ts/src/host-http.tsinlined at build time - Client half (
src/client.ts): Injectsconversation.session.header.utilitiesslot (unique persistent entry: colored status text) +settings.sectionslot ("Save Money" plugin section in system settings) +shell.overlay(floating banner); i18n fromsrc/i18n/*.ts(10 language dicts inlined at build time) - Mount declaration:
plugin/package.json#dsh.bundle.patch(cordis overlay) +plugin/cordis.patch.yml(inserts plugin lineid: save-money)
- Host half (
- Entry files: Host entry
src/host.ts(exports{ inject: ['timer'], apply(ctx) {...} }, build outputplugin/index.js); Client entrysrc/client.ts(build outputplugin/client.js); build scriptsscripts/build.js(TS → JS) +scripts/make-plugin.js(generates official bundle); quick-try overlaycordis.patch.yml
Use Cases
When using DeepSeek and other time-of-use pricing model services in DSH, and wanting zero billing during peak hours (Beijing time 09:00–12:00, 14:00–18:00): this plugin freezes tasks, closes the gate, and stops sending requests at scheduled times; auto-resumes after window ends with conversation and task progress preserved. Also applicable to any "don't want the machine working during certain time periods" scenario (custom electricity prices, custom bandwidth off-peak). Also wants to see recent 8-hour consumption granularity per 10 minutes from official account for bill reconciliation and consumption attribution across devices.
Prerequisites & Compatibility
| Dependency | Min Version | Notes |
|---|---|---|
| DeepSeek Harness (DSH) | Not declared | Plugin doesn't explicitly declare DSH min version in manifest; uses standard ctx.on('llm/stream') + ctx.timer + ctx.get(...) injection mode, requires DSH official plugin form support (typically 0.1.0-rc.6 and above) |
| Node.js | Not declared | Neither package.json nor plugin/package.json in repo declares engines.node; the only engines field in package-lock.json comes from typescript dev dependency (>=14.17) |
| Platform | Cross-platform | No native modules introduced; config persistence via DSH fs service (compatible with Node node:fs), balance history via ~/.dsh/ |
| Native modules | None | No dependencies on node-pty / node:sqlite or other native bindings |
Installation
dsh plugin --profile web add github:zhu168/dsh-save-money/plugin
Configuration Options
All config can be visually edited in DSH Settings → Save Money Plugin or via the session header "Save Money · 🟢" status text popup; the following fields can also be programmatically modified via AI tool save_money_configure (excerpted from src/config.ts:26-58, src/host-tools.ts:31-66, with instant validation and auto-persistence):
| Config | Type | Description | Default |
|---|---|---|---|
enabled | boolean | Master toggle: auto-pause at scheduled times when checked, immediate resume all windows when unchecked | false |
timezone | string (IANA timezone) | Timezone for time windows; auto-detected on first browser launch, falls back to Beijing time | Asia/Shanghai |
warnMinutes | number | Minutes before next window to enter "pending pause" WARN state and show light yellow banner | 5 |
windows | array | Pause/resume window list, each item { pauseAt, resumeAt, days?, timezone? } (HH:mm strings, days is 1-7 array representing Monday–Sunday) | [] |
reconcileOnStart | boolean | If targets are still paused on startup, auto-resume (to avoid permanent freeze after restart) | true |
lang | enum | UI language: auto (follows browser) / zh / zh-TW / en / de / fr / es / it / pt / ja / ko | auto |
showBalance | boolean | Show DeepSeek official account balance next to session header status text (only shows after official API calls) | false |
modelApply.official-flash | boolean | Whether to pause official DeepSeek flash model inside pause window (checked = pause for savings) | true |
modelApply.official-pro | boolean | Whether to pause official DeepSeek pro model inside pause window | true |
modelApply.opencode-flash | boolean | Whether to pause OpenCode Go/Zen flash model inside pause window | false |
modelApply.opencode-pro | boolean | Whether to pause OpenCode Go/Zen pro model inside pause window | false |
FAQ
Q: What's the difference between this plugin and DSH's built-in "budget/cost" feature?
A: The official cost tool is passive accounting with "overspend alerts"; this plugin actively closes the gate—no requests sent to models at scheduled times, zero charges, and freezes running tasks until window ends before resuming.
Q: I don't see "Save Money" status text on the page after installation. What should I do?
A: Must fully restart DSH (Ctrl+C to stop, then start) and force-refresh the browser (Ctrl+Shift+R)—the UI mounts at startup. Builds before v1.2.5 had "toggle won't check / settings won't work" issues; please upgrade to the latest version.
Q: Is it a bug that AI doesn't reply during pause?
A: Not a bug—it's intended behavior. The design for pause windows is "no requests, no charges"; running tasks are frozen, conversation context preserved; auto-resumes all after window ends, or click "End This Savings Session" to resume immediately.
Q: Where is the config file saved? Will it be lost after uninstall?
A: Config auto-saves to workspace save-money.config.json (already .gitignored). Location auto-resolves via 6-level fallback: pointer file ~/.dsh/save-money-config-path.json > current session workspace > session workspace named dsh-save-money > DSH startup directory > sibling dsh-save-money directory of startup directory > sandboxPolicy.workspaceRoot. Uninstall/reinstall won't touch it; delete the file to restore defaults.
Q: Will "End This Savings Session" turn off the master toggle?
A: No. This button is a one-time in-memory operation, ending only this specific triggered pause window; the next window (today or later) still applies, the persisted "enabled" toggle stays unchanged. To disable all window savings, manually uncheck "Enabled".
Q: Does balance display only support DeepSeek official?
A: Yes. Balance and consumption stats only show when the most recent model request ran on official DeepSeek; other vendors' keys (SiliconFlow, relay, etc.) won't show balance. Balance history is saved by API key fingerprint to ~/.dsh/dsh-save-money-balance.json; changing keys automatically retires old history.
Q: Will "One-Click DeepSeek Time-of-Use Strategy" auto-enable?
A: No. One-click strategy only appends peak windows to config (deduplicated, Beijing time 08:58–12:02, 13:58–18:02—2 min early/late margin), the "Enabled" toggle requires user to check—your call.
Q: How to uninstall?
A: dsh plugin --profile web remove dsh-save-money, then restart DSH. Config file save-money.config.json and balance history ~/.dsh/dsh-save-money-balance.json won't be deleted—manually delete them for complete cleanup.
Ease of Use
Beginner—one command install + restart DSH + click session header status text to open popup and check "Enabled" to use; advanced usage involves adjusting time windows, model tier toggles, and timezone as needed.
Known Issues & Limitations
- After install/upgrade must fully restart
dsh web(Ctrl+C to stop, then start) and force-refresh browser (Ctrl+Shift+R) to see UI; page refresh alone won't reload server-side plugin (README.zh.md:298-306) - Balance and consumption stats only support DeepSeek official API—other vendors' keys (SiliconFlow, relay, etc.) won't show balance or bar chart (src/balance-host.ts:88-130)
- AI not responding during pause windows is intended behavior, but may mislead users into thinking "AI is stuck"; use banner and status text color changes to determine whether current state is NORMAL/WARN/PAUSED
- Any unrecognized models (old names like
chat/reasoner, other third-party, etc.) are always exempt from pause—if you intentionally use old names, they pass through the gate (src/host-tools.ts:33 / README.zh.md:28) - Config auto-saves to workspace, but if current directory is DSH installation directory on first install, plugin writes to repo directory first and records via
~/.dsh/save-money-config-path.jsonpointer to avoid polluting DSH installation directory (src/config.ts:118-185) - Builds before v1.2.2 had
harness is not definederror, before v1.2.4 had incomplete UI loading, before v1.2.5 had "toggle won't check" issue—must upgrade to latest version (README.zh.md:298-306) - Plugin doesn't explicitly declare DSH and Node.js min version requirements in manifest; after upgrading DSH major version, recommend verifying compatibility first
Save-money plugin for DSH (DeepSeek Harness) — define your own "pause / resume" time windows; at pause time running long tasks are paused (not stopped) automatically, and they resume when the window ends. Built for LLM API peak/off-peak pricing (e.g. DeepSeek peak hours 9:00–12:00, 14:00–18:00 Beijing time, off-peak at half price), and equally useful for time-of-use electricity rates, bandwidth off-peak shifting, or any "I don't want the machine working during this period" scenario.
Status: ✅ Implemented, continuously maintained. 中文版
Interface

The colored status text in the top-right of the session header (Save · ⚪/🟢/🟡/🔴, color follows the state) is the single persistent entry — click it to open the settings popover. When a pause is upcoming or active, a reminder banner appears at the top of the page (with the End this save mode button).
With "Show balance" enabled, your official DeepSeek account balance appears next to the status text; clicking the balance opens the last-8-hours spend bar chart:

Each bar is a 10-minute window: the X axis shows whole-hour ticks, the Y axis key-point amount ticks (≤5, with gridlines), and hovering shows each window's exact time range and amount. Windows where the balance dropped without any local activity are marked in warning color (changes may come from another device).
Features
- Multiple time windows: add / remove pause-resume windows freely; supports midnight-crossing windows (23:00–08:00) and per-weekday filtering;
- Automatic pause on schedule: when the pause time arrives, running tasks are safely "frozen" (their progress is preserved exactly — nothing is interrupted or lost) and resume automatically when the window ends; if nothing is running, nothing is paused;
- No requests during the window (the saving core): inside a pause window the AI sends no new requests to the model service, so no cost is incurred; after the window ends (or on disable / end-this-window) everything resumes, with conversation context and in-flight tasks unaffected. The AI not replying inside a window (including new conversations) is expected — to resume right away, click the End this save mode button (takes effect directly, no AI involved);
- Per-model-tier save mode: decide per model tier which ones pause during windows. The settings panel shows two compact rows — Official API: flash / pro and opencode go·zen API: flash / pro — four toggles total. Checked = paused (saving money); unchecked = exempt (requests flow even during a window). Defaults: official flash + pro checked, opencode exempt; your changes are persisted and never reset. Any unrecognized model (legacy
chat/reasonernames, other third parties, anything else) is always exempt — it can never block your requests; - End this save mode (one-shot, current window only): the banner and settings-popover button end the currently active pause window only — if paused, tasks resume immediately and the gate opens; if a pause is upcoming, it is cancelled. The window is skipped until its resume time, then the state clears automatically. The next window (today or later) still takes effect, and the persistent Enable flag is never touched, so you cannot accidentally leave the feature disabled;
- UI reminders: top floating banner (light yellow for upcoming pause / light red for paused, with the End this save mode button) + the single persistent session-header entry (next to the Session log, "Save · 🟢 Working" colored status text; click to expand settings), colors follow the state in real time;
- Timezone support: IANA timezone dropdown, browser auto-detection with Beijing time (+8) fallback; UTC projection checked (Beijing 09:00 == UTC 01:00);
- One-click DeepSeek preset: dedupe-append the peak windows (08:58–12:02, 13:58–18:02, with a 2-minute boundary margin — pause 2 min early, resume 2 min late); it does not auto-enable — your call; legacy no-margin windows are upgraded automatically on one-click;
- Persistent config: all settings are saved automatically to the workspace file
save-money.config.json(gitignored); configuration survives browser refresh and plugin disable/re-activate, and is loaded on startup with optional reconciliation of paused goals; - Account balance display (optional): tick "Show balance" in settings and your official DeepSeek account balance appears next to the status text in the header (automatic currency symbol, theme-adaptive color). Off by default. With several model sources configured (official DeepSeek + SiliconFlow, relays, …) the balance follows the model actually in use: it is shown while the latest real request runs on the official provider and hidden otherwise (the sampled spend history is kept, so switching back to DeepSeek re-shows the balance immediately);
- Spend statistics: with the balance display enabled, the backend samples the balance every 5 minutes (288 points covering the last 24 hours). Hover the balance to see how much was spent in the last 1 hour / 10 minutes / 24 hours (balance increases from top-ups or refunds show as "+amount");
- Spend bar chart (last 8 hours, per 10 minutes): click the balance to open a chart of the last 48 ten-minute windows. External-spend attribution — a window where the balance dropped without any local model activity is marked with a warning color (hover: "no local activity in this window; change may come from elsewhere") instead of being reported as local consumption, so using the same API key from another machine never looks like a one-second spend explosion. Balance top-ups (recharges / refunds) are never drawn as bars and never stretch the Y axis — the axis stays spend-only; a hidden top-up window shows a "balance recovered (recharge / refund), not counted in spend analysis" note on hover;
- Persistent balance history: sampled history is saved to
~/.dsh/dsh-save-money-balance.json(account-level, shared across projects) and survives plugin updates/restarts. The file is keyed by a fingerprint of the API key — changing the key discards the old history automatically; - Non-intrusive by design: no screen lock, no overlay blocking, no user action prevented — only the automatic continuation of goals is paused; manual interaction always flows.
How it works
stateDiagram-v2
direction TB
[*] --> Disabled
Disabled : Disabled ⚪ — nothing pauses, no money saved
Working : Working 🟢 — normal use, requests flow
Pausing : Pausing soon 🟡 — yellow banner reminder
Paused : Paused 🔴 — requests suspended, no cost
Disabled --> Working : check "Enable"
Working --> Pausing : a window starts within 5 minutes
Pausing --> Paused : pause time reached
Paused --> Working : window ends, auto-resumes
Paused --> Working : "End this save mode" — resume now
Pausing --> Working : "End this save mode" — cancel this pause
Working --> Disabled : uncheck "Enable"
Pausing --> Disabled : uncheck "Enable"
Paused --> Disabled : uncheck "Enable"
- When a pause window starts, running tasks are frozen (progress preserved) and the AI stops replying — that is normal: the machine is saving you money. Everything resumes automatically when the window ends;
- See the yellow "pause in X min" banner but don't want to pause right now? Click End this save mode to cancel this window only;
- Paused and want to continue immediately? Click End this save mode (red button) — it resumes right away, skips only the current window, and never turns off Enable: the next window (today or later) still saves money as usual. If you ever want every window to take effect again, uncheck and re-check Enable.
Install
The official DSH plugin form is a module exporting apply + cordis.yml mounting (see the DSH official tutorial). You can install from npm (one command, recommended) or build from this repository (three options below: --patch quick try, distributable bundle, or the link + HMR development workflow). Every official form ships the full plugin: Host logic (scheduling, gate, goal freeze, tools, HTTP endpoints) and the browser UI (status text, banner, settings page), which loads automatically — no AI-assisted setup needed.
Install from npm (recommended)
The plugin is published to npm — one command installs it into your Web profile:
First-time install
npx @deepseek-ai/dsh plugin --profile web add dsh-save-money
Remove
npx @deepseek-ai/dsh plugin --profile web remove dsh-save-money
After installing, fully restart DSH (Ctrl+C, start again) and hard-refresh the browser (Ctrl+Shift+R) — the "Save" status text appears top-right in the session header.
Source install (build from this repo)
Using ~/app/ as an example directory (each step assumes the previous one succeeded):
cd ~/app/
git clone https://github.com/zhu168/dsh-save-money.git
cd dsh-save-money
npm install
cd plugin
npm pack # runs the build automatically; produces the plugin tarball
ls # check the tarball name, e.g. dsh-save-money-1.4.3.tgz
cd ~/app/deepseek-harness # change to your harness directory (clone it first if missing — see "0." below)
pnpm dsh plugin --profile web remove dsh-save-money # only if you installed it before; skip if you haven't
pnpm dsh plugin --profile web add ../dsh-save-money/plugin/dsh-save-money-1.4.3.tgz # use the name from `ls` above
pnpm dsh --profile web # starts DeepSeek Harness — the plugin appears top-right in the session header
Two notes: ① the tarball name on the
addline must match whatlsprinted (1.4.0is just an example); ② if your harness runs via npx, replace everypnpm dshabove withnpx @deepseek-ai/dsh(install commands too).
0. Run DSH itself first
The plugin runs inside DSH, so DSH must run before anything below. Two ways:
A. Installed CLI (easiest, needs Node.js):
npx @deepseek-ai/dsh web # starts the Web UI at http://127.0.0.1:3080
B. From source (e.g. on a Raspberry Pi; dsh is NOT a global command — you must run pnpm dsh inside the checked-out deepseek-harness directory):
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web # inside this directory only
In source mode, never type bare
dsh ...— it is not on PATH (command not found: dsh). Usepnpm dsh ...from thedeepseek-harnessdirectory, or./node_modules/.bin/dsh ....
Path 1: --patch quick try (official, loads local source)
Good for trying the plugin on the same machine where you keep this repository.
-
(Optional) Rebuild the latest plugin module — a fresh clone needs the build deps first:
npm install # first clone: installs typescript and friends npm run prepare # one step: src/*.ts -> dist/*.js -> plugin/index.js + plugin/client.js -
Edit
cordis.patch.yml, replacing<REPO_ROOT>with the repository's absolute path (it is the only placeholder in the file — use your editor's "replace all"):- insert: - id: save-money name: '<REPO_ROOT>/plugin/index.js'Examples — Windows:
name: 'D:/git/github/dsh-save-money/plugin/index.js'; Linux / macOS / Pi:name: '/home/pi/dsh-save-money/plugin/index.js'.If your profile already has the plugin installed via bundle / link, skip Path 1 (stacking
--patchwould register the plugin twice). -
Start with the overlay:
dsh web --patch ./cordis.patch.yml # globally installed dsh npx @deepseek-ai/dsh web --patch ./cordis.patch.yml # npx-launched (Step 0, option A) # from source: pnpm dsh web --patch ./cordis.patch.yml -
Open the browser at the printed URL (default http://127.0.0.1:3080) — the "Save" status text appears in the session header top-right. No extra step needed.
Path 2: bundle + dsh plugin add (official, distributable — recommended for another machine / the Pi)
The bundle is a small .tgz that you build once and install anywhere. You do not clone this repository on the target machine — DSH installs the plugin into its own profile (~/.dsh/profiles/web/node_modules/).
Step 1 — build & pack the bundle (on any machine with this repository):
cd dsh-save-money
npm install # first clone only (typescript dev dependency)
cd plugin
npm pack # runs the prepare build automatically; produces dsh-save-money-1.4.3.tgz
plugin/ is the standard bundle layout: package.json declares dsh.bundle.patch + dsh.client, cordis.patch.yml inserts the plugin row, index.js is the Host module and client.js the browser UI bundle.
Step 2 — copy the tgz to the target machine (scp / USB stick / however you move files; run this on the build machine, one directory above the repository, adjusting the path to yours):
scp dsh-save-money/plugin/dsh-save-money-1.4.3.tgz pi@<pi-ip>:~/
Step 3 — install into a profile (on the target machine; first run initializes the profile with @deepseek-ai/dsh-base):
# DSH run from source (inside the deepseek-harness directory):
pnpm dsh plugin --profile web add ~/dsh-save-money-1.4.3.tgz
# npx-launched (Step 0, option A) or globally installed dsh: works from any directory
npx @deepseek-ai/dsh plugin --profile web add ~/dsh-save-money-1.4.3.tgz
Both commands do the same thing: install the tgz into
~/.dsh/profiles/web/node_modules/. Use whichever matches how you start DSH.
Git install also works: dsh plugin --profile web add github:you/dsh-save-money#<sha> (git install requires prepare builds and allowBuilds, see the DSH publish tutorial).
Step 4 — verify the layer mounted, then start (fully restart DSH, don't just refresh the page):
pnpm dsh --profile web --dump-config # the output should show a dsh-save-money layer (its plugin rows)
pnpm dsh --profile web # Ctrl+C to stop an already-running instance first
Step 5 — open the browser at http://127.0.0.1:3080 and hard-refresh (Ctrl+Shift+R). The "Save" status text appears in the session header; click it for the settings. You can also confirm the plugin is live from a conversation (save_money_status).
Upgrading to a newer version (e.g. 1.3.0 → 1.4.0):
The commands below use
pnpm dsh; if you launched DSH via npx, replacepnpm dshwithnpx @deepseek-ai/dsh.
# on the build machine:
cd dsh-save-money && git pull && cd plugin && npm pack # fresh dsh-save-money-<new>.tgz
scp dsh-save-money/plugin/dsh-save-money-1.4.3.tgz pi@<pi-ip>:~/
# on the target:
pnpm dsh plugin --profile web remove dsh-save-money
pnpm dsh plugin --profile web add ~/dsh-save-money-1.4.3.tgz
pnpm dsh --profile web # restart, then hard-refresh the browser
Your settings survive upgrades (they live in save-money.config.json, untouched by remove/re-add).
Uninstalling:
pnpm dsh plugin --profile web remove dsh-save-money
Path 3: development workflow (what this repository actually uses: link install + HMR hot reload)
Changes take effect immediately — no DSH restart and no tgz needed. This is the form used while developing the plugin: dsh plugin add link: links the plugin/ directory straight into the profile, and a cordis-plugin-hmr hot-reload plugin watches the build output. Save the source → npm run prepare → the running DSH hot-swaps the new plugin; hard-refresh the browser to see the client UI.
One-time setup (on the development machine):
-
Build and link-install:
cd dsh-save-money npm run prepare # src/*.ts -> plugin/index.js + plugin/client.js pnpm dsh plugin --profile web add link:D:/git/github/dsh-save-money/plugin # use your repository's absolute path -
Configure hot reload: edit
~/.dsh/profiles/web/cordis.patch.yml(create it if missing) and replacerootwith your repository'splugindirectory absolute path:- insert: - id: save-money-hmr name: '@deepseek-ai/cordis-plugin-hmr' config: root: ['D:/git/github/dsh-save-money/plugin'] ignored: ['**/node_modules', '**/.*'] debounce: 100 -
Start DSH (
pnpm dsh web/npx @deepseek-ai/dsh web) and open the browser (default http://127.0.0.1:3080).
Daily dev loop:
# after editing src/*.ts, run once:
npm run prepare # rebuild -> writes plugin/index.js + plugin/client.js
DSH hot-replaces the plugin within ~0.1s (look for [hmr] logs in the terminal) — no restart; hard-refresh the browser (Ctrl+Shift+R) to see client-side changes.
Note:
link:and the Path 2 bundle install are mutually exclusive — if you installed the bundle before, runpnpm dsh plugin --profile web remove dsh-save-moneyfirst, thenadd link:.
Config persistence (all forms)
The plugin writes its settings to save-money.config.json (excluded by .gitignore, never committed): loaded automatically at startup, persisted immediately on every change. The config file location is resolved at runtime by walking a candidate list — the first directory that actually contains a config file wins:
- the directory recorded by the pointer file
~/.dsh/save-money-config-path.json(the last real location, so a restart resolves the same file even when no session matches); - the current session's workspace directory;
- every session workspace whose path ends in
dsh-save-money(the newest one wins when several exist); - the DSH startup directory itself (
process.cwd(), official install form); - a
dsh-save-moneydirectory sibling to the DSH startup directory (the README quick-install layout:~/app/deepseek-harnessnext to~/app/dsh-save-money); - final fallback:
sandboxPolicy.workspaceRoot(the DSH install dir).
On a fresh install (no config anywhere) the plugin prefers to write the config into the repo directory (the dsh-save-money dir matched by step 4/5) instead of polluting the DSH install dir, and records a pointer in ~/.dsh/ so every later restart loads the same file. Deleting the config file resets all settings.
Troubleshooting
| Symptom | Cause & fix |
|---|---|
zsh: command not found: dsh / dsh: command not found | You are running DSH from source — dsh is only available inside the deepseek-harness directory as pnpm dsh ... (or ./node_modules/.bin/dsh). Never use bare dsh outside it. |
Plugin fails to load with ReferenceError: harness is not defined | You are running a build older than v1.2.2 — the official form was fixed in v1.2.2. Rebuild (npm run prepare), re-pack, reinstall, and restart. |
| Installed but no status text / banner / settings page | Fully restart DSH (Ctrl+C, start again) and hard-refresh the browser (Ctrl+Shift+R) — the Client half is discovered at startup. If it still does not appear, you are running a build older than v1.2.4 — upgrade. |
| UI shows but the Enable checkbox (and other settings) do nothing | You are running a build older than v1.2.5 — the plugin used to start before the Web server was ready, so UI requests could not reach it. Upgrade to ≥ v1.2.5 and restart. |
| Where is my config file? | Resolved automatically from the 6-step candidate list above (session workspace / repo dir preferred; ~/.dsh/save-money-config-path.json records the last location). Deleting it resets all settings. |
Quick start
The steps below apply to every install form — Path 1 / Path 2 / Path 3 all ship the same settings UI. You can also configure through the tools in a conversation (
save_money_configure,save_money_status).
- After install and activation, click the "Save · 🟢 Working" status text in the top-right of the session header (next to the Session log) to open settings (the single persistent entry); or use the system settings page (sidebar → Settings → Save-money);
- Click One-click DeepSeek peak/off-peak savings → the peak windows are added automatically (08:58–12:02, 13:58–18:02 Beijing time, with the 2-minute boundary margin);
- Check "Enable" (the one-click does not auto-enable);
- Save the window settings — the plugin starts watching: tasks pause automatically at the window start and resume when it ends.
Entries
| Entry | Where |
|---|---|
| Status text (single persistent entry) | Session header top-right (next to the Session log) "Save · 🟢 Working", click to open the settings popover |
| System settings page | Sidebar → Settings → Save-money |
| Floating banner | Top capsule when a pause is upcoming / active + End this save mode button (one-shot: ends only the current window, future windows unaffected) |
Dynamic tools (Host)
| Tool | Purpose |
|---|---|
save_money_status | Query state / gate state (gate: open|closed) / window / pause record / UTC projection |
save_money_configure | Configure (enabled / timezone / warnMinutes / windows) |
save_money_end_window | End save mode for the currently active window only (one-shot, in-memory): resume now / cancel the upcoming pause; next windows still take effect |
save_money_debug_tick | Dev tool: manually advance the state machine |
Docs & License
- Repository layout:
src/holds the TypeScript plugin sources (single source of truth, every file ≤ 400 lines):core.ts/state.ts/config.ts(pure logic: time helpers, state machine, config controller),balance-history.ts/balance-bars.ts/balance-host.ts/balance-client.ts(balance history, model classification + spend bars, balance transport, balance UI helpers),host-goals.ts/gate.ts/balance-tracker.ts/host-http.ts/host-tools.ts(host sub-modules wired byhost.ts),i18n/*(10 locale dictionaries + aggregator),ui/*(client components wired byclient.ts).tests/(unit tests,npm test),scripts/build.js(TS → JS plugin bodies),scripts/typecheck.js(type check),scripts/make-plugin.js(official bundle generator),plugin/(bundle:package.json+cordis.patch.yml+index.js+client.js),cordis.patch.yml(quick-try overlay),package.json/tsconfig.json(build & type config),dist/(build output, gitignored),save-money.config.json(runtime config, gitignored) - i18n: UI strings live in
src/i18n/*— one dictionary per language (zh.ts,zh-TW.ts,en.ts,de.ts,fr.ts,es.ts,it.ts,pt.ts,ja.ts,ko.ts); the language is auto-detected from the browser locale, manually selectable (Auto + 10), and the choice persists in the config - License: MIT (see
LICENSE)
Read the usage guide →
Install steps, key points, FAQ and compatibility for this plugin — auto-derived from indexed fields.
Listing badge
[](https://deepseek-plugin.org/plugins/zhu168/dsh-save-money/plugin)Paste this markdown into your GitHub README to link back to this listing. The badge only states the listing — not a security endorsement.