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.

Timer — DSH Plugin for DeepSeek Harness
← Plugins
T

dsh-timer

Timer

In-process scheduled task plugin for dsh: systemd.timer-aligned directory-config scheduling (30s tick, one agent session per run)

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

npx -y @deepseek-ai/dsh plugin --profile web add github:xuan25/dsh-timer#98446bd942ca46d4f9ba9f9d03afa5342c1b4306
READMECompatibilityVersions

Compatibility and provenance

Timer is published as dsh-timer and currently resolves to version 0.1.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
github
Registry updated
10/15/2026

Versions

0.1.0stable
10/15/2026

Related plugins

Loading related plugins…

Latest
0.1.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
any
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
10/15/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
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in productivity-workflow.

Web All@linxin666/dsh-web-allDSH Web UI 全家桶聚合插件:一键安装全部功能插件(task-board / git-graph / pet / remote-web-ui / web-ui-settings / skin-center / community-plugins / compat shim)。compat 桥接层已并入本包(src/client),无需独立 compat npm 包。Deepseek Ipptdeepseek-ipptiPolloWork PPT Studio and its curated slide templates as a native DeepSeek Harness conversation view.Codex Ui@michengai/dsh-codex-uiDSH Codex UI — 为 DeepSeek Harness Web 提供 Codex 风格侧栏、工作区会话树、全局搜索和轮次导航 · A Codex-style sidebar, workspace session tree, global search, and turn navigation for DSH WebRemotedsh-remoteRemote-work assistant for DeepSeek Harness: connect SSH (password/key/agent/keyboard-interactive/proxy jump), pick a remote workspace, operate on it with 22 rw_* tools (incl. rw_edit/rw_stat/rw_mkdir/rw_remove/rw_move/rw_forward), conflict-aware mirror sy

README

dsh-timer

dsh-timer gives a dsh deployment a built-in job scheduler: when a trigger expires the plugin starts a fresh prompted session, so recurring work - daily reports, periodic checks, one-shot tasks - runs on schedule inside the dsh process.

  • Jobs live in a job directory, written by hand or with the nine-verb timer tool
  • Three trigger kinds: cron expressions and one-shot ISO times, intervals counted from the last run, and one-shot startup delays
  • Each run is a fresh prompted session with a configurable working directory, preset, model, and file policy
  • A per-job state file tracks the next trigger and the 50 most recent run records
  • systemd.timer-style behavior: persistent catch-up after downtime, overlap policies (skip/stop/allow), run-time limits, timezone and clock-rewind handling
  • Live rescan: the job directory is watched with a 500 ms debounce, so job edits take effect in seconds
  • Session offload: a per-minute sweep archives settled run sessions that outlived the job's run.sessionRetention window, keeping the framework session store bounded

This README is the operations quick reference. The complete field reference with examples is schemas/job.schema.json; usage details, examples, and troubleshooting live in the in-package skill skill/dsh-timer/SKILL.md.

Installation

dsh-timer is an out-of-tree plugin for a dsh profile. Install it with the dsh plugin command:

dsh plugin --profile <name> add dsh-timer

No configuration is required: the plugin registers itself and reads jobs from the job directory described below. One optional plugin config field refines the deployment's layout: jobsDir (the job directory, below). Its only other on-disk state is the session offload's retention log and archive under $DSH_HOME/dsh-timer/ (see Session offload).

Jobs

Job directory

The plugin resolves the job directory in the following order:

  1. Explicit candidates, in this order: the jobsDir plugin config value, then the environment variable DSH_TIMER_JOBS_DIR. If a designated candidate is unavailable, the plugin start fails hard.
  2. The default directory <profileDir>/timers, used only when no candidate is designated, where profileDir is the dsh profile directory that hosts the plugin. The profile is detected from the plugin's install location, or - when the plugin lives outside any install tree - from the running process's harness home ($DSH_HOME, resolved through the framework-provided dshHomePath service) by the framework's dsh-profile-<name> profile naming; exactly one profile must be found. If the resulting directory is unavailable, the plugin writes a log entry and starts degraded: no jobs are loaded and no state is persisted until the directory is fixed and reloaded.
  3. If no candidate is designated and no unique profile can be determined (zero or multiple profiles found, or the host does not provide the harness-home service), the plugin start throws; the error points at the explicit overrides from item 1, plus a jobsDir entry in the profile's cordis.patch.yml (a !!js loader expression, e.g. dshHomePath('profiles','<name>','timers'), shown below). A development directory that lives outside the profile must be designated through candidate 1.

