@goodandready/dsh-cron 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,6 +35,8 @@ Autonomous AI agents often need to perform recurring duties: generating daily mo
35
35
  2. **Interactive "Create with DSH" Workflow** — chat with your agent to translate high-level requirements into a well-formed scheduled task.
36
36
  3. **Autonomous AI Tool Calling** — native `cron_*` tools let agents schedule their own follow-up executions during conversations.
37
37
  4. **Robust Scheduler & Atomic Storage** — built on `croner` with interval aliases, one-shot delays, atomic file persistence, run histories, and cost tracking.
38
+ 5. **Six Execution Runtimes** — shell, Node.js, Python, HTTP/webhook, remote SSH and Docker, plus per-task environment variables, workspace binding and isolated git worktrees for code-modifying agent tasks.
39
+ 6. **Multi-Channel Delivery With Templates** — one run fans out to Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, email, voice (`dsh-tts`) and Gitea, with `{variable}` message templates and secrets referenced by DSH credential name.
38
40
 
39
41
  ---
40
42
 
@@ -47,7 +49,7 @@ graph TD
47
49
  Overlay["Visual Task Manager Panel<br/>(Tabs: All, Active, Paused, Completed)"]
48
50
  CreateWithDSH["'Create with DSH' Dialog<br/>(natural language task)"]
49
51
  ManualForm["Manual Task Form<br/>(Cron Expression, Timeout, Overlap, Model)"]
50
- SettingsCard["Settings Card<br/>(Telegram / Kanban integration)"]
52
+ SettingsCard["Settings Card<br/>(Channels, Templates, Credentials)"]
51
53
  end
52
54
 
53
55
  subgraph Server ["Server Runtime (Cordis & DSH Services)"]
@@ -56,7 +58,9 @@ graph TD
56
58
  Scheduler["TaskScheduler Engine<br/>(Croner instances + one-shot timers)"]
57
59
  Store["Atomic TaskStore<br/>(tasks.json with atomic write)"]
58
60
  AgentRunner["Agent Session Dispatcher<br/>(Executes prompt with chosen model)"]
59
- Notify["Delivery<br/>(Telegram Bot API, dsh-kanban cards)"]
61
+ Runtimes["Execution Runtimes<br/>(shell, node, python, http, ssh, docker)"]
62
+ Notify["Delivery Router<br/>(templates + 10 channels)"]
63
+ Secrets["Credential References<br/>(DSH credentials / ENV)"]
60
64
  end
61
65
 
62
66
  SidebarBtn --> Overlay
@@ -95,7 +99,7 @@ Autonomous agents can manage schedules directly:
95
99
 
96
100
  | Tool | Description |
97
101
  |:---|:---|
98
- | `cron_create_task` | Creates a scheduled task: `title`, `schedule`, `prompt`, optional `type` (`llm`/`script`), `delivery`, `provider`, `model`, `notifyTelegram`, `onlyOnFailure`, `timeoutSeconds`, `overlapPolicy`, `kanbanMode` |
102
+ | `cron_create_task` | Creates a scheduled task: `title`, `schedule`, `prompt`, optional `type` (`llm`/`script`/`node`/`python`/`http`/`ssh`/`docker`/`skill`/`workflow`), `delivery`, `provider`, `model`, `channels`, `template`, `notifyTelegram`, `onlyOnFailure`, `timeoutSeconds`, `overlapPolicy`, `kanbanMode` |
99
103
  | `cron_schedule_task` | Alias of `cron_create_task` kept for compatibility with existing agent prompts |
100
104
  | `cron_list_tasks` | Lists tasks with statuses, next run timestamps, token totals, and cost estimates |
101
105
  | `cron_pause_task` | Pauses a schedule without deleting its configuration |
@@ -122,22 +126,54 @@ Powered by `croner`, supporting standard 5-field cron expressions plus user-frie
122
126
  * `*/15 * * * *` — every 15 minutes
123
127
  * `0 0 * * 0` — every Sunday at midnight
124
128
  * `every 10m` / `every 2h` / `every 30s` — natural duration intervals
125
- * `daily` / `hourly` / `weekdays` shortcuts
129
+ * `daily` / `hourly` / `weekdays` shortcuts, plus standard `@hourly` / `@daily` / `@weekly` / `@monthly` / `@yearly` and `@every 30m`
130
+ * **Per-task time zones** — set an IANA zone (e.g. `Europe/Berlin`) on a task; without it the schedule follows the server's local time
126
131
  * **One-shot tasks**: `at: 2026-09-05T15:00:00Z` (exact ISO timestamp) or relative delays `in 20m` / `in 2h` (Russian aliases such as `через 15 минут` are accepted too). One-shot tasks flip to `completed` automatically after their single run and are listed under the **Completed** tab.
127
132
 
