DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Peak Hours — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
P

dsh-peak-hours

Peak Hours

Run-window switch for DeepSeek Harness: run always, or only in off-peak hours — every task, including one already running, pauses when a China peak-price window starts and resumes when it ends.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:lion231226/dsh-peak-hours#2764dcf75dcbdf62e77ea0c3f731e1546b156026
READMECompatibilityVersions

Compatibility and provenance

Peak Hours is published as dsh-peak-hours and currently resolves to version 0.2.1. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
github
Registry updated
10/14/2026

Versions

0.2.1stable
10/14/2026
0.1.1stable
10/9/2026
0.1.0stable
10/9/2026

Related plugins

Loading related plugins…

Latest
0.2.1
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
web
License
MIT
Source
github
GitHub
★ 1
Weekly downloads
0
Last push
10/14/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in models-usage.

Whale Widgetdsh-whale-widgetDeepSeek balance whale widget in the bottom-right corner of the DSH Web interface: balance/today’s usage/peak-off-peak pricing, customizable bubble click sequence (text/balance/today/peak-off-peak/image/random phrases and parallel weighted selection), per-line styles and fonts, floating quick editinUsage@linxin666/dsh-usageUsage statistics plugin for the dsh web GUI: per-provider balance and coding-plan quota detection plus a live token usage ledger, with a compact price glance card above the sidebar's Settings rowClient Ui Model Capabilities@linxin666/dsh-client-ui-model-capabilitiesPer-model capability declarations (image input and reasoning efforts) for custom pi-ai providers, edited in place on the Models settings cards; writes the official llm-pi-ai settings namespace, no DSH source changes.Cost Meterdsh-cost-meterDeepSeek Harness session cost statistics plugin: current session cost, today’s charges, historical records, and synchronization with official pricing; supports multi-provider, multi-model price calculation (built-in pricing catalog for 170+ model IDs with automatic matching); queries and displays su

README

dsh-peak-hours

A run-window switch for DeepSeek Harness (dsh).

It adds a control to the right of the composer's mode buttons that chooses between running always and running only in off-peak hours. In the second mode every dsh task — including one that is already running — is paused when a peak-price window starts and resumed automatically when it ends.

Available in English (this file) and 中文.

Run windows

Off-peak is the half-price window. Peak, in Beijing time (UTC+8), is:

WhenPhase
Monday–Friday, 09:00–12:00 and 14:00–18:00, excluding Chinese statutory holidayspeak
Everything else — 00:00–09:00, 12:00–14:00, 18:00–24:00, all weekend, all statutory holidaysidle

Make-up workdays (weekends that are worked to offset a holiday) stay idle: the rule only looks at the weekday and whether the date is a statutory holiday.

Install

From the dsh CLI:

dsh plugin --profile web add github:lion231226/dsh-peak-hours

The plugin is pure JavaScript with no build step and no runtime dependencies, so a source install is enough.

For a desktop profile that is currently running, this repository also ships install.ps1, which copies the package into %DSH_HOME%\profiles\<profile>\node_modules\ and maintains one managed row in the profile's cordis.patch.yml. It validates the patch file with js-yaml before writing (a malformed patch file stops the Loader from reconciling the whole profile), waits for the plugin to answer, and re-activates the entry with a fresh row id if a previous attempt left it failed. No restart is required.

Do not mix the two install paths. dsh plugin add registers the package as a profile bundle, and the bundle layer then contributes the patch row from the package's own cordis.patch.yml. install.ps1 writes that row by hand instead; running it against a profile that already has the bundle would leave two entries with the same id, and nothing downstream de-duplicates them (the patch applier pushes inserted rows as-is and the cordis Loader has no duplicate check). The script now refuses to run in that case.

powershell -ExecutionPolicy Bypass -File .\install.ps1
powershell -ExecutionPolicy Bypass -File .\install.ps1 -Uninstall

Using it

The button sits in the composer tool row, immediately to the right of the mode buttons (the permission and plan selectors). It has three states:

StateLabelDot
Run always全天运行 / Run alwaysneutral
Off-peak only, currently idle空闲时段 · 运行中 / Off-peak · runninggreen
Off-peak only, currently peak空闲时段 · 已暂停 / Off-peak · pausedamber

Clicking it opens a panel with the two modes, the current Beijing time, the next switch and a per-second countdown, today's holiday (when there is one), a warning when the holiday calendar has no data for the year, and a note while a clock override is active.

While a peak window is in force, a banner above the composer card states that submitted tasks will start automatically at the end of the window, with a live countdown.

How the pause works

The host half hooks two waterfalls and only acts in idle-only mode:

  1. agent/pre-step — before every model call. An abort during the wait is re-thrown, which is the control flow the agent loop already expects (signal.throwIfAborted() follows the waterfall).
  2. tools/pre-execute — before every tool body is dispatched, covering a step that runs into a peak window. On abort it returns the runtime's designed { kind: 'cancel' } instead of throwing, because a throwing listener is classified as a scheduler failure and fails the whole step.

The gate releases immediately when the phase is idle, and while a peak window is in force it registers a waiter and arms a single timer at the exact next transition instant — no polling, no busy loop. When the timer fires, the phase is re-evaluated and every waiter is released, so the work continues on its own.

llm/stream is deliberately not gated: it returns an AsyncIterable rather than a promise, and the agent loop has a call path that bypasses it, so the primary gate already covers every model call.

Nothing is lost while waiting: messages and tool calls stay in the session log, no model request is made, and the wait is abortable (pressing stop cancels it).

HTTP API

Registered on the host's loopback web server; no GUI token is required on loopback.

The mode is per session: every session carries its own choice, and a task is judged by the session that owns it. A session with no explicit choice follows default.