The last channel above can be written as a loader expression. A !!js value in a plugin config is a YAML scalar tagged with the loader-expression tag: when the plugin entry activates, the loader evaluates the expression in a scope where every framework service - including dshHomePath, the harness-home path resolver - is available as an identifier, and the expression's result replaces the value:

# <profileDir>/cordis.patch.yml - pin the job directory from the profile layer
- id: dsh-timer
  config:
    jobsDir: !!js dshHomePath('profiles', '<name>', 'timers')

Here <name> is the profile directory name under the harness home. A patch entry replaces the whole config key of the targeted entry; dsh-timer's default config is empty, so the example sets the field it needs.

Run root

A job's run root is the working directory its run session is created in, and - under the plugin's privilege model - the job's control scope: a session outside that root (and below full access) manages the job only through the approval channel. The run root is the spec's explicit run.cwd; an add without one inherits the creating session's workspace itself (<session-cwd>) and persists the inherited value into the job file, so a job created through the plugin always carries its run root in its own file. A job whose spec sets no run.cwd has an undetermined run root: managing it fails closed through the approval channel, and its runs fail create_failed with the actionable remedy (set run.cwd in the job file).

The plugin ships no host-absolute run path of its own: every run root is either the caller's session fact or the spec's own explicit value.

The directory layout is as follows:

Path (relative to the job directory)Content
<id>.jsonone job per file; the in-file enable field decides whether the job is active (absent or true = enabled, false = disabled; a disabled job keeps living in the same file)
state/<jobId>.jsonthe job's runtime state (see "State file" below); only the plugin writes it
a job file that fails to parse or fails validationthe id is rejected and the reason is reported in status; the plugin keeps no last-good state
any other file name or subdirectoryignored and never rejected

A job id must match ^[a-z0-9][a-z0-9-]{0,63}$ - a lowercase letter or a digit first, then up to 63 lowercase letters, digits, or hyphens.

The plugin watches the job directory with a 500 ms debounce and rescans on any change; the reload verb of the timer tool forces the same rescan. A job that is disabled, or whose spec becomes invalid, while a run is in progress keeps its state file, so fixing the spec restores the job's run history.

Job file fields

The table below is a quick reference; see schemas/job.schema.json for the complete field descriptions with examples. The three trigger fields each accept a null value meaning that trigger is not used, and at least one trigger field must be non-null.

FieldTypeDefaultSemantics
enablebooleantrueWhen false, the job is stored disabled in the same file: it is never scheduled, but it can still be run manually and re-enabled by flipping the field.
onCalendarstring[] or null-Each entry is a 5/6-field cron expression (strict cron parsing, recurring) or a full ISO instant (one-shot).
onActiveSectime span or null-Counted from the last activation, which is the most recent run's start moment.
onStartupSectime span or null-Counted from process start and fires once per process.
timeZoneIANA timezone namethe system timezoneOverrides the system timezone for the job; applies only to calendar conditions. Ambiguous local times (DST folds) resolve to the first occurrence; a one-shot ISO instant inside a DST gap is rejected.
persistentbooleanfalseWhen true, if a trigger was due while the process was down or the job disabled, the plugin fires exactly one catch-up run after recovery, rebased to the current moment, and re-arms from the actual trigger moment. The missed interval is not persisted.
deferReactivationbooleanfalseApplies only to jobs that have onCalendar conditions (on other jobs the value is accepted and ignored). When true, a condition that starts a run is suspended for the duration of that run (its due moments are skipped without advancing the phase) and is re-armed from the run end moment. When false, the phase advances at every due moment even while a run is in progress.
runtimeMaxSectime span, 0, infinity, or nullno limitA run that exceeds the limit is cancelled and recorded as failed with cause timeout.
overlapskip, stop, or