128
- ### 5. Telegram Notifications & Delivery Routing
129
- Direct integration with the Telegram Bot API delivers execution reports and error traces straight to your messenger:
130
-
131
- * **Auto-detected or custom credentials** — enter a custom `botToken` and `chatId` in the settings dialog, or let the plugin inherit defaults from the `dsh-messenger-gateway` section of your DSH `settings.yaml` (best-effort fallback).
132
- * **Only-on-failure mode** — enable `onlyOnFailure` globally or per task. Clean runs stay silent; failures (`error` or `timeout` statuses) dispatch an alert with the error trace.
133
- * **Markdown formatting** — messages carry status badges (✅ / ❌), duration, schedule description, and monospace output blocks; dynamic values are escaped so odd titles cannot break the message.
133
+ ### 5. Execution Reliability
134
+ * **Automatic retries** — set `maxRetries` and a base `retryBackoffMs` per task; failed runs (`error`/`timeout`) are retried with exponential backoff, and the attempt counter resets on success.
135
+ * **Misfire policies** — choose per task what happens when the daemon was offline at a scheduled time: `skip` (default — record the gap), `runOnce` (execute once, late), or `catchUpAll` (run late and record the gap). A missed one-shot under `skip` is retired as `completed` instead of firing stale.
136
+ * **Concurrency limit** — `maxConcurrent` (plugin setting) caps parallel runs; extra runs are recorded as `skipped` with a reason.
137
+ * **Live execution indicator** — the task list shows a pulsing status icon and a running timer for the task in flight.
138
+
139
+ ### 6. Execution Runtimes
140
+ Every task picks its own runtime; non-LLM runtimes need no model and consume no tokens:
141
+
142
+ * **Shell** (`script`) — command or script through the harness shell, with `env` and `cwd`.
143
+ * **Node.js** (`node`) and **Python** (`python`) — run a snippet with an explicit interpreter path (`nodePath`, `pythonPath`); Python detects a project virtualenv.
144
+ * **HTTP** (`http`) — GET/POST/… to a URL with custom headers and body, and the response status/output recorded in the run history.
145
+ * **SSH** (`ssh`) — execute a command on a remote host through a `dsh-remote-workspace` profile (`sshProfileId`) or standalone host/key fields.
146
+ * **Docker** (`docker`) — run the command in a container image (`dockerImage`).
147
+ * **Environment variables** — a per-task `env` map (KEY VALUE per line in the UI) applied to external runtimes; secrets do not belong here.
148
+ * **Workspaces and worktrees** — bind a task to a harness workspace (`workspaceId`) and, for code-modifying agent tasks, run it in an isolated git worktree (`worktree`, `keepWorktree`).
149
+
150
+ ### 7. Session Integration & Permissions
151
+ * **Per-task permission presets** — `default`, `read-only`, `workspace-write`, or `full` are applied to the task's agent session before the prompt runs.
152
+ * **Session auto-archive** — isolated cron sessions are archived after each run (best-effort) so they do not clutter the chat list.
153
+ * **History → session navigation** — every LLM run records its session; open it straight from the run history entry.
154
+
155
+ ### 8. Notification Channels & Message Templates
156
+ A finished run is delivered to every channel configured for the task — Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, email (SMTP), voice via `dsh-tts`, and Gitea issues:
157
+
158
+ * **Per-task channels** — tick the channels in the task form; an explicit selection overrides the legacy `notifyTelegram` / `kanbanMode` switches, and an empty selection falls back to them.
159
+ * **Failure isolation** — one unreachable channel is reported in the scheduler log with the other channels still delivered; a broken webhook never swallows the rest of the report.
160
+ * **Message templates** — a global template, per-channel overrides, or a per-task template rendered from `{title} {id} {status} {output} {error} {duration} {schedule} {time} {tokens} {cost}`. Unknown placeholders are left intact, failed runs default to a failure template.
161
+ * **`onlyOnFailure`** — globally or per task, clean runs stay silent and only `error`/`timeout` runs are dispatched.
162
+ * **Credentials by reference** — webhook tokens, SMTP passwords and the Telegram bot token are entered as the NAME of a DSH credential (`botTokenRef`, `ntfyTokenRef`, `pushplusTokenRef`, `smtpPasswordRef`, `giteaTokenRef`); the value is resolved at send time through the DSH credentials service with an environment-variable fallback, and never travels through plugin settings. Webhook URLs and the Bark device key do embed a secret, so they are stored in the plugin settings file but are always returned masked to the browser and a masked value echoed back by the UI never overwrites the stored one.
163
+ * **Delivery timeout** — every channel request is bounded (`deliveryTimeoutMs`, default 15000 ms, editable in the settings panel or `settings.yaml`) and channels are dispatched concurrently, so one unresponsive endpoint is recorded as a failure and cannot delay the other channels or the next scheduled tick. The bound is enforced around the whole channel handler, which also covers credential resolution and the SMTP transport (`connectionTimeout`/`greetingTimeout`/`socketTimeout`), none of which support abort signals.
164
+ * **Telegram** — Markdown report with status badges (✅ / ❌), duration, schedule description and monospace output; dynamic values are escaped so odd titles cannot break the message. Credentials may be entered directly, or inherited from the `dsh-messenger-gateway` section of your DSH `settings.yaml` (best-effort fallback).
165
+ * **Discord / Slack** — webhook delivery; Discord carries an embed coloured by run status, Slack a plain text body.
166
+ * **ntfy / Bark / PushPlus** — mobile push with a topic/device key and an optional bearer token; the Bark title and text travel in the request path.
167
+ * **Email** — SMTP with host, port, TLS, user, `smtpFrom` and a comma-separated recipient list; requires `nodemailer` in the harness runtime and reports a clear error when it is missing. The transport inherits the delivery deadline, so a stalled SMTP server cannot hold the run.
168
+ * **Voice** — `dsh-tts` speaks the report through its HTTP route (`ttsBaseUrl`, default `http://127.0.0.1:3080`).
169
+ * **Gitea** — opens an issue with the run report (`giteaBaseUrl`, `giteaRepo`, token credential); failures are labelled `cron`, `bug`, `alert`.
134
170
  * **Test dispatch button** — verify Telegram connectivity on the spot before scheduling critical jobs.
135
171
 