MethodPathBody / result
GET/api/peak-hours/health{ ok, plugin, version }
GET/api/peak-hours/state?session=<id>the state envelope below; without ?session= it reports default
POST/api/peak-hours/mode{ "mode": "always" | "idle-only" | null, "session"?: "<id>", "scope"?: "session" | "default" } — mode: null with a session clears that override; neither field writes default. Requires Content-Type: application/json
POST/api/peak-hours/clock{ "offsetMs": <int|null>, "ttlMs"?: <int> }; ttlMs is capped at 30 minutes
{ "ok": true, "state": {
  "mode": "always", "defaultMode": "always", "modeSource": "session",
  "sessionId": "session-abc", "phase": "idle", "reason": "off-hours",
  "now": 1791541815888, "nowText": "2026-10-09 18:30:15",
  "nextTransitionAt": 1791766800000, "nextTransitionText": "2026-10-12 09:00:00", "nextPhase": "peak",
  "msUntilTransition": 224984112, "paused": false,
  "waitingTasks": 0, "waitingTasksTotal": 0, "waitingTasksUnresolved": 0,
  "holidayToday": null, "holidayCoverage": "known", "unknownHolidayYear": null,
  "timeZone": "Asia/Shanghai (UTC+8)", "peakWindows": ["09:00-12:00", "14:00-18:00"],
  "clockOverride": null, "config": { "allowClockOverride": true } } }

waitingTasks counts the session named by ?session=, and the whole host when the parameter is absent — which is why the older session-less curl examples keep their meaning. waitingTasksTotal is always host-wide. waitingTasksUnresolved counts waiters whose session could not be resolved; those follow default, and the count exists so a resolution failure is visible instead of looking like "nothing is waiting".

clockOverride shifts the host's notion of "now" so a peak window can be simulated without waiting for one. It always carries a TTL (10 minutes by default) and clears itself: without that, a session that is held by the window it created has nothing left to release it. Set DSH_PEAK_HOURS_ALLOW_CLOCK=0 to disable the endpoint.

Holiday data

The built-in calendar lists every statutory day off in 2025 (28 days) and 2026 (33 days), taken from the State Council holiday notices. Make-up workdays are not part of the set. Each entry keeps its source titles and URLs in HOLIDAY_SOURCE_DETAIL inside lib/core/holidays.js.

The 2027 notice has not been published yet, so that year reports holidayCoverage: "unknown" with unknownHolidayYear: 2027 and treats the date as a non-holiday (a weekday inside a window is peak); the UI surfaces the warning. Adding a year is one data entry.

Callers can overlay their own dates with createCalendar({ extra: { '2027-01-01': '元旦' } }); extra wins and is not restricted by years.

Configuration

KeyDefaultMeaning
allowClockOverridetrueWhether POST /api/peak-hours/clock is accepted
versionfrom package.jsonVersion reported by /health

Per-session choices live in %DSH_HOME%\peak-hours-state.json as { "version": 2, "default": "always", "sessions": { "<sessionId>": "idle-only" } }, written with serialized atomic writes. An older { "mode": ... } file is migrated on first read — default takes that value and sessions starts empty — and a corrupt file falls back to always with an empty table, logging once. The table is capped at 500 entries and evicts the least recently written one, logging each eviction. Disposing the plugin releases every listener, timer and route.

Known limits

  • A model call that has already been issued is not interrupted; the task pauses at its next safe boundary.
  • Background processes in a terminal, and background shells started as jobs, are not agent steps and are not gated.
  • A message submitted during a peak window is not dropped — it waits and continues when the window ends.
  • Each session with a mounted client keeps its own poll (15 s, 3 s near a boundary), so request volume grows with the number of open sessions.
  • Writing default changes every session that has no override of its own; the composer states that scope before the change is made.

Upgrading from 0.1.x

The mode used to be one host-wide value. It now lives per session, and the old value becomes default, so every session without its own entry keeps behaving exactly as before after the upgrade. Two things to know:

  • Refresh the window after installing. A page still running the 0.1.x client sends a bare {mode} write, which the new host correctly reads as "write default" — i.e. the old bundle would reproduce the very bug this release fixes.
  • Downgrading loses sessions. An 0.1.x build ignores the sessions table and rewrites the file in the old shape, so back up or delete %DSH_HOME%\peak-hours-state.json before going back.

Tests

node --test                          # author suites + the independent verification suite
node --test "test/**/*.test.mjs"     # author suites only
node --test "verify/**/*.test.mjs"   # independent verification only
node tools/smoke-host.mjs            # host half against a fake context, over real HTTP
node tools/show-monday.mjs           # print one day's phase segments

test/ holds the author suites (core schedule, host gate/routes/store, client). verify/ holds an independent suite written against the same contracts: a day-by-day check of the holiday calendar against the official notices, a boundary matrix at one-second resolution, property tests over the transition function, gate semantics under a fake clock, adversarial cases, client bundle checks, and a harness that mounts the plugin under the real cordis runtime. verify/EVIDENCE.md records each command with its raw output.

Node 24 rejects a directory argument for node --test, hence the glob patterns above.

Field test (real clock, no simulation)

On Monday 2026-10-12, with the plugin in off-peak mode, a message submitted at 09:01:58 Beijing time — inside the morning peak window — was held for 178 minutes. The session log for that run shows the message entering the inbox at 09:01:58.530, the turn opening two milliseconds later, then no event of any kind until step/start at 12:00:03.051 — three seconds after the boundary at 12:00:00 — followed by the model request at 12:00:03.054. Nothing was dropped and no interval was skipped: the work simply waited for the window to end.

License

MIT