Time spans use the systemd syntax: the units us/usec, ms/msec, s/sec/second(s), m/min/mins/minute(s), h/hour(s), d/day(s), w/week(s), M/month(s) (approx. 30 days), y/year(s) (approx. 365 days) - case-sensitive (m is minutes, M is months) - where a bare number means seconds and units may be combined (for example 30s, 5min, 1h30min, or 2d). The *Sec field names follow the systemd time directive names; a Sec field value is a span string (or null where the field tolerates it) - a JSON number is never accepted. A span shorter than half a millisecond rounds to 0 ms and makes the job fire on every tick; prefer values of at least 1 ms.

State file

The plugin persists each job's runtime state in <job directory>/state/<jobId>.json using atomic, best-effort writes with a stable key order; explicit null keys are preserved. The file format is schemas/state.schema.json:

{
  "schemaVersion": 1,
  "jobId": "example",
  "lastActivationMs": 1727200000000,     // most recent run start moment (null when never run; the onActiveSec base then falls back to process start)
  "lastTriggerMs": 1727100000000,        // job-level last on-calendar elapse (fire or overlap=skip consume; the phase anchor for the next elapse)
  "calendars": {                            // indexed by onCalendar value
    "0 9 * * 1-5": {
      "value": "0 9 * * 1-5",
      "kind": "cron",                       // cron | iso
      "neverMatch": false,
      "consumed": false,                     // true after a one-shot ISO has fired
      "nextFireMs": 1727300000000
    }
  },
  "runs": [ /* most recent 50, FIFO */ ]
}

A run record inside runs has the following keys:

KeyMeaning
runIdthe unique identifier of the run
sessionIdthe dsh session that executed the run
triggerwhich condition fired: scheduled (a calendar condition), monotonic (onActiveSec), startup (onStartupSec), or manual (the run verb)
startedAt / finishedAtepoch milliseconds; the run's start and end moments
statusrunning while the run is in progress, then finished or failed
causenull for a finished run; the value is set only on failure: timeout, cancelled, interrupted, create_failed, or turn_error
durationMsthe run's elapsed duration in milliseconds
summarya summary of the run, truncated to 2000 characters
errorthe error text when the run failed

finished only records that the turn ended cleanly; it does not claim that the task succeeded - the outcome lives in summary and the session transcript. A job that has never run reports no run in status (recent run: none (no runs yet) in the detail, last run: none (no runs yet) on the overview line).

Managing timers

The plugin registers the timer tool, which exposes the following nine verbs, and the dsh-timer skill:

VerbEffect
reloadrescans the job directory
status (id optional)shows an overview or a single job's detail
runtriggers the job manually once (allowed for disabled jobs too; unconditional start)
enablesets the in-file enable field to true (the onStartupSec already-fired flag is preserved via in-process memory)
disablesets the in-file enable field to false, cancelling the job's in-progress runs first (= disable --now)
addcreates a job; rejects if the id already exists (delete the job first to replace it) or if the spec is invalid
updatereplaces the spec of an existing job in place; rejects if the id is unknown (use add) or if the spec is invalid; the job's state file is preserved
deletedeletes the job file and its state file (in-progress runs settle without writing state)
cancelcancels all in-progress runs of the job

Session offload

