@goodandready/dsh-cron 0.2.3 → 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 |
@@ -132,24 +136,44 @@ Powered by `croner`, supporting standard 5-field cron expressions plus user-frie
132
136
  * **Concurrency limit** — `maxConcurrent` (plugin setting) caps parallel runs; extra runs are recorded as `skipped` with a reason.
133
137
  * **Live execution indicator** — the task list shows a pulsing status icon and a running timer for the task in flight.
134
138
 
135
- ### 6. Session Integration & Permissions
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
136
151
  * **Per-task permission presets** — `default`, `read-only`, `workspace-write`, or `full` are applied to the task's agent session before the prompt runs.
137
152
  * **Session auto-archive** — isolated cron sessions are archived after each run (best-effort) so they do not clutter the chat list.
138
153
  * **History → session navigation** — every LLM run records its session; open it straight from the run history entry.
139
154
 
140
- ### 5. Telegram Notifications & Delivery Routing
141
- Direct integration with the Telegram Bot API delivers execution reports and error traces straight to your messenger:
142
-
143
- * **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).
144
- * **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.
145
- * **Markdown formatting** — messages carry status badges (✅ / ❌), duration, schedule description, and monospace output blocks; dynamic values are escaped so odd titles cannot break the message.
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`.
146
170
  * **Test dispatch button** — verify Telegram connectivity on the spot before scheduling critical jobs.
147
171
 
148
- ### 6. Kanban Integration & Cost Meter
172
+ ### 9. Kanban Integration & Cost Meter
149
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).
150
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.
151
175
 
152
- ### 7. Overlap Policies & Execution Timeout
176
+ ### 10. Overlap Policies & Execution Timeout
153
177
  Prevent rogue processes from stacking concurrent duplicate executions:
154
178
 
155
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).
@@ -160,7 +184,7 @@ Prevent rogue processes from stacking concurrent duplicate executions:
160
184
 
161
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.
162
186
 
163
- ### 8. Heartbeat Monitoring (#16-style dead man's switch)
187
+ ### 11. Heartbeat Monitoring (#16-style dead man's switch)
164
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.
165
189
  * A built-in `GET /dsh-cron/heartbeat` endpoint reports liveness, active task count and the last run time for your own watchdogs.
166
190
 
@@ -194,6 +218,31 @@ dsh-cron:
194
218
  maxConcurrent: 0 # max parallel task runs (0 = unlimited)
195
219
  heartbeatUrl: "" # dead man's snitch URL pinged on the heartbeat interval
196
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: ""
197
246
  ```
198
247
 
199
248
  ### Configuration Parameters
@@ -209,6 +258,17 @@ dsh-cron:
209
258
  | `maxConcurrent` | `number` | `0` | Cap on parallel task runs; extra runs are recorded as `skipped` (0 = unlimited) |
210
259
  | `heartbeatUrl` | `string` | `""` | Dead man's snitch URL pinged every `heartbeatIntervalSec` while the scheduler is alive |
211
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 |
212
272
 
213
273
  Notes:
214
274
 
@@ -231,7 +291,7 @@ All endpoints are served by the DSH web server under `/dsh-cron/`. Read endpoint
231
291
  | `POST` | `/dsh-cron/tasks/:id/pause` | Pause the schedule |
232
292
  | `POST` | `/dsh-cron/tasks/:id/resume` | Resume the schedule |
233
293
  | `POST` | `/dsh-cron/tasks/:id/toggle` | Toggle active/paused |
234
- | `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`) |
235
295
  | `DELETE` | `/dsh-cron/tasks/:id` | Delete the task |
236
296
  | `GET` | `/dsh-cron/models` | List LLM providers; `?provider=<id>` lists models |
