DSH Plugins Marketplace

DSH Plugins

Plugins

/

Development & Infrastructure

/

dsh-peak-hours

l

dsh-peak-hours

Manifest valid★ 1

Run-window switch for DeepSeek Harness: run always, or only in off-peak hours - every task pauses at China peak-price windows and resumes at the boundary.

UI (client)hasBundlePatch

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 中文.

中文文档 topic: dsh-plugin License: MIT

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.

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.

MethodPathBody / result
GET/api/peak-hours/health{ ok, plugin, version }
GET/api/peak-hours/statethe state envelope below
POST/api/peak-hours/mode{ "mode": "always" | "idle-only" }
POST/api/peak-hours/clock{ "offsetMs": <int|null>, "ttlMs"?: <int> }
{ "ok": true, "state": {
  "mode": "always", "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,
  "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 } } }

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

The selected mode is persisted in %DSH_HOME%\peak-hours-state.json with serialized atomic writes; a corrupt file falls back to always and logs once. 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.
  • waitingTasks in the state envelope is the host-wide gate count, so it can include tasks from other sessions.

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.

License

MIT

Comments

Loading…

From the same category

awesome-dsh-plugin

by awesome-dsh-plugin

A curated list of plugins for DeepSeek Harness (dsh) · DeepSeek Harness 插件精选列表

Development & Infrastructure

★ 18.3k

CC0-1.0

Python

Oct 10, 2026

Index only — not installable

by 0xsline

DeepSeek Harness (DSH) ecosystem: curated plugins, tools, and infrastructure from dsh-external/hub and the public dsh-plugin topic.

Development & Infrastructure

★ 1.2k

CC0-1.0

Python

Oct 10, 2026

Index only — not installable

by pax-beehive

Open-source CLI, schemas, resolver, and DSH agent tools for DSH Plugin Hub

Development & Infrastructure

★ 450

MIT

TypeScript

Oct 6, 2026

Index only — not installable

by xiajiajun516

DeepSeek Harness (DSH) backup & restore plugin — export, import, migrate and sync your complete DSH configuration, plugins, MCP servers, skills and workspace. One-click migration to another machine.

Development & InfrastructureManifest valid

★ 176

MIT

TypeScript

Oct 8, 2026

dsh plugin --profile web add dsh-config-manager

by yjh051108

推荐组件(非必须):DeepSeek Harness 运行时注入器;已随 dsh-routing-suite 单仓库化保留,本仓库继续维护/发布。

Development & InfrastructureManifest valid

★ 164

TypeScript

Sep 18, 2026

dsh plugin --profile web add @dsh-external/dsh-super-injector

by jigjoy-ai

A CLI that turns a goal into a pull request - and a sandbox for testing concurrent AI coding agents on the Mozaik runtime.

Development & Infrastructure

★ 124

MIT

TypeScript

Oct 2, 2026

Index only — not installable