Job runs leave on-disk session logs in the framework session store; at run volume (frequent intervals, long prompts) the store grows without bound. The per-minute offload sweep archives settled run sessions once the job's effective retention window elapses, so the store stays bounded: the store is never deleted, only moved, a run that is still in flight is kept until it settles, and a job that vanishes from the job directory is offloaded immediately regardless of age.

  • Windows: run.sessionRetention is per-job (config layer: the job file's run block, hot-updated through the live rescan): a non-negative integer number of milliseconds, or a duration string of 1-2 digits+unit segments (units d/h/m/s/ms) in descending order (e.g. 24h, 4h30m, 90s, 5000ms; a bare numeric string is invalid). 0 / null / absent = the unit default (see below; no offload when it is 0); an invalid value warns and is treated as 0. A job without a run.cwd is never offloaded (its runs fail create_failed first).
  • Unit default window: TIMER_SESSION_RETENTION (env, read once at boot; a duration string like 24h - a bare numeric string is invalid; absent = 0, invalid warns and falls back to 0) sets the default retention window for every job without an explicit run.sessionRetention (absent or null); an explicit job value always takes precedence over it.
  • Budget: TIMER_OFFLOAD_BATCH (env, default 50) bounds the sessions one sweep may process; an invalid value warns and falls back to 50, never disabling the sweep. When consecutive sweeps observe new log lines arriving faster than the budget, the sweep logs a trend warning (raise the knob or lower the retention windows; the warning takes no action on its own).
  • Failure semantics: an unreachable persistence service, a missing store root, or an archive on a different mount disables the sweep for that round (a visible warning; log lines are never lost); a failed individual move keeps the entry for the next round.

Clock & timezone

During operation the plugin detects the following wall-clock events:

EventDetectionEffect
clock rewindthe wall clock lags the monotonic clock by more than 1 s between two consecutive ticksall trigger times are recomputed from the new current moment
system timezone changethe mtime of /etc/localtime changescalendar conditions without a job-level timeZone are recomputed under the new timezone; if the new timezone cannot be decoded, the plugin keeps the old timezone and writes a log entry

Development

The package is written in TypeScript using only the erasable syntax subset (no enums, namespaces, parameter properties, or decorators), so Node 24 can run the output with plain type stripping. The tsc build compiles src/ into lib/ as ESM (NodeNext resolution, strict mode).

Layout

PathContent
src/the TypeScript source
lib/the compiled output, shipped with the package
schemas/the machine-readable contracts: job.schema.json (job file) and state.schema.json (state file)
skill/the in-package skill
test/selftest.mjsthe pure-logic selftest
test/smoke.mjsthe framework-path smoke (tool schema validation, skill provider); needs the peer packages installed
test/schema-gate.mjsthe ajv-based schema gate (both schemas compiled as draft 2020-12, positive/negative cases including the structural trigger rule); uses an ajv 8 build that supports draft 2020-12, probed from the host toolchain (the DSH_HOME env, then the default host install location) rather than the package's own dependency tree

Plugin entry

The package entry is lib/index.js. Its default export DshTimerPlugin extends the cordis Service and follows the class-form plugin contract: static name, static inject, static Config (a schemastery schema that exposes ~standard.validate), and an instance async *[Service.init]() generator that yields the disposer first and then starts the scheduler.

Scripts

CommandPurpose
npm run buildcompiles src/ into lib/ with tsc
npm run typecheckruns tsc --noEmit
npm run lintruns eslint src/ (flat config + @typescript-eslint, including the type-aware no-floating-promises rule)
npm run selftestruns the selftest described below

Selftest

The selftest asserts the pure logic (util, time, cron, rearm, validator, and state) plus the croner test vectors against the compiled lib/, covers the session-offload plane (window parsing, retention-log mechanics, the sweep's window/liveness/budget/trend semantics, the run-lifecycle write gate, and the layout pin), and cross-checks the timer tool schema's null branches against schemas/job.schema.json as a source-level sync guard.

allow
skip
Defines what happens when a trigger fires while a run is already in progress. skip consumes the trigger in place (the phase advances at the due moment and no catch-up fire happens after the run ends - a run longer than the interval swallows grid points); stop cancels the in-progress run first and then starts; allow starts a parallel run.
runobject (required)-The run block. prompt is required and non-empty; cwd, preset, model, policy, and sessionRetention are optional - full field semantics in schemas/job.schema.json (run-cwd inheritance is covered in the Run-root section above; sessionRetention is covered in the Session-offload section below).