237
297
  | `POST` | `/dsh-cron/chat/start` | Start a "Create with DSH" agent session with the task-setup instructions |
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` | Приостанавливает расписание без удаления конфигурации |
@@ -131,24 +135,44 @@ cron_create_task({
131
135
  * **Лимит параллельности** — настройка `maxConcurrent` ограничивает число одновременных запусков; лишние помечаются `skipped` с причиной.
132
136
  * **Живой индикатор** — в списке задач пульсирует статус и идёт таймер текущего запуска.
133
137
 
134
- ### 6. Интеграция сессий и права
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. Интеграция сессий и права
135
150
  * **Permission-пресеты на задачу** — `default`, `read-only`, `workspace-write` или `full` применяются к сессии агента перед запуском промпта.
136
151
  * **Автоархивация сессий** — изолированные cron-сессии архивируются после запуска (best-effort), не засоряя список чатов.
137
152
  * **История → сессия** — каждый LLM-запуск хранит свою сессию; открыть диалог можно прямо из записи истории.
138
153
 
139
- ### 5. Уведомления Telegram
140
- Прямая интеграция с Telegram Bot API доставляет отчёты о запусках и трейсы ошибок в мессенджер:
141
-
142
- * **Автоматические или собственные креденшелы** — введите свои `botToken` и `chatId` в диалоге настроек или позвольте плагину унаследовать значения по умолчанию из секции `dsh-messenger-gateway` вашего DSH `settings.yaml` (best-effort).
143
- * **Режим «только при сбоях»** — включите `onlyOnFailure` глобально или для отдельной задачи. Успешные запуски молчат; сбои (статусы `error` и `timeout`) отправляют алерт с трейсом.
144
- * **Markdown-оформление** — статусные значки (✅ / ❌), длительность, описание расписания и monospace-блоки вывода; динамические значения экранируются, поэтому нестандартные символы в названии не сломают сообщение.
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`.
145
169
  * **Кнопка проверки** — проверьте доставку в Telegram до запуска критичных задач.
146
170
 
147
- ### 6. Интеграция с Kanban и учёт стоимости
171
+ ### 9. Интеграция с Kanban и учёт стоимости
148
172
  * **Автоматические карточки Kanban** — при `kanbanMode` = `on_failure` или `always` плагин создаёт карточки в `dsh-kanban` (`on_failure` → *Backlog* при `error`/`timeout`; `always` → *Done*/*Backlog* по завершении).
149
173
  * **Счётчик токенов и стоимости** — потребление токенов (ввод, вывод, чтения из кэша) учитывается по запускам и задачам с оценкой в USD по встроенной таблице цен и сводной панелью аналитики.
150
174
 
151
- ### 7. Политики наложения и таймаут выполнения
175
+ ### 10. Политики наложения и таймаут выполнения
152
176
 
153
177
  * **Таймаут (`timeoutSeconds`)** — по достижении лимита shell-процесс немедленно завершается через abort-сигнал, а агентская сессия закрывается, чтобы не расходовать токены. По умолчанию `1800` (30 минут).
154
178
  * **Политика наложения (`overlapPolicy`)** — что делать, когда тик срабатывает при ещё активном предыдущем запуске:
@@ -186,6 +210,31 @@ dsh-cron:
186
210
  maxConcurrent: 0 # максимум параллельных запусков (0 = без лимита)
187
211
  heartbeatUrl: "" # URL dead man's snitch, пингуется по интервалу
188
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: ""
189
238
  ```
190
239
 
191
240
  ### Параметры
@@ -201,6 +250,17 @@ dsh-cron:
201
250
  | `maxConcurrent` | `number` | `0` | Лимит параллельных запусков; лишние помечаются `skipped` (0 = без лимита) |
202
251
  | `heartbeatUrl` | `string` | `""` | URL dead man's snitch, пингуемый каждый `heartbeatIntervalSec`, пока жив планировщик |
203
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-токена |
204
264
 
205
265
  Примечания:
206
266
 
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` | 暂停调度而不删除配置 |
@@ -131,19 +135,44 @@ cron_create_task({
131
135
  * **并发上限** —— 插件设置 `maxConcurrent` 限制并行运行数;超出的运行记录为 `skipped` 并附原因。
132
136
  * **实时执行指示** —— 任务列表中的脉冲状态图标与运行计时器。
133
137
 
134
- ### 5. Telegram 通知与投递路由
135
- 通过与 Telegram Bot API 的直接集成,将执行报告与错误跟踪推送到你的即时通讯工具:
136
-
137
- * **自动获取或自定义凭据** —— 在设置对话框中输入自己的 `botToken` 与 `chatId`,或让插件从 DSH `settings.yaml` 的 `dsh-messenger-gateway` 段尽力继承默认值。
138
- * **仅失败时通知** —— 全局或按任务启用 `onlyOnFailure`。成功运行保持静默;失败(`error` 或 `timeout` 状态)会发送带错误跟踪的告警。
139
- * **Markdown 排版** —— 消息包含状态徽标(✅ / ❌)、耗时、调度描述与等宽输出块;动态值会被转义,特殊字符不会破坏消息。
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`。
140
169
  * **测试发送按钮** —— 在安排关键任务前现场验证 Telegram 连通性。
141
170
 
142
- ### 6. Kanban 集成与成本统计
171
+ ### 9. Kanban 集成与成本统计
143
172
  * **自动创建 Kanban 卡片** —— 当 `kanbanMode` 为 `on_failure` 或 `always` 时,插件在 `dsh-kanban` 中创建卡片(`on_failure` → `error`/`timeout` 时进入 *Backlog*;`always` → 完成后进入 *Done*/*Backlog*)。