136
- ### 6. Kanban Integration & Cost Meter
172
+ ### 9. Kanban Integration & Cost Meter
137
173
  * **Automatic Kanban cards** — with `kanbanMode` set to `on_failure` or `always`, the plugin creates cards in `dsh-kanban` (`on_failure` → *Backlog* on `error`/`timeout`; `always` → *Done*/*Backlog* on completion).
138
174
  * **Token & execution cost meter** — token consumption (input, output, cache reads) is tracked per run and per task, with USD estimates from a built-in pricing table and an aggregated analytics bar.
139
175
 
140
- ### 7. Overlap Policies & Execution Timeout
176
+ ### 10. Overlap Policies & Execution Timeout
141
177
  Prevent rogue processes from stacking concurrent duplicate executions:
142
178
 
143
179
  * **Execution timeout (`timeoutSeconds`)** — when the limit is reached, shell subprocesses are killed immediately via the abort signal and agent sessions are disposed so they stop consuming tokens. Default: `1800` (30 minutes).
@@ -148,6 +184,10 @@ Prevent rogue processes from stacking concurrent duplicate executions:
148
184
 
149
185
  If the daemon was offline at a scheduled time, the run is recorded as `missed` on startup, so gaps in the history stay visible.
150
186
 
187
+ ### 11. Heartbeat Monitoring (#16-style dead man's switch)
188
+ * Set `heartbeatUrl` and `heartbeatIntervalSec` in the plugin settings and the scheduler pings that URL on schedule — an external monitor alerts when the pings stop.
189
+ * A built-in `GET /dsh-cron/heartbeat` endpoint reports liveness, active task count and the last run time for your own watchdogs.
190
+
151
191
  ---
152
192
 
153
193
  ## 📦 Installation
@@ -174,6 +214,35 @@ dsh-cron:
174
214
  notifyTelegram: false # deliver reports for every task globally
175
215
  onlyOnFailure: false # deliver reports only for failed runs
176
216
  kanbanBaseUrl: "http://127.0.0.1:3000" # dsh-kanban HTTP API base URL
217
+ defaultTimezone: "" # default IANA time zone for schedules (empty = server local)
218
+ maxConcurrent: 0 # max parallel task runs (0 = unlimited)
219
+ heartbeatUrl: "" # dead man's snitch URL pinged on the heartbeat interval
220
+ heartbeatIntervalSec: 0 # heartbeat ping interval in seconds (0 = off)
221
+ # --- delivery channels ---
222
+ botTokenRef: "" # credential NAME for the Telegram bot token
223
+ template: "" # global message template, e.g. "⏰ {title} — {status}"
224
+ channelTemplates: {} # per-channel template overrides keyed by channel id
225
+ deliveryTimeoutMs: 15000 # per-channel delivery timeout; slow channel = failure, others unaffected
226
+ discordWebhookUrl: "" # Discord webhook
227
+ slackWebhookUrl: "" # Slack incoming webhook
228
+ ntfyUrl: "https://ntfy.sh" # ntfy server; ntfyTopic / ntfyTokenRef
229
+ ntfyTopic: ""
230
+ ntfyTokenRef: ""
231
+ barkServerUrl: "https://api.day.app" # Bark server; barkKey = device key
232
+ barkKey: ""
233
+ pushplusUrl: "https://www.pushplus.plus/send" # pushplusTokenRef
234
+ pushplusTokenRef: ""
235
+ smtpHost: "" # smtpPort / smtpSecure / smtpUser / smtpFrom / smtpTo
236
+ smtpPort: 587
237
+ smtpSecure: false
238
+ smtpUser: ""
239
+ smtpPasswordRef: "" # credential NAME for the SMTP password
240
+ smtpFrom: ""
241
+ smtpTo: ""
242
+ ttsBaseUrl: "http://127.0.0.1:3080" # dsh-tts base URL
243
+ giteaBaseUrl: "" # giteaRepo = owner/repo, giteaTokenRef = credential NAME
244
+ giteaRepo: ""
245
+ giteaTokenRef: ""
177
246
  ```
178
247
 
179
248
  ### Configuration Parameters
@@ -185,6 +254,21 @@ dsh-cron:
185
254
  | `notifyTelegram` | `boolean` | `false` | Global switch: deliver run reports to Telegram |
186
255
  | `onlyOnFailure` | `boolean` | `false` | Global switch: deliver reports only for `error`/`timeout` runs |
187
256
  | `kanbanBaseUrl` | `string` | `"http://127.0.0.1:3000"` | Base URL of the `dsh-kanban` HTTP API used for automatic card creation |
257
+ | `defaultTimezone` | `string` | `""` | Default IANA time zone for task schedules; empty = server local time |
258
+ | `maxConcurrent` | `number` | `0` | Cap on parallel task runs; extra runs are recorded as `skipped` (0 = unlimited) |
259
+ | `heartbeatUrl` | `string` | `""` | Dead man's snitch URL pinged every `heartbeatIntervalSec` while the scheduler is alive |
260
+ | `heartbeatIntervalSec` | `number` | `0` | Heartbeat ping interval in seconds (0 = disabled) |
261
+ | `botTokenRef` | `string` | `""` | Name of the DSH credential holding the Telegram bot token; resolved at send time (falls back to `botToken`, then the messenger-gateway settings, then the `CRON_TELEGRAM_BOT_TOKEN` environment variable) |
262
+ | `template` | `string` | `""` | Global message template with `{title}`/`{status}`/`{duration}`/… placeholders; empty = built-in text |
263
+ | `channelTemplates` | `object` | `{}` | Per-channel template overrides keyed by channel id (`telegram`, `discord`, …) |
264
+ | `deliveryTimeoutMs` | `number` | `15000` | Per-channel delivery timeout; a slower endpoint is recorded as a delivery failure and does not delay the other channels or the next tick |
265
+ | `discordWebhookUrl` / `slackWebhookUrl` | `string` | `""` | Webhook URLs for the Discord and Slack channels |
266
+ | `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | ntfy server, topic and an optional token credential name (sent as `Authorization: Bearer …`) |
267
+ | `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Bark server and device key (key, title and text travel in the request path) |
268
+ | `pushplusUrl` / `pushplusTokenRef` | `string` | `"https://www.pushplus.plus/send"` / `""` | PushPlus endpoint (override for a self-hosted proxy) and token credential name |
269
+ | `smtpHost` / `smtpPort` / `smtpSecure` / `smtpUser` / `smtpPasswordRef` / `smtpFrom` / `smtpTo` | `string`/`number`/`boolean` | `""` / `587` / `false` / `""` / `""` / `""` / `""` | Email channel; the password is referenced by credential name and email requires `nodemailer` in the harness runtime |
270
+ | `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | Base URL of the `dsh-tts` plugin used for voice announcements |
271
+ | `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Gitea channel: base URL, `owner/repo`, and the credential name of the API token |
188
272
 
189
273
  Notes:
190
274
 
@@ -207,12 +291,13 @@ All endpoints are served by the DSH web server under `/dsh-cron/`. Read endpoint
207
291
  | `POST` | `/dsh-cron/tasks/:id/pause` | Pause the schedule |
208
292
  | `POST` | `/dsh-cron/tasks/:id/resume` | Resume the schedule |
209
293
  | `POST` | `/dsh-cron/tasks/:id/toggle` | Toggle active/paused |
210
- | `PATCH` | `/dsh-cron/tasks/:id` | Partial update (whitelisted fields only: `title`, `schedule`, `prompt`, `type`, `delivery`, `provider`, `model`, notification/timeout/overlap/kanban settings, `status`, `oneShot`) |
294
+ | `PATCH` | `/dsh-cron/tasks/:id` | Partial update (whitelisted fields only: `title`, `schedule`, `prompt`, `type`, `delivery`, `provider`, `model`, runtime settings, `channels`, `template`, notification/timeout/overlap/kanban settings, `status`, `oneShot`) |
211
295
  | `DELETE` | `/dsh-cron/tasks/:id` | Delete the task |
212
296
  | `GET` | `/dsh-cron/models` | List LLM providers; `?provider=<id>` lists models |
213
297
  | `POST` | `/dsh-cron/chat/start` | Start a "Create with DSH" agent session with the task-setup instructions |
214
298
  | `GET` | `/dsh-cron/settings` | Client-safe settings (token masked) |
215
- | `POST` | `/dsh-cron/settings` | Update integration settings |
299
+ | `POST` | `/dsh-cron/settings` | Update integration settings (applied through the settings service) |
300
+ | `GET` | `/dsh-cron/heartbeat` | Liveness probe: active task count, last run time, server time |
216
301
  | `POST` | `/dsh-cron/telegram/test` | Send a Telegram test message |
217
302
  | `POST` | `/dsh-cron/kanban/test` | Create a Kanban connectivity-test card |
218
303
  | `*` | `/dsh-cron/action/:id/:action` | Legacy alias for the task action routes (`run`, `toggle`, `delete`, `history`) |
package/docs/README.ru.md CHANGED
@@ -35,6 +35,8 @@
35
35
  2. **Интерактивный сценарий «Создать с DSH»** — опишите задачу словами, агент уточнит детали и оформит расписание.
36
36
  3. **Автономный tool calling** — нативные инструменты `cron_*` позволяют агентам планировать собственные последующие действия прямо в диалоге.
37
37
  4. **Надёжный планировщик и атомарное хранилище** — на базе `croner`: интервалы, разовые задачи с задержкой, атомарная запись, история запусков, учёт стоимости.
38
+ 5. **Шесть рантаймов исполнения** — shell, Node.js, Python, HTTP/webhook, удалённый SSH и Docker, плюс переменные окружения на задачу, привязка workspace и изолированные git worktree для изменяющих код агентских задач.
39
+ 6. **Многоканальная доставка с шаблонами** — один запуск расходится в Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, email, голос (`dsh-tts`) и Gitea, с шаблонами сообщений `{переменные}` и секретами по имени credential в DSH.
38
40
 
39
41
  ---
40
42
 
@@ -46,8 +48,8 @@ graph TD
46
48
  SidebarBtn["Кнопка-часы в сайдбаре<br/>(слот DSH Client UI)"]
47
49
  Overlay["Панель управления задачами<br/>(табы: Все, Активные, На паузе, Завершённые)"]
48
50
  CreateWithDSH["Диалог «Создать с DSH»<br/>(задача на естественном языке)"]
49
- ManualForm["Ручная форма задачи<br/>(cron, таймаут, overlap, модель)"]
50
- SettingsCard["Карточка настроек<br/>(интеграции Telegram / Kanban)"]
51
+ ManualForm["Ручная форма задачи<br/>(рантайм, cron, таймаут, overlap, каналы)"]
52
+ SettingsCard["Карточка настроек<br/>(каналы, шаблоны, credentials)"]
51
53
  end
52
54
 
53
55
  subgraph Server ["Серверная часть (Cordis и сервисы DSH)"]
@@ -56,7 +58,9 @@ graph TD
56
58
  Scheduler["Движок TaskScheduler<br/>(экземпляры Croner + таймеры one-shot)"]
57
59
  Store["Атомарный TaskStore<br/>(tasks.json, атомарная запись)"]
58
60
  AgentRunner["Диспетчер агентских сессий<br/>(запуск промпта выбранной моделью)"]
59
- Notify["Доставка<br/>(Telegram Bot API, карточки dsh-kanban)"]
61
+ Runtimes["Рантаймы исполнения<br/>(shell, node, python, http, ssh, docker)"]
62
+ Notify["Маршрутизатор доставки<br/>(шаблоны + 10 каналов)"]
63
+ Secrets["Credential-ссылки<br/>(DSH credentials / ENV)"]
60
64
  end
61
65
 
62
66
  SidebarBtn --> Overlay
@@ -94,7 +98,7 @@ graph TD
94
98
 
95
99
  | Инструмент | Описание |
96
100
  |:---|:---|
97
- | `cron_create_task` | Создаёт задачу: `title`, `schedule`, `prompt`, опционально `type` (`llm`/`script`), `delivery`, `provider`, `model`, `notifyTelegram`, `onlyOnFailure`, `timeoutSeconds`, `overlapPolicy`, `kanbanMode` |
101
+ | `cron_create_task` | Создаёт задачу: `title`, `schedule`, `prompt`, опционально `type` (`llm`/`script`/`node`/`python`/`http`/`ssh`/`docker`/`skill`/`workflow`), `delivery`, `provider`, `model`, `channels`, `template`, `notifyTelegram`, `onlyOnFailure`, `timeoutSeconds`, `overlapPolicy`, `kanbanMode` |
98
102
  | `cron_schedule_task` | Псевдоним `cron_create_task` для совместимости с существующими промптами |
99
103
  | `cron_list_tasks` | Список задач со статусами, временем следующего запуска, токенами и стоимостью |
100
104
  | `cron_pause_task` | Приостанавливает расписание без удаления конфигурации |
@@ -121,22 +125,54 @@ cron_create_task({
121
125
  * `*/15 * * * *` — каждые 15 минут
122
126
  * `0 0 * * 0` — каждое воскресенье в полночь
123
127
  * `every 10m` / `every 2h` / `every 30s` — естественные интервалы
124
- * алиасы `daily` / `hourly` / `weekdays` (`weekdays` → `0 9 * * 1-5`)
128
+ * алиасы `daily` / `hourly` / `weekdays`, а также стандартные `@hourly` / `@daily` / `@weekly` / `@monthly` / `@yearly` и `@every 30m`
129
+ * **Часовые пояса** — для задачи можно указать IANA-зону (например, `Europe/Berlin`); без неё расписание живёт в серверном времени
125
130
  * **Разовые задачи**: `at: 2026-09-05T15:00:00Z` (точный ISO-таймстемп) или относительные задержки `in 20m` / `in 2h` (принимаются и русские варианты вроде `через 15 минут`). После единственного запуска задача автоматически переходит в `completed` и отображается на табе **Завершённые**.
126
131
 
127
- ### 5. Уведомления Telegram
128
- Прямая интеграция с Telegram Bot API доставляет отчёты о запусках и трейсы ошибок в мессенджер:
129
-
130
- * **Автоматические или собственные креденшелы** — введите свои `botToken` и `chatId` в диалоге настроек или позвольте плагину унаследовать значения по умолчанию из секции `dsh-messenger-gateway` вашего DSH `settings.yaml` (best-effort).
131
- * **Режим «только при сбоях»** — включите `onlyOnFailure` глобально или для отдельной задачи. Успешные запуски молчат; сбои (статусы `error` и `timeout`) отправляют алерт с трейсом.
132
- * **Markdown-оформление** — статусные значки (✅ / ❌), длительность, описание расписания и monospace-блоки вывода; динамические значения экранируются, поэтому нестандартные символы в названии не сломают сообщение.
132
+ ### 5. Надёжность исполнения
133
+ * **Автоповторы** — `maxRetries` и база `retryBackoffMs` на задачу: упавшие запуски (error/timeout) повторяются с экспоненциальной задержкой, счётчик сбрасывается после успеха.
134
+ * **Misfire-политики** — что делать с пропущенным за время простоя запуском: `skip` (по умолчанию — записать пропуск), `runOnce` (выполнить один раз с опозданием) или `catchUpAll` (выполнить и зафиксировать пропуск). Пропущенный one-shot при `skip` уходит в `completed` без выполнения.
135
+ * **Лимит параллельности** — настройка `maxConcurrent` ограничивает число одновременных запусков; лишние помечаются `skipped` с причиной.
136
+ * **Живой индикатор** — в списке задач пульсирует статус и идёт таймер текущего запуска.
137
+
138
+ ### 6. Рантаймы исполнения
139
+ Каждая задача выбирает собственный рантайм; не-LLM рантаймы не используют модель и не тратят токены:
140
+
141
+ * **Shell** (`script`) — команда или скрипт через shell харнесса, с `env` и `cwd`.
142
+ * **Node.js** (`node`) и **Python** (`python`) — запуск сниппета с указанием интерпретатора (`nodePath`, `pythonPath`); для Python определяется виртуальное окружение проекта.
143
+ * **HTTP** (`http`) — GET/POST/… по URL с собственными заголовками и телом; статус и вывод ответа попадают в историю запуска.
144
+ * **SSH** (`ssh`) — выполнение команды на удалённом хосте через профиль `dsh-remote-workspace` (`sshProfileId`) или отдельные поля host/key.
145
+ * **Docker** (`docker`) — выполнение команды в контейнере образа (`dockerImage`).
146
+ * **Переменные окружения** — карта `env` на задачу (в UI — строки KEY VALUE) для внешних рантаймов; секретам здесь не место.
147
+ * **Workspace и worktree** — привязка задачи к workspace харнесса (`workspaceId`) и, для изменяющих код агентских задач, запуск в изолированном git worktree (`worktree`, `keepWorktree`).
148
+
149
+ ### 7. Интеграция сессий и права
150
+ * **Permission-пресеты на задачу** — `default`, `read-only`, `workspace-write` или `full` применяются к сессии агента перед запуском промпта.
151
+ * **Автоархивация сессий** — изолированные cron-сессии архивируются после запуска (best-effort), не засоряя список чатов.
152
+ * **История → сессия** — каждый LLM-запуск хранит свою сессию; открыть диалог можно прямо из записи истории.
153
+
154
+ ### 8. Каналы доставки и шаблоны сообщений
155
+ Отчёт о завершённом запуске уходит во все каналы, выбранные для задачи — Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, email (SMTP), голос через `dsh-tts` и issue в Gitea:
156
+
157
+ * **Каналы на задачу** — отметьте каналы в форме задачи; явный выбор перекрывает legacy-переключатели `notifyTelegram`/`kanbanMode`, а пустой выбор возвращается к ним.
158
+ * **Изоляция сбоев** — недоступный канал фиксируется в логе планировщика, остальные каналы получают отчёт; сломанный webhook не поглощает доставку целиком.
159
+ * **Шаблоны сообщений** — глобальный шаблон, переопределения по каналам или шаблон на задачу с переменными `{title} {id} {status} {output} {error} {duration} {schedule} {time} {tokens} {cost}`. Неизвестные плейсхолдеры остаются как есть, для сбойных запусков по умолчанию используется шаблон ошибки.
160
+ * **`onlyOnFailure`** — глобально или на задачу: успешные запуски молчат, уходят только `error`/`timeout`.
161
+ * **Креденшелы по ссылке** — токены webhook'ов, пароль SMTP и токен Telegram вводятся как ИМЯ credential в DSH (`botTokenRef`, `ntfyTokenRef`, `pushplusTokenRef`, `smtpPasswordRef`, `giteaTokenRef`); значение резолвится в момент отправки через credentials-сервис DSH с фолбэком на переменную окружения и никогда не проходит через настройки плагина. Webhook-URL и ключ устройства Bark содержат секрет внутри, поэтому хранятся в настройках плагина, но всегда отдаются в браузер замаскированными, а замаскированное значение из UI никогда не перезаписывает сохранённое.
162
+ * **Таймаут доставки** — каждый запрос канала ограничен (`deliveryTimeoutMs`, по умолчанию 15000 мс, задаётся в панели настроек или `settings.yaml`), каналы отправляются параллельно: недоступный endpoint фиксируется как сбой и не задерживает остальные каналы и следующий тик расписания. Ограничение действует на весь обработчик канала, включая резолв credential'ов и SMTP-транспорт (`connectionTimeout`/`greetingTimeout`/`socketTimeout`), которые не поддерживают abort-сигнал.
163
+ * **Telegram** — Markdown-отчёт со статусными значками (✅ / ❌), длительностью, описанием расписания и monospace-блоком вывода; динамические значения экранируются. Креденшелы можно ввести напрямую или унаследовать из секции `dsh-messenger-gateway` вашего DSH `settings.yaml` (best-effort).
164
+ * **Discord / Slack** — доставка через webhook: Discord получает embed с цветом по статусу запуска, Slack — обычный текст.
165
+ * **ntfy / Bark / PushPlus** — мобильные пуши: тема/ключ устройства и опциональный bearer-токен; у Bark заголовок и текст идут в пути запроса, у PushPlus endpoint настраивается (self-hosted прокси).
166
+ * **Email** — SMTP с хостом, портом, TLS, пользователем, `smtpFrom` и списком получателей; требует `nodemailer` в рантайме харнесса и сообщает понятную ошибку, если его нет. Транспорт наследует дедлайн доставки, поэтому зависший SMTP-сервер не удерживает запуск.
167
+ * **Голос** — `dsh-tts` озвучивает отчёт через свой HTTP-маршрут (`ttsBaseUrl`, по умолчанию `http://127.0.0.1:3080`).
168
+ * **Gitea** — создаёт issue с отчётом (`giteaBaseUrl`, `giteaRepo`, credential токена); сбойные запуски помечаются метками `cron`, `bug`, `alert`.
133
169
  * **Кнопка проверки** — проверьте доставку в Telegram до запуска критичных задач.
134
170
 
135
- ### 6. Интеграция с Kanban и учёт стоимости
171
+ ### 9. Интеграция с Kanban и учёт стоимости
136
172
  * **Автоматические карточки Kanban** — при `kanbanMode` = `on_failure` или `always` плагин создаёт карточки в `dsh-kanban` (`on_failure` → *Backlog* при `error`/`timeout`; `always` → *Done*/*Backlog* по завершении).
137
173
  * **Счётчик токенов и стоимости** — потребление токенов (ввод, вывод, чтения из кэша) учитывается по запускам и задачам с оценкой в USD по встроенной таблице цен и сводной панелью аналитики.
138
174
 
139
- ### 7. Политики наложения и таймаут выполнения
175
+ ### 10. Политики наложения и таймаут выполнения
140
176
 
141
177
  * **Таймаут (`timeoutSeconds`)** — по достижении лимита shell-процесс немедленно завершается через abort-сигнал, а агентская сессия закрывается, чтобы не расходовать токены. По умолчанию `1800` (30 минут).
142
178
  * **Политика наложения (`overlapPolicy`)** — что делать, когда тик срабатывает при ещё активном предыдущем запуске:
@@ -170,6 +206,35 @@ dsh-cron:
170
206
  notifyTelegram: false # глобально отправлять отчёты о всех задачах
171
207
  onlyOnFailure: false # отправлять отчёты только при сбоях
172
208
  kanbanBaseUrl: "http://127.0.0.1:3000" # базовый URL HTTP API dsh-kanban
209
+ defaultTimezone: "" # IANA-зона по умолчанию (пусто = серверное время)
210
+ maxConcurrent: 0 # максимум параллельных запусков (0 = без лимита)
211
+ heartbeatUrl: "" # URL dead man's snitch, пингуется по интервалу
212
+ heartbeatIntervalSec: 0 # интервал heartbeat-пинга в секундах (0 = выключено)
213
+ # --- каналы доставки ---
214
+ botTokenRef: "" # ИМЯ credential для токена Telegram-бота
215
+ template: "" # глобальный шаблон сообщения, напр. "⏰ {title} — {status}"
216
+ channelTemplates: {} # переопределения шаблонов по каналам
217
+ deliveryTimeoutMs: 15000 # таймаут доставки на канал; медленный канал = сбой, остальные не ждут
218
+ discordWebhookUrl: "" # webhook Discord
219
+ slackWebhookUrl: "" # incoming webhook Slack
220
+ ntfyUrl: "https://ntfy.sh" # сервер ntfy; ntfyTopic / ntfyTokenRef
221
+ ntfyTopic: ""
222
+ ntfyTokenRef: ""
223
+ barkServerUrl: "https://api.day.app" # сервер Bark; barkKey — ключ устройства
224
+ barkKey: ""
225
+ pushplusUrl: "https://www.pushplus.plus/send" # pushplusTokenRef
226
+ pushplusTokenRef: ""
227
+ smtpHost: "" # smtpPort / smtpSecure / smtpUser / smtpFrom / smtpTo
228
+ smtpPort: 587
229
+ smtpSecure: false
230
+ smtpUser: ""
231
+ smtpPasswordRef: "" # ИМЯ credential для пароля SMTP
232
+ smtpFrom: ""
233
+ smtpTo: ""
234
+ ttsBaseUrl: "http://127.0.0.1:3080" # базовый URL dsh-tts
235
+ giteaBaseUrl: "" # giteaRepo = owner/repo, giteaTokenRef = ИМЯ credential
236
+ giteaRepo: ""
237
+ giteaTokenRef: ""
173
238
  ```
174
239
 
175
240
  ### Параметры
@@ -181,11 +246,26 @@ dsh-cron:
181
246
  | `notifyTelegram` | `boolean` | `false` | Глобальный выключатель доставки отчётов в Telegram |
182
247
  | `onlyOnFailure` | `boolean` | `false` | Глобальный режим «только при сбоях» (`error`/`timeout`) |
183
248
  | `kanbanBaseUrl` | `string` | `"http://127.0.0.1:3000"` | Базовый URL HTTP API `dsh-kanban` для автоматических карточек |
249
+ | `defaultTimezone` | `string` | `""` | IANA-зона по умолчанию для расписаний; пусто = серверное время |
250
+ | `maxConcurrent` | `number` | `0` | Лимит параллельных запусков; лишние помечаются `skipped` (0 = без лимита) |
251
+ | `heartbeatUrl` | `string` | `""` | URL dead man's snitch, пингуемый каждый `heartbeatIntervalSec`, пока жив планировщик |
252
+ | `heartbeatIntervalSec` | `number` | `0` | Интервал heartbeat-пинга в секундах (0 = выключено) |
253
+ | `botTokenRef` | `string` | `""` | Имя credential DSH с токеном Telegram-бота; резолвится при отправке (фолбэк: `botToken` → настройки messenger-gateway → переменная окружения `CRON_TELEGRAM_BOT_TOKEN`) |
254
+ | `template` | `string` | `""` | Глобальный шаблон сообщения с плейсхолдерами `{title}`/`{status}`/`{duration}`/…; пусто = встроенный текст |
255
+ | `channelTemplates` | `object` | `{}` | Переопределения шаблонов по каналам (`telegram`, `discord`, …) |
256
+ | `deliveryTimeoutMs` | `number` | `15000` | Таймаут доставки на канал; более медленный endpoint фиксируется как сбой и не задерживает остальные каналы и следующий тик |
257
+ | `discordWebhookUrl` / `slackWebhookUrl` | `string` | `""` | Webhook-URL каналов Discord и Slack |
258
+ | `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | Сервер ntfy, тема и опциональное имя credential токена (`Authorization: Bearer …`) |
259
+ | `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Сервер Bark и ключ устройства (ключ, заголовок и текст идут в пути запроса) |
260
+ | `pushplusUrl` / `pushplusTokenRef` | `string` | `"https://www.pushplus.plus/send"` / `""` | Endpoint PushPlus (переопределяется для self-hosted прокси) и имя credential токена |
261
+ | `smtpHost` / `smtpPort` / `smtpSecure` / `smtpUser` / `smtpPasswordRef` / `smtpFrom` / `smtpTo` | `string`/`number`/`boolean` | `""` / `587` / `false` / `""` / `""` / `""` / `""` | Канал email; пароль задаётся ссылкой на credential, для отправки нужен `nodemailer` в рантайме харнесса |
262
+ | `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | Базовый URL плагина `dsh-tts` для голосовых объявлений |
263
+ | `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Канал Gitea: базовый URL, `owner/repo` и имя credential API-токена |
184
264
 
185
265
  Примечания:
186
266
 
187
267
  * История запусков ограничена **50 записями на задачу** (фиксировано); в записи хранится до 4000 символов вывода.
188
- * Задачи выполняются в **локальном часовом поясе сервера**; cron-выражения вычисляет `croner` по часам хоста.
268
+ * Задачи выполняются в **локальном часовом поясе сервера**, если для задачи не указана своя IANA-зона; cron-выражения вычисляет `croner` по часам хоста.
189
269
  * Задачи сохраняются в каталоге данных DSH (`cron/tasks.json`) и переживают перезапуск; пропущенные one-shot запуски обнаруживаются при старте.
190
270
 
191
271
  ---
@@ -208,7 +288,8 @@ dsh-cron:
208
288
  | `GET` | `/dsh-cron/models` | Список LLM-провайдеров; `?provider=<id>` — модели |
209
289
  | `POST` | `/dsh-cron/chat/start` | Старт агентской сессии «Создать с DSH» с инструкциями планировщика |
210
290
  | `GET` | `/dsh-cron/settings` | Настройки для клиента (токен замаскирован) |
211
- | `POST` | `/dsh-cron/settings` | Обновление настроек интеграций |
291
+ | `POST` | `/dsh-cron/settings` | Обновление настроек интеграций (через службу настроек) |
292
+ | `GET` | `/dsh-cron/heartbeat` | Probe живости: число активных задач, время последнего запуска |
212
293
  | `POST` | `/dsh-cron/telegram/test` | Тестовое сообщение в Telegram |
213
294
  | `POST` | `/dsh-cron/kanban/test` | Тестовая карточка в Kanban |
214
295
  | `*` | `/dsh-cron/action/:id/:action` | Legacy-алиас действий над задачей (`run`, `toggle`, `delete`, `history`) |
package/docs/README.zh.md CHANGED
@@ -35,6 +35,8 @@
35
35
  2. **交互式“由 DSH 创建”流程** —— 与智能体对话,把高层需求转化为规范的定时任务。
36
36
  3. **自主工具调用** —— 原生 `cron_*` 工具让智能体在会话中自行安排后续执行。
37
37
  4. **健壮的调度器与原子存储** —— 基于 `croner`:间隔别名、一次性延时任务、原子写入、运行历史与成本追踪。
38
+ 5. **六种执行运行时** —— shell、Node.js、Python、HTTP/webhook、远程 SSH 与 Docker,并支持按任务的环境变量、工作区绑定以及面向代码修改任务的隔离 git worktree。
39
+ 6. **多渠道路由与模板** —— 一次运行可投递到 Telegram、dsh-kanban、Discord、Slack、ntfy、Bark、PushPlus、邮件、语音(`dsh-tts`)与 Gitea,支持 `{变量}` 消息模板与按 DSH 凭据名称引用的密钥。
38
40
 
39
41
  ---
40
42
 
@@ -46,8 +48,8 @@ graph TD
46
48
  SidebarBtn["侧边栏时钟按钮<br/>(DSH 客户端插槽)"]
47
49
  Overlay["任务管理面板<br/>(标签: 全部 / 活跃 / 暂停 / 已完成)"]
48
50
  CreateWithDSH["“由 DSH 创建”对话框<br/>(自然语言任务)"]
49
- ManualForm["手动任务表单<br/>(cron 表达式、超时、重叠策略、模型)"]
50
- SettingsCard["设置卡片<br/>(Telegram / Kanban 集成)"]
51
+ ManualForm["手动任务表单<br/>(运行时、cron、超时、重叠策略、渠道)"]
52
+ SettingsCard["设置卡片<br/>(渠道、模板、凭据)"]
51
53
  end
52
54
 
53
55
  subgraph Server ["服务端 (Cordis 与 DSH 服务)"]
@@ -56,7 +58,9 @@ graph TD
56
58
  Scheduler["TaskScheduler 引擎<br/>(Croner 实例 + one-shot 定时器)"]
57
59
  Store["原子 TaskStore<br/>(tasks.json 原子写入)"]
58
60
  AgentRunner["智能体会话调度器<br/>(以指定模型执行提示词)"]
59
- Notify["通知投递<br/>(Telegram Bot API、dsh-kanban 卡片)"]
61
+ Runtimes["执行运行时<br/>(shell、node、python、http、ssh、docker)"]
62
+ Notify["投递路由<br/>(模板 + 10 个渠道)"]
63
+ Secrets["凭据引用<br/>(DSH credentials / ENV)"]
60
64
  end
61
65
 
62
66
  SidebarBtn --> Overlay
@@ -94,7 +98,7 @@ graph TD
94
98
 
95
99
  | 工具 | 说明 |
96
100
  |:---|:---|
97
- | `cron_create_task` | 创建任务:`title`、`schedule`、`prompt`,可选 `type`(`llm`/`script`)、`delivery`、`provider`、`model`、`notifyTelegram`、`onlyOnFailure`、`timeoutSeconds`、`overlapPolicy`、`kanbanMode` |
101
+ | `cron_create_task` | 创建任务:`title`、`schedule`、`prompt`,可选 `type`(`llm`/`script`/`node`/`python`/`http`/`ssh`/`docker`/`skill`/`workflow`)、`delivery`、`provider`、`model`、`channels`、`template`、`notifyTelegram`、`onlyOnFailure`、`timeoutSeconds`、`overlapPolicy`、`kanbanMode` |
98
102
  | `cron_schedule_task` | `cron_create_task` 的别名,保持与既有提示词兼容 |
99
103
  | `cron_list_tasks` | 列出任务的状态、下次运行时间、token 总量与成本估算 |
100
104
  | `cron_pause_task` | 暂停调度而不删除配置 |
@@ -121,22 +125,54 @@ cron_create_task({
121
125
  * `*/15 * * * *` —— 每 15 分钟
122
126
  * `0 0 * * 0` —— 每周日午夜
123
127
  * `every 10m` / `every 2h` / `every 30s` —— 自然语言间隔
124
- * `daily` / `hourly` / `weekdays` 快捷方式
128
+ * `daily` / `hourly` / `weekdays` 快捷方式,以及标准 `@hourly` / `@daily` / `@weekly` / `@monthly` / `@yearly` 与 `@every 30m`
129
+ * **任务级时区** —— 可为任务设置 IANA 时区(如 `Europe/Berlin`);未设置时按服务器本地时间调度
125
130
  * **一次性任务**:`at: 2026-09-05T15:00:00Z`(精确 ISO 时间戳)或相对延时 `in 20m` / `in 2h`(也接受 `через 15 минут` 之类的俄语输入)。一次性任务在单次运行后自动转为 `completed`,显示在 **已完成** 标签下。
126
131
 
127
- ### 5. Telegram 通知与投递路由
128
- 通过与 Telegram Bot API 的直接集成,将执行报告与错误跟踪推送到你的即时通讯工具:
129
-
130
- * **自动获取或自定义凭据** —— 在设置对话框中输入自己的 `botToken` 与 `chatId`,或让插件从 DSH `settings.yaml` 的 `dsh-messenger-gateway` 段尽力继承默认值。
131
- * **仅失败时通知** —— 全局或按任务启用 `onlyOnFailure`。成功运行保持静默;失败(`error` 或 `timeout` 状态)会发送带错误跟踪的告警。
132
- * **Markdown 排版** —— 消息包含状态徽标(✅ / ❌)、耗时、调度描述与等宽输出块;动态值会被转义,特殊字符不会破坏消息。
132
+ ### 5. 执行可靠性
133
+ * **自动重试** —— 按任务设置 `maxRetries` 与基础 `retryBackoffMs`:失败(`error`/`timeout`)的运行按指数退避自动重试,成功后计数归零。
134
+ * **Misfire 策略** —— 选择守护进程离线期间错过的运行如何处理:`skip`(默认 —— 记录缺口)、`runOnce`(迟执行一次)或 `catchUpAll`(迟执行并记录缺口)。`skip` 下错过的一次性任务直接转为 `completed`,不再过期触发。
135
+ * **并发上限** —— 插件设置 `maxConcurrent` 限制并行运行数;超出的运行记录为 `skipped` 并附原因。
136
+ * **实时执行指示** —— 任务列表中的脉冲状态图标与运行计时器。
137
+
138
+ ### 6. 执行运行时
139
+ 每个任务可选择自己的运行时;非 LLM 运行时不需要模型,也不消耗 token:
140
+
141
+ * **Shell**(`script`)—— 通过 Harness shell 执行命令或脚本,支持 `env` 与 `cwd`。
142
+ * **Node.js**(`node`)与 **Python**(`python`)—— 指定解释器(`nodePath`、`pythonPath`)运行片段;Python 会自动识别项目虚拟环境。
143
+ * **HTTP**(`http`)—— 以自定义请求头与请求体访问 URL,状态码与响应写入运行历史。
144
+ * **SSH**(`ssh`)—— 通过 `dsh-remote-workspace` 配置(`sshProfileId`)或独立 host/key 字段在远程主机执行命令。
145
+ * **Docker**(`docker`)—— 在镜像容器(`dockerImage`)中执行命令。
146
+ * **环境变量** —— 按任务的 `env` 映射(界面中每行 KEY VALUE)应用于外部运行时;请勿在此存放密钥。
147
+ * **工作区与 worktree** —— 将任务绑定到 Harness 工作区(`workspaceId`);对会修改代码的智能体任务,可在隔离的 git worktree 中运行(`worktree`、`keepWorktree`)。
148
+
149
+ ### 7. 会话集成与权限
150
+ * **按任务的权限预设** —— `default`、`read-only`、`workspace-write` 或 `full` 在提示词执行前应用于任务会话。
151
+ * **会话自动归档** —— 隔离的 cron 会话在运行后自动归档(尽力而为),不干扰聊天列表。
152
+ * **历史 → 会话** —— 每次 LLM 运行都会记录会话,可直接从历史记录打开对话。
153
+
154
+ ### 8. 通知渠道与消息模板
155
+ 运行完成后,报告会发送到该任务配置的所有渠道 —— Telegram、dsh-kanban、Discord、Slack、ntfy、Bark、PushPlus、邮件(SMTP)、语音(`dsh-tts`)以及 Gitea issue:
156
+
157
+ * **按任务选择渠道** —— 在任务表单中勾选渠道;显式选择会覆盖旧版 `notifyTelegram`/`kanbanMode` 开关,留空则回退到它们。
158
+ * **故障隔离** —— 某个渠道不可用会记录在调度器日志中,其余渠道仍会收到报告;失效的 webhook 不会吞掉整份报告。
159
+ * **消息模板** —— 支持全局模板、按渠道覆盖或按任务模板,变量为 `{title} {id} {status} {output} {error} {duration} {schedule} {time} {tokens} {cost}`。未知占位符保持原样,失败运行默认使用失败模板。
160
+ * **`onlyOnFailure`** —— 全局或按任务生效:成功运行静默,仅发送 `error`/`timeout`。
161
+ * **凭据按名称引用** —— webhook token、SMTP 密码与 Telegram bot token 填写 DSH 凭据的名称(`botTokenRef`、`ntfyTokenRef`、`pushplusTokenRef`、`smtpPasswordRef`、`giteaTokenRef`),发送时通过 DSH credentials 服务解析,并可回退到环境变量,且绝不会经过插件设置。webhook URL 与 Bark 设备键本身内嵌密钥,因此保存在插件设置文件中,但返回浏览器时始终为掩码,界面回传的掩码值也不会覆盖已保存的值。
162
+ * **投递超时** —— 每个渠道请求都有上限(`deliveryTimeoutMs`,默认 15000 毫秒,可在设置面板或 `settings.yaml` 中调整),且各渠道并发发送:无响应的端点只记录为失败,不会拖慢其他渠道或下一次调度。限制作用于整个渠道处理过程,也覆盖凭据解析与 SMTP 传输(`connectionTimeout`/`greetingTimeout`/`socketTimeout`)——这些都不支持 abort 信号。
163
+ * **Telegram** —— 带状态徽标(✅ / ❌)、耗时、调度描述与等宽输出块的 Markdown 报告;动态值会被转义。凭据可直接填写,或从 DSH `settings.yaml` 的 `dsh-messenger-gateway` 段继承(尽力而为)。
164
+ * **Discord / Slack** —— 通过 webhook 投递:Discord 使用按运行状态着色的 embed,Slack 使用纯文本正文。
165
+ * **ntfy / Bark / PushPlus** —— 移动推送,支持主题/设备键与可选 bearer token;Bark 的标题与正文放在请求路径中,PushPlus 端点可指向自建代理。
166
+ * **邮件** —— SMTP(host、port、TLS、user、`smtpFrom` 与逗号分隔的收件人);需要 Harness 运行时安装 `nodemailer`,缺少时会给出明确错误。传输会继承投递截止时间,因此无响应的 SMTP 服务器不会拖住运行。
167
+ * **语音** —— `dsh-tts` 通过其 HTTP 路由朗读报告(`ttsBaseUrl`,默认 `http://127.0.0.1:3080`)。
168
+ * **Gitea** —— 创建包含运行报告的 issue(`giteaBaseUrl`、`giteaRepo`、token 凭据);失败运行标记为 `cron`、`bug`、`alert`。
133
169
  * **测试发送按钮** —— 在安排关键任务前现场验证 Telegram 连通性。
134
170
 
135
- ### 6. Kanban 集成与成本统计
171
+ ### 9. Kanban 集成与成本统计
136
172
  * **自动创建 Kanban 卡片** —— 当 `kanbanMode` 为 `on_failure` 或 `always` 时,插件在 `dsh-kanban` 中创建卡片(`on_failure` → `error`/`timeout` 时进入 *Backlog*;`always` → 完成后进入 *Done*/*Backlog*)。
137
173
  * **Token 与执行成本计量** —— 按运行与任务统计 token 消耗(输入、输出、缓存读取),基于内置价格表估算美元成本,并提供汇总分析栏。
138
174
 
139
- ### 7. 重叠策略与执行超时
175
+ ### 10. 重叠策略与执行超时
140
176
 
141
177
  * **执行超时(`timeoutSeconds`)** —— 达到限制后,shell 子进程通过 abort 信号立即终止,智能体会话被释放以停止消耗 token。默认 `1800`(30 分钟)。
142
178
  * **重叠策略(`overlapPolicy`)** —— 上一次运行尚未结束时再次触发调度时的行为:
@@ -146,6 +182,10 @@ cron_create_task({
146
182
 
147
183
  如果守护进程在计划时刻处于离线状态,启动时该次运行会被记录为 `missed`,历史空档始终可见。
148
184
 
185
+ ### 11. 心跳监控(Dead man's switch)
186
+ * 在插件设置中配置 `heartbeatUrl` 与 `heartbeatIntervalSec`,调度器会按间隔 GET 该地址 —— 外部监控可在心跳停止时告警。
187
+ * 内置 `GET /dsh-cron/heartbeat` 端点返回存活状态、活跃任务数与最近运行时间,便于自建看门狗。
188
+
149
189
  ---
150
190
 
151
191
  ## 📦 安装
@@ -170,6 +210,35 @@ dsh-cron:
170
210
  notifyTelegram: false # 全局投递所有任务的报告
171
211
  onlyOnFailure: false # 仅失败时投递报告
172
212
  kanbanBaseUrl: "http://127.0.0.1:3000" # dsh-kanban HTTP API 基础地址
213
+ defaultTimezone: "" # 默认 IANA 时区(空 = 服务器本地)
214
+ maxConcurrent: 0 # 最大并行运行数(0 = 不限)
215
+ heartbeatUrl: "" # 心跳上报 URL(dead man's snitch)
216
+ heartbeatIntervalSec: 0 # 心跳间隔秒数(0 = 关闭)
217
+ # --- 投递渠道 ---
218
+ botTokenRef: "" # Telegram bot token 的凭据名称
219
+ template: "" # 全局消息模板,例如 "⏰ {title} — {status}"
220
+ channelTemplates: {} # 按渠道覆盖模板
221
+ deliveryTimeoutMs: 15000 # 每个渠道的投递超时;慢端点记为失败,不影响其他渠道
222
+ discordWebhookUrl: "" # Discord webhook
223
+ slackWebhookUrl: "" # Slack incoming webhook
224
+ ntfyUrl: "https://ntfy.sh" # ntfy 服务器;ntfyTopic / ntfyTokenRef
225
+ ntfyTopic: ""
226
+ ntfyTokenRef: ""
227
+ barkServerUrl: "https://api.day.app" # Bark 服务器;barkKey = 设备键
228
+ barkKey: ""
229
+ pushplusUrl: "https://www.pushplus.plus/send" # pushplusTokenRef
230
+ pushplusTokenRef: ""
231
+ smtpHost: "" # smtpPort / smtpSecure / smtpUser / smtpFrom / smtpTo
232
+ smtpPort: 587
233
+ smtpSecure: false
234
+ smtpUser: ""
235
+ smtpPasswordRef: "" # SMTP 密码的凭据名称
236
+ smtpFrom: ""
237
+ smtpTo: ""
238
+ ttsBaseUrl: "http://127.0.0.1:3080" # dsh-tts 基础地址
239
+ giteaBaseUrl: "" # giteaRepo = owner/repo,giteaTokenRef = 凭据名称
240
+ giteaRepo: ""
241
+ giteaTokenRef: ""
173
242
  ```
174
243
 
175
244
  ### 配置参数
@@ -181,6 +250,21 @@ dsh-cron:
181
250
  | `notifyTelegram` | `boolean` | `false` | 全局开关:向 Telegram 投递运行报告 |
182
251
  | `onlyOnFailure` | `boolean` | `false` | 全局开关:仅对 `error`/`timeout` 运行投递报告 |
183
252
  | `kanbanBaseUrl` | `string` | `"http://127.0.0.1:3000"` | 用于自动卡片的 `dsh-kanban` HTTP API 基础地址 |
253
+ | `defaultTimezone` | `string` | `""` | 任务调度的默认 IANA 时区;空 = 服务器本地时间 |
254
+ | `maxConcurrent` | `number` | `0` | 并行运行上限;超出的运行记录为 `skipped`(0 = 不限) |
255
+ | `heartbeatUrl` | `string` | `""` | 心跳上报 URL,调度器存活期间按 `heartbeatIntervalSec` 间隔 GET |
256
+ | `heartbeatIntervalSec` | `number` | `0` | 心跳间隔秒数(0 = 关闭) |
257
+ | `botTokenRef` | `string` | `""` | 保存 Telegram bot token 的 DSH 凭据名称;发送时解析(回退顺序:`botToken` → messenger-gateway 设置 → 环境变量 `CRON_TELEGRAM_BOT_TOKEN`) |
258
+ | `template` | `string` | `""` | 带 `{title}`/`{status}`/`{duration}` 等占位符的全局消息模板;留空使用内置文本 |
259
+ | `channelTemplates` | `object` | `{}` | 按渠道 ID 覆盖模板(`telegram`、`discord` 等) |
260
+ | `deliveryTimeoutMs` | `number` | `15000` | 每个渠道的投递超时;超时的端点记为失败,不拖慢其他渠道或下一次调度 |
261
+ | `discordWebhookUrl` / `slackWebhookUrl` | `string` | `""` | Discord 与 Slack 渠道的 webhook 地址 |
262
+ | `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | ntfy 服务器、主题与可选的 token 凭据名称(以 `Authorization: Bearer …` 发送) |
263
+ | `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Bark 服务器与设备键(键、标题和正文位于请求路径中) |
264
+ | `pushplusUrl` / `pushplusTokenRef` | `string` | `"https://www.pushplus.plus/send"` / `""` | PushPlus 端点(可指向自建代理)与 token 凭据名称 |
265
+ | `smtpHost` / `smtpPort` / `smtpSecure` / `smtpUser` / `smtpPasswordRef` / `smtpFrom` / `smtpTo` | `string`/`number`/`boolean` | `""` / `587` / `false` / `""` / `""` / `""` / `""` | 邮件渠道;密码以凭据名称引用,发送需要 Harness 运行时安装 `nodemailer` |
266
+ | `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | 用于语音播报的 `dsh-tts` 基础地址 |
267
+ | `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Gitea 渠道:基础地址、`owner/repo` 与 API token 的凭据名称 |
184
268
 
185
269
  说明:
186
270
 
@@ -208,7 +292,8 @@ dsh-cron:
208
292
  | `GET` | `/dsh-cron/models` | 列出 LLM 提供方;`?provider=<id>` 列出模型 |
209
293
  | `POST` | `/dsh-cron/chat/start` | 启动带任务配置指令的“由 DSH 创建”智能体会话 |
210
294
  | `GET` | `/dsh-cron/settings` | 客户端安全设置(令牌掩码显示) |
211
- | `POST` | `/dsh-cron/settings` | 更新集成设置 |
295
+ | `POST` | `/dsh-cron/settings` | 更新集成设置(通过设置服务应用) |
296
+ | `GET` | `/dsh-cron/heartbeat` | 存活探针:活跃任务数与最近运行时间 |
212
297
  | `POST` | `/dsh-cron/telegram/test` | 发送 Telegram 测试消息 |
213
298
  | `POST` | `/dsh-cron/kanban/test` | 创建 Kanban 连通性测试卡片 |
214
299
  | `*` | `/dsh-cron/action/:id/:action` | 任务操作路由的兼容别名(`run`、`toggle`、`delete`、`history`) |