144
173
  * **Token 与执行成本计量** —— 按运行与任务统计 token 消耗(输入、输出、缓存读取),基于内置价格表估算美元成本,并提供汇总分析栏。
145
174
 
146
- ### 7. 重叠策略与执行超时
175
+ ### 10. 重叠策略与执行超时
147
176
 
148
177
  * **执行超时(`timeoutSeconds`)** —— 达到限制后,shell 子进程通过 abort 信号立即终止,智能体会话被释放以停止消耗 token。默认 `1800`(30 分钟)。
149
178
  * **重叠策略(`overlapPolicy`)** —— 上一次运行尚未结束时再次触发调度时的行为:
@@ -153,7 +182,7 @@ cron_create_task({
153
182
 
154
183
  如果守护进程在计划时刻处于离线状态,启动时该次运行会被记录为 `missed`,历史空档始终可见。
155
184
 
156
- ### 8. 心跳监控(Dead man's switch)
185
+ ### 11. 心跳监控(Dead man's switch)
157
186
  * 在插件设置中配置 `heartbeatUrl` 与 `heartbeatIntervalSec`,调度器会按间隔 GET 该地址 —— 外部监控可在心跳停止时告警。
158
187
  * 内置 `GET /dsh-cron/heartbeat` 端点返回存活状态、活跃任务数与最近运行时间,便于自建看门狗。
159
188
 
@@ -185,6 +214,31 @@ dsh-cron:
185
214
  maxConcurrent: 0 # 最大并行运行数(0 = 不限)
186
215
  heartbeatUrl: "" # 心跳上报 URL(dead man's snitch)
187
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: ""
188
242
  ```
189
243
 
190
244
  ### 配置参数
@@ -200,6 +254,17 @@ dsh-cron:
200
254
  | `maxConcurrent` | `number` | `0` | 并行运行上限;超出的运行记录为 `skipped`(0 = 不限) |
201
255
  | `heartbeatUrl` | `string` | `""` | 心跳上报 URL,调度器存活期间按 `heartbeatIntervalSec` 间隔 GET |
202
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 的凭据名称 |
203
268
 
204
269
  说明:
205
270
 
@@ -3,7 +3,7 @@
3
3
  ## Product / Purpose
4
4
  - Назначение: Интегрированный планировщик cron-задач и фоновой автоматизации для DeepSeek Harness. Позволяет запускать агентские сессии по расписанию, выполнять автоматические проверки и предоставлять визуальный интерфейс управления задачами.
5
5
  - Аудитория: Пользователи и операторы DeepSeek Harness, автоматизирующие периодические процессы (утренние сводки, проверка тикетов, мониторинг серверов).
6
- - Статус: Active, публичный npm-пакет @goodandready/dsh-cron (текущая линия 0.1.x → 0.2.0).
6
+ - Статус: Active, публичный npm-пакет @goodandready/dsh-cron (линия 0.2.x).
7
7
 
8
8
  ## User Surfaces
9
9
  - Web/UI:
@@ -11,14 +11,14 @@
11
11
  - Кнопка вызова в боковой панели (sidebar-entry) рядом с новой сессией + иконка в шапке сессии (utilities slot).
12
12
  - Кнопка «Создать ⌄» с дропдауном:
13
13
  - 💬 «Создать с DSH» (запуск интерактивного диалога постановки задачи агенту).
14
- - ✏️ «Настроить вручную» (модальная форма: тип, расписание, таймаут, overlap, промпт, модель, уведомления).
14
+ - ✏️ «Настроить вручную» (модальная форма: тип и параметры рантайма, расписание, таймаут, overlap, промпт, модель, каналы доставки, шаблон сообщения).
15
15
  - Табы фильтрации: «Все», «Активные», «На паузе», «Завершённые».
16
16
  - Поисковая строка; сводная статистика (активные задачи, запуски, токены, стоимость).
17
17
  - Карточки задач: статус-переключатель, название, расписание (человекочитаемое + raw cron), действия (запуск, редактирование, удаление).
18
18
  - Блок «Рекомендуемые задачи»: готовые шаблоны (Daily digest, Weekly review, Follow-up monitor) в один клик.
19
19
  - Карточка настроек в слоте settings.plugin.item, key = namespace `dsh-cron`; отдельный раздел настроек не используется (#102).
20
- - LLM Tools: cron_create_task (+ alias cron_schedule_task), cron_list_tasks, cron_pause_task, cron_resume_task, cron_delete_task, cron_run_task.
21
- - API: HTTP эндпоинты /dsh-cron/* (tasks, models, chat/start, settings, telegram/test, kanban/test, tasks/:id/actions, legacy action/:id/:action). Мутирующие эндпоинты отклоняют cross-origin запросы; script-задачи по HTTP требуют заголовок x-dsh-cron-confirm.
20
+ - LLM Tools: cron_create_task (+ alias cron_schedule_task), cron_list_tasks, cron_pause_task, cron_resume_task, cron_delete_task, cron_run_task. Параметры задачи включают рантайм (`type`), `channels` (список каналов доставки) и `template` (шаблон сообщения).
21
+ - API: HTTP эндпоинты /dsh-cron/* (tasks, models, chat/start, settings, telegram/test, kanban/test, tasks/:id/actions, legacy action/:id/:action). Мутирующие эндпоинты отклоняют cross-origin запросы; script-задачи по HTTP требуют заголовок x-dsh-cron-confirm. Настройки доставки принимаются как плоские ключи (webhook URL, топики, поля SMTP, base URL) и `channelTemplates` — карта шаблонов по каналам.
22
22
  - Chat / Slash Commands: отсутствуют (ранее заявленные /cron-команды не были реализованы и удалены из документации; решение 2026-09-09).
23
23
 
24
24
  ## Visual Direction
@@ -42,8 +42,9 @@
42
42
  - CronSidebarButton: кнопка в левом сайдбаре DSH.
43
43
  - CronScreen: основной оверлей со списком, табами, статистикой и рекомендациями.
44
44
  - CreateDropdown: всплывающее меню выбора способа создания.
45
- - ManualTaskModal: модальная форма создания/редактирования (вкладки «Параметры» / «История запусков»).
46
- - SettingsModal: настройки Telegram/Kanban с тестами доставки.
45
+ - ManualTaskModal: модальная форма создания/редактирования (вкладки «Параметры» / «История запусков»); блок каналов доставки — сетка чекбоксов (Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, Email, Voice, Gitea) и поле шаблона сообщения с подсказкой по переменным.
46
+ - SettingsModal: настройки доставки с тестами; три сворачиваемые секции — «Credentials (references)», «Delivery channels», «Message templates» (шапка-кнопка, aria-expanded, шеврон).
47
+ - DeliverySettingsForm: общая форма настроек доставки, одна реализация для SettingsModal и CronSettingsCard; секреты вводятся только по имени credential-ссылки.
47
48
  - TaskItem: строка задачи с переключателем состояния и действиями.
48
49
  - RecommendationCard: плашка с готовым шаблоном.
49
50
  - CronSettingsCard: карточка параметров плагина в настройках; свёрнута по умолчанию, шапка-кнопка с шевроном разворачивает тело (aria-expanded), кнопка «Открыть панель задач» — внутри раскрытого тела (решение 2026-09-10, #100).
@@ -57,10 +58,15 @@
57
58
  ## User Flows
58
59
  1. Создание через DSH-чат: «напоминай каждый день в 9 утра...» → «Создать с DSH» → агент уточняет тип (LLM/NO-LLM), расписание, модель, Silent Rule → после подтверждения вызывает cron_create_task → задача появляется на экране.
59
60
  2. Создание вручную: кнопка сайдбара → «Создать ⌄» → «Настроить вручную» → форма → сохранение.
60
- 3. Выполнение по расписанию: croner/таймер one-shot → запуск shell-команды или изолированной агентской сессии → запись в историю → доставка отчёта (Telegram/Kanban по настройкам).
61
+ 3. Выполнение по расписанию: croner/таймер one-shot → запуск по выбранному рантайму (агентская сессия, shell, node, python, http, ssh, docker) → запись в историю → доставка отчёта в выбранные каналы (Telegram, Kanban, Discord, Slack, ntfy, Bark, PushPlus, Email, Voice, Gitea) с учётом `onlyOnFailure`.
61
62
  4. Разбор инцидента: история запусков в карточке задачи → статус, длительность, вывод/ошибка.
62
63
 
63
64
  ## Locked Design Decisions
65
+ - 2026-09-11 — Доставка вынесена в отдельный слой: сообщение рендерится шаблоном `{var}` (#25), транспорт — адаптеры каналов с чистым builder'ом payload и инжектируемым fetch, маршрутизатор собирает ошибки каналов и не роняет запуск (#26, #20–#23, #28, #47). Явно выбранные каналы задачи перекрывают legacy-флаги `notifyTelegram`/`kanbanMode`.
66
+ - 2026-09-11 — Доставка не может заблокировать планировщик: каждый канал ограничен таймаутом (`deliveryTimeoutMs`, по умолчанию 15 с), каналы отправляются параллельно, а ошибки (включая таймаут) собираются в `failures`. Причина: `protect: true` в croner пропускал бы следующие тики, пока висит незавершённая доставка (находка независимого review PR #114).
67
+ - 2026-09-11 — Значения с секретом внутри (webhook-URL Discord/Slack, ключ Bark) маскируются при отдаче в браузер, а замаскированное значение, вернувшееся от UI, не перезаписывает сохранённое; сырые credential-ключи отклоняются и в store, и на входе `/dsh-cron/settings`.
68
+ - 2026-09-11 — Секреты доставки хранятся только как credential-ссылки (#51): настройки содержат имя credential, значение резолвится в момент отправки через DSH credentials-сервис с фолбэком на ENV; store отказывается сохранять сырые secret-ключи.
69
+ - 2026-09-11 — Каталог данных плагина: `DSH_DATA_DIR` → `DSH_HOME/data` → `~/.dsh/data`. Причина: изолированный профиль не должен писать в чужой домашний каталог (issue #112, найдено на приёмке в тест-контуре).
64
70
  - 2026-09-09 — Пакет надёжности ядра (v0.1.24): буфер shell-задач 10МБ, атомарное сохранение с PID, аудит пропущенных запусков при рестарте (missed), фоновый поллинг UI (8с).
65
71
  - 2026-09-03 — Публичный скоуп @goodandready/dsh-cron; оверлей через mountSidebarEntry/mountScreen аналогично dsh-kanban; двойная кнопка «Создать ⌄».
66
72
  - 2026-09-09 — Слот карточки настроек: settings.plugin.item с key/namespace `dsh-cron` (совпадение с серверной регистрацией). Причина: контракт слота настроек (#85). Changed 2026-09-10 (#102): settings.section fallback удалён — карточка только во вкладке плагинов.