@goodandready/dsh-cron 0.2.4 → 0.2.6

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
@@ -31,12 +31,12 @@ Autonomous AI agents often need to perform recurring duties: generating daily mo
31
31
 
32
32
  **`@goodandready/dsh-cron`** is a native full-stack scheduling and background automation plugin for DeepSeek Harness. It bridges standard cron expressions and natural interval syntax with autonomous agent execution, providing:
33
33
 
34
- 1. **Rich Visual Task Manager** — a sidebar button and a full-featured panel to inspect, filter, pause, trigger, and create recurring tasks.
34
+ 1. **Rich Visual Task Manager** — a sidebar button with a collapsible list of active jobs (next run or live state, capped and persisted), plus a full panel to inspect, filter by type/model/channel, pause, trigger, duplicate, export/import and create tasks.
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
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.
39
+ 6. **Multi-Channel Delivery With Templates** — one run fans out to Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, voice (`dsh-tts`) and Gitea, with `{variable}` message templates and secrets referenced by DSH credential name.
40
40
 
41
41
  ---
42
42
 
@@ -59,7 +59,7 @@ graph TD
59
59
  Store["Atomic TaskStore<br/>(tasks.json with atomic write)"]
60
60
  AgentRunner["Agent Session Dispatcher<br/>(Executes prompt with chosen model)"]
61
61
  Runtimes["Execution Runtimes<br/>(shell, node, python, http, ssh, docker)"]
62
- Notify["Delivery Router<br/>(templates + 10 channels)"]
62
+ Notify["Delivery Router<br/>(templates + 9 channels)"]
63
63
  Secrets["Credential References<br/>(DSH credentials / ENV)"]
64
64
  end
65
65
 
@@ -99,13 +99,15 @@ Autonomous agents can manage schedules directly:
99
99
 
100
100
  | Tool | Description |
101
101
  |:---|:---|
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` |
102
+ | `cron_create_task` | Creates a scheduled task: `title`, `schedule`, `prompt`, `fallbackModel` (one retry on a stronger model when a run fails), optional `type` (`llm`/`script`/`node`/`python`/`http`/`ssh`/`docker`/`skill`/`workflow`), `delivery`, `provider`, `model`, `channels`, `template`, `notifyTelegram`, `onlyOnFailure`, `timeoutSeconds`, `overlapPolicy`, `kanbanMode` |
103
103
  | `cron_schedule_task` | Alias of `cron_create_task` kept for compatibility with existing agent prompts |
104
104
  | `cron_list_tasks` | Lists tasks with statuses, next run timestamps, token totals, and cost estimates |
105
105
  | `cron_pause_task` | Pauses a schedule without deleting its configuration |
106
106
  | `cron_resume_task` | Resumes a paused schedule |
107
107
  | `cron_delete_task` | Permanently removes a task and its history |
108
108
  | `cron_run_task` | Triggers an immediate out-of-band run |
109
+ | `cron_get_task` | Reads the full configuration of one task, including fields the list does not show |
110
+ | `cron_update_task` | Changes an existing task in place (whitelisted fields, same validation as the HTTP route); the model is told to confirm code-executing changes with the user first |
109
111
 
110
112
  Example invocation the model can make during a conversation:
111
113
 
@@ -147,33 +149,41 @@ Every task picks its own runtime; non-LLM runtimes need no model and consume no
147
149
  * **Environment variables** — a per-task `env` map (KEY VALUE per line in the UI) applied to external runtimes; secrets do not belong here.
148
150
  * **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
151
 
150
- ### 7. Session Integration & Permissions
152
+ ### 7. Cost Control: Fallback Model
153
+ A task can run on the cheap model by default and still finish on the strong one: set `fallbackModel` (and optionally `fallbackProvider`) and a failed run — `error` or `timeout` — is retried **once** on that model before the ordinary retry backoff applies. History records which model produced the result and whether the fallback was used, usage and cost of both attempts are summed, and the `{model}` template variable renders the model that finished the run. Only agent-mediated tasks (`llm`, `skill`, `workflow`) can use a fallback.
154
+
155
+ ### 8. Session Integration & Permissions
151
156
  * **Per-task permission presets** — `default`, `read-only`, `workspace-write`, or `full` are applied to the task's agent session before the prompt runs.
152
157
  * **Session auto-archive** — isolated cron sessions are archived after each run (best-effort) so they do not clutter the chat list.
153
158
  * **History → session navigation** — every LLM run records its session; open it straight from the run history entry.
154
159
 
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:
160
+ ### 9. Quiet by Rule
161
+ A task with output can carry a **silent rule** written in plain words ("stay silent when no filesystem is above 80%"). On a successful run a cheap model judges the output against that rule and the report is skipped when the verdict is to stay silent, with the reason recorded in the run history. It fails open: no rule, no model, a failed call or an unreadable answer all mean the report is delivered. `silentRuleModel` (plugin setting) picks the model used for the judgement.
162
+
163
+ ### 10. Failure Diagnosis
164
+ Agent tasks can ask for a diagnosis: with `inspectOnFailure` set, a failed run (`error` or `timeout`) is read by a model together with the task prompt and truncated output, and the run history stores a short diagnosis plus a concrete prompt change. The history entry offers to load that suggestion into the edit form — nothing is applied automatically. The model is configurable with `inspectorModel`, and `{diagnosis}` is available in message templates. A broken or unavailable model call leaves the failed run exactly as it was.
165
+
166
+ ### 11. Notification Channels & Message Templates
167
+ A finished run is delivered to every channel configured for the task — Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, voice via `dsh-tts`, and Gitea issues:
157
168
 
158
169
  * **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
170
  * **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
171
  * **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
172
  * **`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.
173
+ * **Credentials by reference** — webhook tokens and the Telegram bot token are entered as the NAME of a DSH credential (`botTokenRef`, `ntfyTokenRef`, `pushplusTokenRef`, `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.
174
+ * **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, which does not support abort signals.
164
175
  * **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
176
  * **Discord / Slack** — webhook delivery; Discord carries an embed coloured by run status, Slack a plain text body.
166
177
  * **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
178
  * **Voice** — `dsh-tts` speaks the report through its HTTP route (`ttsBaseUrl`, default `http://127.0.0.1:3080`).
169
179
  * **Gitea** — opens an issue with the run report (`giteaBaseUrl`, `giteaRepo`, token credential); failures are labelled `cron`, `bug`, `alert`.
170
180
  * **Test dispatch button** — verify Telegram connectivity on the spot before scheduling critical jobs.
171
181
 
172
- ### 9. Kanban Integration & Cost Meter
182
+ ### 12. Kanban Integration & Cost Meter
173
183
  * **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).
174
184
  * **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.
175
185
 
176
- ### 10. Overlap Policies & Execution Timeout
186
+ ### 13. Overlap Policies & Execution Timeout
177
187
  Prevent rogue processes from stacking concurrent duplicate executions:
178
188
 
179
189
  * **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).
@@ -184,7 +194,7 @@ Prevent rogue processes from stacking concurrent duplicate executions:
184
194
 
185
195
  If the daemon was offline at a scheduled time, the run is recorded as `missed` on startup, so gaps in the history stay visible.
186
196
 
187
- ### 11. Heartbeat Monitoring (#16-style dead man's switch)
197
+ ### 14. Heartbeat Monitoring (#16-style dead man's switch)
188
198
  * 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
199
  * A built-in `GET /dsh-cron/heartbeat` endpoint reports liveness, active task count and the last run time for your own watchdogs.
190
200
 
@@ -232,13 +242,6 @@ dsh-cron:
232
242
  barkKey: ""
233
243
  pushplusUrl: "https://www.pushplus.plus/send" # pushplusTokenRef
234
244
  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
245
  ttsBaseUrl: "http://127.0.0.1:3080" # dsh-tts base URL
243
246
  giteaBaseUrl: "" # giteaRepo = owner/repo, giteaTokenRef = credential NAME
244
247
  giteaRepo: ""
@@ -266,7 +269,6 @@ dsh-cron:
266
269
  | `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | ntfy server, topic and an optional token credential name (sent as `Authorization: Bearer …`) |
267
270
  | `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Bark server and device key (key, title and text travel in the request path) |
268
271
  | `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
272
  | `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | Base URL of the `dsh-tts` plugin used for voice announcements |
271
273
  | `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Gitea channel: base URL, `owner/repo`, and the credential name of the API token |
272
274
 
@@ -291,6 +293,10 @@ All endpoints are served by the DSH web server under `/dsh-cron/`. Read endpoint
291
293
  | `POST` | `/dsh-cron/tasks/:id/pause` | Pause the schedule |
292
294
  | `POST` | `/dsh-cron/tasks/:id/resume` | Resume the schedule |
293
295
  | `POST` | `/dsh-cron/tasks/:id/toggle` | Toggle active/paused |
296
+ | `POST` | `/dsh-cron/tasks/:id/duplicate` | Creates a paused copy of a task: configuration copied, run state (history, counters, last run) reset |
297
+ | `GET` | `/dsh-cron/recipes` | Built-in recipe catalog: ready-to-use monitoring presets grouped by category, all read-only |
298
+ | `GET` | `/dsh-cron/tasks/export` | Versioned JSON document with task configuration only — no history or counters. Channels reference credentials by name, but a task-level `env` map or HTTP headers you typed in yourself are part of the configuration and therefore appear in the file |
299
+ | `POST` | `/dsh-cron/tasks/import` | Validates a document and applies it with `add`, `replace` or `skip`; supports a `dryRun` summary. Imported tasks always start **paused**, so a restore never fires until reviewed |
294
300
  | `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`) |
295
301
  | `DELETE` | `/dsh-cron/tasks/:id` | Delete the task |
296
302
  | `GET` | `/dsh-cron/models` | List LLM providers; `?provider=<id>` lists models |
package/docs/README.ru.md CHANGED
@@ -31,12 +31,12 @@
31
31
 
32
32
  **`@goodandready/dsh-cron`** — нативный полноформатный плагин планирования и фоновой автоматизации для DeepSeek Harness. Он связывает стандартные cron-выражения и естественные интервалы с автономным исполнением агентами:
33
33
 
34
- 1. **Развитый визуальный менеджер задач** — кнопка в сайдбаре и полноценная панель: просмотр, фильтры, пауза, немедленный запуск, создание задач.
34
+ 1. **Развитый визуальный менеджер задач** — кнопка в сайдбаре со сворачиваемым списком активных задач (следующий запуск или живой статус, с ограничением и запоминанием состояния) и полноценная панель: фильтры по типу, модели и каналу, пауза, немедленный запуск, дублирование, экспорт/импорт и создание задач.
35
35
  2. **Интерактивный сценарий «Создать с DSH»** — опишите задачу словами, агент уточнит детали и оформит расписание.
36
36
  3. **Автономный tool calling** — нативные инструменты `cron_*` позволяют агентам планировать собственные последующие действия прямо в диалоге.
37
37
  4. **Надёжный планировщик и атомарное хранилище** — на базе `croner`: интервалы, разовые задачи с задержкой, атомарная запись, история запусков, учёт стоимости.
38
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.
39
+ 6. **Многоканальная доставка с шаблонами** — один запуск расходится в Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, голос (`dsh-tts`) и Gitea, с шаблонами сообщений `{переменные}` и секретами по имени credential в DSH.
40
40
 
41
41
  ---
42
42
 
@@ -59,7 +59,7 @@ graph TD
59
59
  Store["Атомарный TaskStore<br/>(tasks.json, атомарная запись)"]
60
60
  AgentRunner["Диспетчер агентских сессий<br/>(запуск промпта выбранной моделью)"]
61
61
  Runtimes["Рантаймы исполнения<br/>(shell, node, python, http, ssh, docker)"]
62
- Notify["Маршрутизатор доставки<br/>(шаблоны + 10 каналов)"]
62
+ Notify["Маршрутизатор доставки<br/>(шаблоны + 9 каналов)"]
63
63
  Secrets["Credential-ссылки<br/>(DSH credentials / ENV)"]
64
64
  end
65
65
 
@@ -98,13 +98,15 @@ graph TD
98
98
 
99
99
  | Инструмент | Описание |
100
100
  |:---|:---|
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` |
101
+ | `cron_create_task` | Создаёт задачу: `title`, `schedule`, `prompt`, `fallbackModel` (одна повторная попытка на сильной модели при сбое), опционально `type` (`llm`/`script`/`node`/`python`/`http`/`ssh`/`docker`/`skill`/`workflow`), `delivery`, `provider`, `model`, `channels`, `template`, `notifyTelegram`, `onlyOnFailure`, `timeoutSeconds`, `overlapPolicy`, `kanbanMode` |
102
102
  | `cron_schedule_task` | Псевдоним `cron_create_task` для совместимости с существующими промптами |
103
103
  | `cron_list_tasks` | Список задач со статусами, временем следующего запуска, токенами и стоимостью |
104
104
  | `cron_pause_task` | Приостанавливает расписание без удаления конфигурации |
105
105
  | `cron_resume_task` | Возобновляет приостановленное расписание |
106
106
  | `cron_delete_task` | Полностью удаляет задачу и её историю |
107
107
  | `cron_run_task` | Немедленный внеплановый запуск |
108
+ | `cron_get_task` | Полная конфигурация одной задачи, включая поля, которых нет в списке |
109
+ | `cron_update_task` | Изменяет существующую задачу на месте (whitelisted-поля, та же валидация, что у HTTP-маршрута); модели предписано сперва подтверждать с пользователем изменения, исполняющие код |
108
110
 
109
111
  Пример вызова модели в диалоге:
110
112
 
@@ -146,33 +148,42 @@ cron_create_task({
146
148
  * **Переменные окружения** — карта `env` на задачу (в UI — строки KEY VALUE) для внешних рантаймов; секретам здесь не место.
147
149
  * **Workspace и worktree** — привязка задачи к workspace харнесса (`workspaceId`) и, для изменяющих код агентских задач, запуск в изолированном git worktree (`worktree`, `keepWorktree`).
148
150
 
149
- ### 7. Интеграция сессий и права
151
+ ### 7. Экономия: fallback-модель
152
+ Задача может идти на дешёвой модели по умолчанию и всё же завершиться на сильной: задайте `fallbackModel` (и при необходимости `fallbackProvider`), и сбойный запуск (`error` или `timeout`) один раз повторится на этой модели, прежде чем включится обычный retry с задержкой. В истории видно, какая модель произвела результат и был ли использован fallback; расход и стоимость обеих попыток суммируются; переменная шаблона `{model}` подставляет модель, завершившую запуск. Fallback доступен только агентским типам (`llm`, `skill`, `workflow`).
153
+
154
+ ### 8. Интеграция сессий и права
150
155
  * **Permission-пресеты на задачу** — `default`, `read-only`, `workspace-write` или `full` применяются к сессии агента перед запуском промпта.
151
156
  * **Автоархивация сессий** — изолированные cron-сессии архивируются после запуска (best-effort), не засоряя список чатов.
152
157
  * **История → сессия** — каждый LLM-запуск хранит свою сессию; открыть диалог можно прямо из записи истории.
153
158
 
154
- ### 8. Каналы доставки и шаблоны сообщений
155
- Отчёт о завершённом запуске уходит во все каналы, выбранные для задачи — Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, email (SMTP), голос через `dsh-tts` и issue в Gitea:
159
+ ### 9. Тишина по правилу
160
+ У задачи с выводом может быть **правило тишины**, написанное словами («молчи, если ни один раздел не занят больше 80%»). На успешном запуске дешёвая модель сверяет вывод с правилом, и отчёт пропускается, если вердикт — молчать; причина сохраняется в истории запуска. Работает fail-open: нет правила, нет модели, сбой вызова или нечитаемый ответ — отчёт доставляется. Настройка `silentRuleModel` задаёт модель для проверки.
161
+
162
+ ### 10. Диагностика сбоев
163
+ Агентские задачи могут заказывать диагноз: с включённым `inspectOnFailure` сбойный запуск (`error` или `timeout`) вместе с промптом задачи и обрезанным выводом читает модель, и в историю запуска попадают короткий диагноз и конкретная правка промпта. В записи истории есть кнопка, подставляющая эту правку в форму редактирования — автоматически ничего не применяется. Модель задаётся настройкой `inspectorModel`, в шаблонах доступна переменная `{diagnosis}`. Недоступная модель оставляет сбойный запуск ровно таким, каким он был.
156
164
 
165
+ ### 11. Каналы доставки и шаблоны сообщений
166
+ Отчёт о завершённом запуске уходит во все каналы, выбранные для задачи — Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, голос через `dsh-tts` и issue в Gitea:
167
+
168
+ * **Перенос задач** — экспорт всей конфигурации в версионированный JSON и импорт с предварительной сводкой; импортированные задачи приходят на паузе.
157
169
  * **Каналы на задачу** — отметьте каналы в форме задачи; явный выбор перекрывает legacy-переключатели `notifyTelegram`/`kanbanMode`, а пустой выбор возвращается к ним.
158
170
  * **Изоляция сбоев** — недоступный канал фиксируется в логе планировщика, остальные каналы получают отчёт; сломанный webhook не поглощает доставку целиком.
159
171
  * **Шаблоны сообщений** — глобальный шаблон, переопределения по каналам или шаблон на задачу с переменными `{title} {id} {status} {output} {error} {duration} {schedule} {time} {tokens} {cost}`. Неизвестные плейсхолдеры остаются как есть, для сбойных запусков по умолчанию используется шаблон ошибки.
160
172
  * **`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-сигнал.
173
+ * **Креденшелы по ссылке** — токены webhook'ов и токен Telegram вводятся как ИМЯ credential в DSH (`botTokenRef`, `ntfyTokenRef`, `pushplusTokenRef`, `giteaTokenRef`); значение резолвится в момент отправки через credentials-сервис DSH с фолбэком на переменную окружения и никогда не проходит через настройки плагина. Webhook-URL и ключ устройства Bark содержат секрет внутри, поэтому хранятся в настройках плагина, но всегда отдаются в браузер замаскированными, а замаскированное значение из UI никогда не перезаписывает сохранённое.
174
+ * **Таймаут доставки** — каждый запрос канала ограничен (`deliveryTimeoutMs`, по умолчанию 15000 мс, задаётся в панели настроек или `settings.yaml`), каналы отправляются параллельно: недоступный endpoint фиксируется как сбой и не задерживает остальные каналы и следующий тик расписания. Ограничение действует на весь обработчик канала, включая резолв credential'ов, который не поддерживает abort-сигнал.
163
175
  * **Telegram** — Markdown-отчёт со статусными значками (✅ / ❌), длительностью, описанием расписания и monospace-блоком вывода; динамические значения экранируются. Креденшелы можно ввести напрямую или унаследовать из секции `dsh-messenger-gateway` вашего DSH `settings.yaml` (best-effort).
164
176
  * **Discord / Slack** — доставка через webhook: Discord получает embed с цветом по статусу запуска, Slack — обычный текст.
165
177
  * **ntfy / Bark / PushPlus** — мобильные пуши: тема/ключ устройства и опциональный bearer-токен; у Bark заголовок и текст идут в пути запроса, у PushPlus endpoint настраивается (self-hosted прокси).
166
- * **Email** — SMTP с хостом, портом, TLS, пользователем, `smtpFrom` и списком получателей; требует `nodemailer` в рантайме харнесса и сообщает понятную ошибку, если его нет. Транспорт наследует дедлайн доставки, поэтому зависший SMTP-сервер не удерживает запуск.
167
178
  * **Голос** — `dsh-tts` озвучивает отчёт через свой HTTP-маршрут (`ttsBaseUrl`, по умолчанию `http://127.0.0.1:3080`).
168
179
  * **Gitea** — создаёт issue с отчётом (`giteaBaseUrl`, `giteaRepo`, credential токена); сбойные запуски помечаются метками `cron`, `bug`, `alert`.
169
180
  * **Кнопка проверки** — проверьте доставку в Telegram до запуска критичных задач.
170
181
 
171
- ### 9. Интеграция с Kanban и учёт стоимости
182
+ ### 12. Интеграция с Kanban и учёт стоимости
172
183
  * **Автоматические карточки Kanban** — при `kanbanMode` = `on_failure` или `always` плагин создаёт карточки в `dsh-kanban` (`on_failure` → *Backlog* при `error`/`timeout`; `always` → *Done*/*Backlog* по завершении).
173
184
  * **Счётчик токенов и стоимости** — потребление токенов (ввод, вывод, чтения из кэша) учитывается по запускам и задачам с оценкой в USD по встроенной таблице цен и сводной панелью аналитики.
174
185
 
175
- ### 10. Политики наложения и таймаут выполнения
186
+ ### 13. Политики наложения и таймаут выполнения
176
187
 
177
188
  * **Таймаут (`timeoutSeconds`)** — по достижении лимита shell-процесс немедленно завершается через abort-сигнал, а агентская сессия закрывается, чтобы не расходовать токены. По умолчанию `1800` (30 минут).
178
189
  * **Политика наложения (`overlapPolicy`)** — что делать, когда тик срабатывает при ещё активном предыдущем запуске:
@@ -224,19 +235,16 @@ dsh-cron:
224
235
  barkKey: ""
225
236
  pushplusUrl: "https://www.pushplus.plus/send" # pushplusTokenRef
226
237
  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
238
  ttsBaseUrl: "http://127.0.0.1:3080" # базовый URL dsh-tts
235
239
  giteaBaseUrl: "" # giteaRepo = owner/repo, giteaTokenRef = ИМЯ credential
236
240
  giteaRepo: ""
237
241
  giteaTokenRef: ""
238
242
  ```
239
243
 
244
+ ### 14. Мониторинг heartbeat (dead man's switch)
245
+ * Задайте `heartbeatUrl` и `heartbeatIntervalSec` в настройках плагина — планировщик будет пинговать этот адрес по расписанию, и внешний монитор сообщит, когда пинги прекратятся.
246
+ * Встроенный эндпоинт `GET /dsh-cron/heartbeat` сообщает живость, число активных задач и время последнего запуска для ваших собственных сторожей.
247
+
240
248
  ### Параметры
241
249
 
242
250
  | Параметр | Тип | По умолчанию | Описание |
@@ -258,7 +266,6 @@ dsh-cron:
258
266
  | `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | Сервер ntfy, тема и опциональное имя credential токена (`Authorization: Bearer …`) |
259
267
  | `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Сервер Bark и ключ устройства (ключ, заголовок и текст идут в пути запроса) |
260
268
  | `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
269
  | `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | Базовый URL плагина `dsh-tts` для голосовых объявлений |
263
270
  | `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Канал Gitea: базовый URL, `owner/repo` и имя credential API-токена |
264
271
 
@@ -283,6 +290,10 @@ dsh-cron:
283
290
  | `POST` | `/dsh-cron/tasks/:id/pause` | Пауза расписания |
284
291
  | `POST` | `/dsh-cron/tasks/:id/resume` | Возобновление расписания |
285
292
  | `POST` | `/dsh-cron/tasks/:id/toggle` | Переключение активна/на паузе |
293
+ | `POST` | `/dsh-cron/tasks/:id/duplicate` | Копия задачи в статусе «на паузе»: настройки копируются, история и счётчики сбрасываются |
294
+ | `GET` | `/dsh-cron/recipes` | Встроенный каталог рецептов: готовые мониторинговые пресеты по категориям, все только на чтение |
295
+ | `GET` | `/dsh-cron/tasks/export` | Версионированный JSON только с конфигурацией задач — без истории и счётчиков. Каналы ссылаются на credential по имени, но введённые вручную `env` и HTTP-заголовки задачи являются частью конфигурации и попадают в файл |
296
+ | `POST` | `/dsh-cron/tasks/import` | Проверяет документ и применяет его стратегией `add`, `replace` или `skip`; поддерживает `dryRun`. Импортированные задачи всегда приходят **на паузе** — восстановление не сработает само |
286
297
  | `PATCH` | `/dsh-cron/tasks/:id` | Частичное обновление (только whitelisted-поля: `title`, `schedule`, `prompt`, `type`, `delivery`, `provider`, `model`, настройки уведомлений/таймаута/overlap/kanban, `status`, `oneShot`) |
287
298
  | `DELETE` | `/dsh-cron/tasks/:id` | Удаление задачи |
288
299
  | `GET` | `/dsh-cron/models` | Список LLM-провайдеров; `?provider=<id>` — модели |
package/docs/README.zh.md CHANGED
@@ -31,12 +31,12 @@
31
31
 
32
32
  **`@goodandready/dsh-cron`** 是 DeepSeek Harness 的原生全栈调度与后台自动化插件。它将标准 cron 表达式、自然语言间隔语法与自主智能体执行连接起来:
33
33
 
34
- 1. **完善的可视化任务管理器** —— 侧边栏按钮与功能齐全的面板:查看、筛选、暂停、立即运行和创建任务。
34
+ 1. **完善的可视化任务管理器** —— 侧边栏按钮带可折叠的活跃任务列表(下次运行时间或实时状态,行数有上限且状态可记忆),以及功能齐全的面板:按类型、模型、渠道筛选,暂停、立即运行、复制、导出/导入与创建任务。
35
35
  2. **交互式“由 DSH 创建”流程** —— 与智能体对话,把高层需求转化为规范的定时任务。
36
36
  3. **自主工具调用** —— 原生 `cron_*` 工具让智能体在会话中自行安排后续执行。
37
37
  4. **健壮的调度器与原子存储** —— 基于 `croner`:间隔别名、一次性延时任务、原子写入、运行历史与成本追踪。
38
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 凭据名称引用的密钥。
39
+ 6. **多渠道路由与模板** —— 一次运行可投递到 Telegram、dsh-kanban、Discord、Slack、ntfy、Bark、PushPlus、语音(`dsh-tts`)与 Gitea,支持 `{变量}` 消息模板与按 DSH 凭据名称引用的密钥。
40
40
 
41
41
  ---
42
42
 
@@ -59,7 +59,7 @@ graph TD
59
59
  Store["原子 TaskStore<br/>(tasks.json 原子写入)"]
60
60
  AgentRunner["智能体会话调度器<br/>(以指定模型执行提示词)"]
61
61
  Runtimes["执行运行时<br/>(shell、node、python、http、ssh、docker)"]
62
- Notify["投递路由<br/>(模板 + 10 个渠道)"]
62
+ Notify["投递路由<br/>(模板 + 9 个渠道)"]
63
63
  Secrets["凭据引用<br/>(DSH credentials / ENV)"]
64
64
  end
65
65
 
@@ -98,13 +98,15 @@ graph TD
98
98
 
99
99
  | 工具 | 说明 |
100
100
  |:---|:---|
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` |
101
+ | `cron_create_task` | 创建任务:`title`、`schedule`、`prompt`、`fallbackModel`(失败时改用更强模型重试一次),可选 `type`(`llm`/`script`/`node`/`python`/`http`/`ssh`/`docker`/`skill`/`workflow`)、`delivery`、`provider`、`model`、`channels`、`template`、`notifyTelegram`、`onlyOnFailure`、`timeoutSeconds`、`overlapPolicy`、`kanbanMode` |
102
102
  | `cron_schedule_task` | `cron_create_task` 的别名,保持与既有提示词兼容 |
103
103
  | `cron_list_tasks` | 列出任务的状态、下次运行时间、token 总量与成本估算 |
104
104
  | `cron_pause_task` | 暂停调度而不删除配置 |
105
105
  | `cron_resume_task` | 恢复已暂停的调度 |
106
106
  | `cron_delete_task` | 永久删除任务及其历史 |
107
107
  | `cron_run_task` | 触发一次立即的带外运行 |
108
+ | `cron_get_task` | 读取单个任务的完整配置,包括列表中看不到的字段 |
109
+ | `cron_update_task` | 就地修改现有任务(白名单字段,校验与 HTTP 路由一致);提示模型先与用户确认会执行代码的改动 |
108
110
 
109
111
  会话中模型可进行的调用示例:
110
112
 
@@ -146,33 +148,42 @@ cron_create_task({
146
148
  * **环境变量** —— 按任务的 `env` 映射(界面中每行 KEY VALUE)应用于外部运行时;请勿在此存放密钥。
147
149
  * **工作区与 worktree** —— 将任务绑定到 Harness 工作区(`workspaceId`);对会修改代码的智能体任务,可在隔离的 git worktree 中运行(`worktree`、`keepWorktree`)。
148
150
 
149
- ### 7. 会话集成与权限
151
+ ### 7. 成本控制:回退模型
152
+ 任务可以默认使用便宜模型,失败时改用更强模型完成:设置 `fallbackModel`(可选 `fallbackProvider`),失败(`error` 或 `timeout`)的运行会在该模型上重试一次,之后才进入常规重试退避。历史记录会标明最终产出结果的模型以及是否使用了回退,两次尝试的用量与成本都会累计,模板变量 `{model}` 渲染完成运行的模型。回退仅适用于智能体类型(`llm`、`skill`、`workflow`)。
153
+
154
+ ### 8. 会话集成与权限
150
155
  * **按任务的权限预设** —— `default`、`read-only`、`workspace-write` 或 `full` 在提示词执行前应用于任务会话。
151
156
  * **会话自动归档** —— 隔离的 cron 会话在运行后自动归档(尽力而为),不干扰聊天列表。
152
157
  * **历史 → 会话** —— 每次 LLM 运行都会记录会话,可直接从历史记录打开对话。
153
158
 
154
- ### 8. 通知渠道与消息模板
155
- 运行完成后,报告会发送到该任务配置的所有渠道 —— Telegram、dsh-kanban、Discord、Slack、ntfy、Bark、PushPlus、邮件(SMTP)、语音(`dsh-tts`)以及 Gitea issue:
159
+ ### 9. 按规则保持安静
160
+ 有输出的任务可以设置用自然语言描述的**静默规则**(例如“当没有分区使用率超过 80% 时保持安静”)。运行成功时,由便宜模型对照该规则判断输出,若结论为保持安静则跳过报告,并在运行历史中记录原因。遵循 fail-open:没有规则、没有模型、调用失败或答案无法解析时都会照常投递报告。插件设置 `silentRuleModel` 指定用于判断的模型。
161
+
162
+ ### 10. 失败诊断
163
+ 智能体任务可以请求诊断:设置 `inspectOnFailure` 后,失败(`error` 或 `timeout`)的运行会连同任务提示词与截断输出一起交给模型,运行历史中会保存简短诊断与具体的提示词修改建议。历史记录提供按钮把该建议载入编辑表单 —— 不会自动应用。模型由 `inspectorModel` 指定,消息模板中可使用 `{diagnosis}`。模型不可用或调用失败时,失败的运行保持原样。
164
+
165
+ ### 11. 通知渠道与消息模板
166
+ 运行完成后,报告会发送到该任务配置的所有渠道 —— Telegram、dsh-kanban、Discord、Slack、ntfy、Bark、PushPlus、语音(`dsh-tts`)以及 Gitea issue:
156
167
 
168
+ * **任务迁移** —— 将全部配置导出为版本化 JSON,并在别处导入(含预览摘要);导入的任务处于暂停状态。
157
169
  * **按任务选择渠道** —— 在任务表单中勾选渠道;显式选择会覆盖旧版 `notifyTelegram`/`kanbanMode` 开关,留空则回退到它们。
158
170
  * **故障隔离** —— 某个渠道不可用会记录在调度器日志中,其余渠道仍会收到报告;失效的 webhook 不会吞掉整份报告。
159
171
  * **消息模板** —— 支持全局模板、按渠道覆盖或按任务模板,变量为 `{title} {id} {status} {output} {error} {duration} {schedule} {time} {tokens} {cost}`。未知占位符保持原样,失败运行默认使用失败模板。
160
172
  * **`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 信号。
173
+ * **凭据按名称引用** —— webhook token 与 Telegram bot token 填写 DSH 凭据的名称(`botTokenRef`、`ntfyTokenRef`、`pushplusTokenRef`、`giteaTokenRef`),发送时通过 DSH credentials 服务解析,并可回退到环境变量,且绝不会经过插件设置。webhook URL 与 Bark 设备键本身内嵌密钥,因此保存在插件设置文件中,但返回浏览器时始终为掩码,界面回传的掩码值也不会覆盖已保存的值。
174
+ * **投递超时** —— 每个渠道请求都有上限(`deliveryTimeoutMs`,默认 15000 毫秒,可在设置面板或 `settings.yaml` 中调整),且各渠道并发发送:无响应的端点只记录为失败,不会拖慢其他渠道或下一次调度。限制作用于整个渠道处理过程,也覆盖凭据解析——它不支持 abort 信号。
163
175
  * **Telegram** —— 带状态徽标(✅ / ❌)、耗时、调度描述与等宽输出块的 Markdown 报告;动态值会被转义。凭据可直接填写,或从 DSH `settings.yaml` 的 `dsh-messenger-gateway` 段继承(尽力而为)。
164
176
  * **Discord / Slack** —— 通过 webhook 投递:Discord 使用按运行状态着色的 embed,Slack 使用纯文本正文。
165
177
  * **ntfy / Bark / PushPlus** —— 移动推送,支持主题/设备键与可选 bearer token;Bark 的标题与正文放在请求路径中,PushPlus 端点可指向自建代理。
166
- * **邮件** —— SMTP(host、port、TLS、user、`smtpFrom` 与逗号分隔的收件人);需要 Harness 运行时安装 `nodemailer`,缺少时会给出明确错误。传输会继承投递截止时间,因此无响应的 SMTP 服务器不会拖住运行。
167
178
  * **语音** —— `dsh-tts` 通过其 HTTP 路由朗读报告(`ttsBaseUrl`,默认 `http://127.0.0.1:3080`)。
168
179
  * **Gitea** —— 创建包含运行报告的 issue(`giteaBaseUrl`、`giteaRepo`、token 凭据);失败运行标记为 `cron`、`bug`、`alert`。
169
180
  * **测试发送按钮** —— 在安排关键任务前现场验证 Telegram 连通性。
170
181
 
171
- ### 9. Kanban 集成与成本统计
182
+ ### 12. Kanban 集成与成本统计
172
183
  * **自动创建 Kanban 卡片** —— 当 `kanbanMode` 为 `on_failure` 或 `always` 时,插件在 `dsh-kanban` 中创建卡片(`on_failure` → `error`/`timeout` 时进入 *Backlog*;`always` → 完成后进入 *Done*/*Backlog*)。
173
184
  * **Token 与执行成本计量** —— 按运行与任务统计 token 消耗(输入、输出、缓存读取),基于内置价格表估算美元成本,并提供汇总分析栏。
174
185
 
175
- ### 10. 重叠策略与执行超时
186
+ ### 13. 重叠策略与执行超时
176
187
 
177
188
  * **执行超时(`timeoutSeconds`)** —— 达到限制后,shell 子进程通过 abort 信号立即终止,智能体会话被释放以停止消耗 token。默认 `1800`(30 分钟)。
178
189
  * **重叠策略(`overlapPolicy`)** —— 上一次运行尚未结束时再次触发调度时的行为:
@@ -182,7 +193,7 @@ cron_create_task({
182
193
 
183
194
  如果守护进程在计划时刻处于离线状态,启动时该次运行会被记录为 `missed`,历史空档始终可见。
184
195
 
185
- ### 11. 心跳监控(Dead man's switch)
196
+ ### 14. 心跳监控(Dead man's switch)
186
197
  * 在插件设置中配置 `heartbeatUrl` 与 `heartbeatIntervalSec`,调度器会按间隔 GET 该地址 —— 外部监控可在心跳停止时告警。
187
198
  * 内置 `GET /dsh-cron/heartbeat` 端点返回存活状态、活跃任务数与最近运行时间,便于自建看门狗。
188
199
 
@@ -228,13 +239,6 @@ dsh-cron:
228
239
  barkKey: ""
229
240
  pushplusUrl: "https://www.pushplus.plus/send" # pushplusTokenRef
230
241
  pushplusTokenRef: ""
231
- smtpHost: "" # smtpPort / smtpSecure / smtpUser / smtpFrom / smtpTo
232
- smtpPort: 587
233
- smtpSecure: false
234
- smtpUser: ""
235
- smtpPasswordRef: "" # SMTP 密码的凭据名称
236
- smtpFrom: ""
237
- smtpTo: ""
238
242
  ttsBaseUrl: "http://127.0.0.1:3080" # dsh-tts 基础地址
239
243
  giteaBaseUrl: "" # giteaRepo = owner/repo,giteaTokenRef = 凭据名称
240
244
  giteaRepo: ""
@@ -262,7 +266,6 @@ dsh-cron:
262
266
  | `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | ntfy 服务器、主题与可选的 token 凭据名称(以 `Authorization: Bearer …` 发送) |
263
267
  | `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Bark 服务器与设备键(键、标题和正文位于请求路径中) |
264
268
  | `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
269
  | `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | 用于语音播报的 `dsh-tts` 基础地址 |
267
270
  | `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Gitea 渠道:基础地址、`owner/repo` 与 API token 的凭据名称 |
268
271
 
@@ -287,6 +290,10 @@ dsh-cron:
287
290
  | `POST` | `/dsh-cron/tasks/:id/pause` | 暂停调度 |
288
291
  | `POST` | `/dsh-cron/tasks/:id/resume` | 恢复调度 |
289
292
  | `POST` | `/dsh-cron/tasks/:id/toggle` | 切换活跃/暂停 |
293
+ | `POST` | `/dsh-cron/tasks/:id/duplicate` | 创建暂停状态的副本:复制配置,重置运行历史与计数 |
294
+ | `GET` | `/dsh-cron/recipes` | 内置配方目录:按类别分组的现成监控预设,全部为只读操作 |
295
+ | `GET` | `/dsh-cron/tasks/export` | 仅含任务配置的版本化 JSON —— 不含历史与计数。渠道按名称引用凭据,但手动填写在任务中的 `env` 与 HTTP 请求头属于配置,会出现在文件里 |
296
+ | `POST` | `/dsh-cron/tasks/import` | 校验文档并以 `add`、`replace` 或 `skip` 策略导入;支持 `dryRun` 预览。导入的任务始终为**暂停**状态,恢复不会自动触发 |
290
297
  | `PATCH` | `/dsh-cron/tasks/:id` | 部分更新(仅白名单字段:`title`、`schedule`、`prompt`、`type`、`delivery`、`provider`、`model`、通知/超时/重叠/Kanban 设置、`status`、`oneShot`) |
291
298
  | `DELETE` | `/dsh-cron/tasks/:id` | 删除任务 |
292
299
  | `GET` | `/dsh-cron/models` | 列出 LLM 提供方;`?provider=<id>` 列出模型 |
@@ -8,6 +8,8 @@
8
8
  ## User Surfaces
9
9
  - Web/UI:
10
10
  - Экран «Запланированные задачи» (полноэкранный оверлей в центральной колонке интерфейса DSH, аналогично dsh-kanban).
11
+ - Раздел «Активные задачи» в сайдбаре под кнопкой плагина: сворачиваемый (свёрнут по умолчанию, состояние запоминается), до пяти строк с названием и временем следующего запуска или живым статусом, строка «и ещё N»; клик открывает панель и подсвечивает задачу (#34).
12
+ - Панель фильтров в списке задач: тип, модель, канал; фильтрация мгновенная на клиенте, есть сброс, счётчик «показано N из M» и понятное пустое состояние (#40).
11
13
  - Кнопка вызова в боковой панели (sidebar-entry) рядом с новой сессией + иконка в шапке сессии (utilities slot).
12
14
  - Кнопка «Создать ⌄» с дропдауном:
13
15
  - 💬 «Создать с DSH» (запуск интерактивного диалога постановки задачи агенту).
@@ -15,10 +17,10 @@
15
17
  - Табы фильтрации: «Все», «Активные», «На паузе», «Завершённые».
16
18
  - Поисковая строка; сводная статистика (активные задачи, запуски, токены, стоимость).
17
19
  - Карточки задач: статус-переключатель, название, расписание (человекочитаемое + raw cron), действия (запуск, редактирование, удаление).
18
- - Блок «Рекомендуемые задачи»: готовые шаблоны (Daily digest, Weekly review, Follow-up monitor) в один клик.
20
+ - Блок «Рекомендуемые задачи»: подсказки из встроенного каталога рецептов (`lib/recipes.js`, 10 рецептов в 5 категориях), клик открывает форму с заполненными типом, каналом и правилом тишины; ничего не создаётся без сохранения (#48).
19
21
  - Карточка настроек в слоте 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. Параметры задачи включают рантайм (`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
+ - LLM Tools: cron_create_task (+ alias cron_schedule_task), cron_list_tasks, cron_pause_task, cron_resume_task, cron_delete_task, cron_run_task, cron_get_task (полная конфигурация задачи), cron_update_task (правка существующей задачи; переключение в код-исполняющий тип требует явного `confirmCodeSwitch`) (#49). Параметры задачи включают рантайм (`type`), `channels` (список каналов доставки) и `template` (шаблон сообщения).
23
+ - API: `GET /dsh-cron/recipes` (каталог рецептов) (#48); 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, топики, base URL) и `channelTemplates` — карта шаблонов по каналам.
22
24
  - Chat / Slash Commands: отсутствуют (ранее заявленные /cron-команды не были реализованы и удалены из документации; решение 2026-09-09).
23
25
 
24
26
  ## Visual Direction
@@ -42,10 +44,11 @@
42
44
  - CronSidebarButton: кнопка в левом сайдбаре DSH.
43
45
  - CronScreen: основной оверлей со списком, табами, статистикой и рекомендациями.
44
46
  - CreateDropdown: всплывающее меню выбора способа создания.
45
- - ManualTaskModal: модальная форма создания/редактирования (вкладки «Параметры» / «История запусков»); блок каналов доставки — сетка чекбоксов (Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, Email, Voice, Gitea) и поле шаблона сообщения с подсказкой по переменным.
46
- - SettingsModal: настройки доставки с тестами; три сворачиваемые секции — «Credentials (references)», «Delivery channels», «Message templates» (шапка-кнопка, aria-expanded, шеврон).
47
+ - ManualTaskModal: модальная форма создания/редактирования (вкладки «Параметры» / «История запусков»); блок каналов доставки — сетка чекбоксов (Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, Voice, Gitea) и поле шаблона сообщения с подсказкой по переменным.
48
+ - SettingsModal: настройки доставки с тестами; секции «Credentials (references)», «Delivery channels», «Automation models» (модели для правила тишины и диагностики), «Message templates» — сворачиваемые, шапка-кнопка с aria-expanded.
47
49
  - DeliverySettingsForm: общая форма настроек доставки, одна реализация для SettingsModal и CronSettingsCard; секреты вводятся только по имени credential-ссылки.
48
- - TaskItem: строка задачи с переключателем состояния и действиями.
50
+ - TaskItem: строка задачи с переключателем состояния и действиями (запуск, редактирование, дублирование, удаление); адресуется атрибутом data-task-id для подсветки из сайдбара.
51
+ - ImportModal: сводка по файлу (сколько добавится, заменится, пропустится) и выбор стратегии add/replace/skip до применения (#42).
49
52
  - RecommendationCard: плашка с готовым шаблоном.
50
53
  - CronSettingsCard: карточка параметров плагина в настройках; свёрнута по умолчанию, шапка-кнопка с шевроном разворачивает тело (aria-expanded), кнопка «Открыть панель задач» — внутри раскрытого тела (решение 2026-09-10, #100).
51
54
  - States:
@@ -58,12 +61,15 @@
58
61
  ## User Flows
59
62
  1. Создание через DSH-чат: «напоминай каждый день в 9 утра...» → «Создать с DSH» → агент уточняет тип (LLM/NO-LLM), расписание, модель, Silent Rule → после подтверждения вызывает cron_create_task → задача появляется на экране.
60
63
  2. Создание вручную: кнопка сайдбара → «Создать ⌄» → «Настроить вручную» → форма → сохранение.
61
- 3. Выполнение по расписанию: croner/таймер one-shot → запуск по выбранному рантайму (агентская сессия, shell, node, python, http, ssh, docker) → запись в историю → доставка отчёта в выбранные каналы (Telegram, Kanban, Discord, Slack, ntfy, Bark, PushPlus, Email, Voice, Gitea) с учётом `onlyOnFailure`.
64
+ 3. Выполнение по расписанию: croner/таймер one-shot → запуск по выбранному рантайму (агентская сессия, shell, node, python, http, ssh, docker) → запись в историю → доставка отчёта в выбранные каналы (Telegram, Kanban, Discord, Slack, ntfy, Bark, PushPlus, Voice, Gitea) с учётом `onlyOnFailure`.
62
65
  4. Разбор инцидента: история запусков в карточке задачи → статус, длительность, вывод/ошибка.
63
66
 
64
67
  ## Locked Design Decisions
68
+
69
+ - 2026-09-11 — Блок B (0.2.6): рецепты (#48), инструменты чтения и правки задачи (#49), каскад моделей (#45), правило тишины (#44), диагностика сбоев (#43), разбивка обработчиков (#97). Модельные вспомогательные вызовы идут через `ctx.llm.stream` с жёстким дедлайном и fail-open: недоступная или неуверенная модель означает «доставить отчёт», а не тишину. Переключение задачи в код-исполняющий тип подтверждается структурно на обеих поверхностях (HTTP-заголовок и `confirmCodeSwitch` инструмента).
65
70
  - 2026-09-11 — Доставка вынесена в отдельный слой: сообщение рендерится шаблоном `{var}` (#25), транспорт — адаптеры каналов с чистым builder'ом payload и инжектируемым fetch, маршрутизатор собирает ошибки каналов и не роняет запуск (#26, #20–#23, #28, #47). Явно выбранные каналы задачи перекрывают legacy-флаги `notifyTelegram`/`kanbanMode`.
66
71
  - 2026-09-11 — Доставка не может заблокировать планировщик: каждый канал ограничен таймаутом (`deliveryTimeoutMs`, по умолчанию 15 с), каналы отправляются параллельно, а ошибки (включая таймаут) собираются в `failures`. Причина: `protect: true` в croner пропускал бы следующие тики, пока висит незавершённая доставка (находка независимого review PR #114).
72
+ - 2026-09-11 — Блок A (0.2.5): список активных задач в сайдбаре (#34), фильтры по типу/модели/каналу (#40), адаптив до 375 px без скрытия информации (#39), дублирование задачи серверным маршрутом с принудительной паузой и сбросом состояния (#41), экспорт/импорт конфигурации задач в JSON со стратегиями и dry-run (#42), нормализация и нижняя граница таймаута доставки (#115). Экспорт — только JSON: YAML-парсера в проекте нет, а зависимость ради формата не добавляется.
67
73
  - 2026-09-11 — Значения с секретом внутри (webhook-URL Discord/Slack, ключ Bark) маскируются при отдаче в браузер, а замаскированное значение, вернувшееся от UI, не перезаписывает сохранённое; сырые credential-ключи отклоняются и в store, и на входе `/dsh-cron/settings`.
68
74
  - 2026-09-11 — Секреты доставки хранятся только как credential-ссылки (#51): настройки содержат имя credential, значение резолвится в момент отправки через DSH credentials-сервис с фолбэком на ENV; store отказывается сохранять сырые secret-ключи.
69
75
  - 2026-09-11 — Каталог данных плагина: `DSH_DATA_DIR` → `DSH_HOME/data` → `~/.dsh/data`. Причина: изолированный профиль не должен писать в чужой домашний каталог (issue #112, найдено на приёмке в тест-контуре).
@@ -0,0 +1,50 @@
1
+ # План: блок A — пользовательские поверхности (0.2.5)
2
+
3
+ Живой план блока. Обновляется по факту работы; источник истины по задачам —
4
+ Gitea (`goodandready/dsh-cron`), milestone `0.2.5`.
5
+
6
+ ## Состав блока (согласован владельцем)
7
+
8
+ | Issue | Задача | Сложность |
9
+ |:--|:--|:--|
10
+ | #34 | Сворачиваемый раздел активных задач в сайдбаре | H |
11
+ | #40 | Поиск и фильтры списка задач | M |
12
+ | #39 | Адаптив под узкие экраны | M |
13
+ | #41 | Клонирование задачи | L |
14
+ | #42 | Экспорт/импорт задач (JSON/YAML) | L |
15
+ | #115 | Поле «Delivery timeout»: сброс и нижняя граница | L |
16
+
17
+ Дальше по согласованию: блок B (0.2.6) — #45, #44, #43, #49, #48;
18
+ блок C (0.2.7) — #54, #50, #53, #97, #121.
19
+
20
+ ## Границы и инварианты
21
+
22
+ - Один блок → одна ветка `feat/0.2.5-ui-block` → один PR → один релиз 0.2.5.
23
+ - В релиз 0.2.5 также входит уже смёрженное удаление email-канала (#23, PR #120).
24
+ - Клиентская половина остаётся single-file (`lib/client.js`) — требование загрузчика DSH.
25
+ - Новых зависимостей блок не добавляет; экспорт/импорт использует существующие средства разбора.
26
+ - Публичные экспорты `lib/index.js` и контракты маршрутов не ломаются: новые поля — только additive.
27
+ - Секреты в экспортируемом файле отсутствуют: переносятся лишь имена credential-ссылок.
28
+
29
+ ## Вне scope
30
+
31
+ - Серверная авторизация и внешний API (блок C).
32
+ - Изменения в путях исполнения и работе с моделью (блок B).
33
+ - `#117` (мажор croner), `#95` (scope токена), `#113` (инфраструктура тест-контура) — вне блоков.
34
+
35
+ ## План проверки блока
36
+
37
+ - `npm test` + новые тесты на каждый пункт (контракт разметки, фильтрация, сериализация, клонирование, клампинг таймаута).
38
+ - `bash deploy.sh check` — тесты, гейт размера пакета, сборка кандидата.
39
+ - Приёмка на изолированном MiniPC: установка кандидата, проверка списка/фильтров/дублей/экспорта-импорта и геометрии на 375/768/1280 в браузере.
40
+ - Независимое ревью PR перед merge.
41
+
42
+ ## Журнал
43
+
44
+ - 2026-09-11 — блок согласован (A=0.2.5, B=0.2.6, C=0.2.7), созданы milestones и ТЗ по каждой задаче.
45
+ - 2026-09-11 — реализовано: #115 (клампинг таймаута), #41 (дублирование серверным маршрутом), #42 (экспорт/импорт JSON), #40 (фильтры), #39 (адаптив), #34 (аккордеон в сайдбаре). 123/123 тестов, гейт размера 23 файла.
46
+ - Приёмка на MiniPC (изолированный профиль, кандидат из ветки): дубликат — копия `disk-check (copy)` в статусе paused, настройки скопированы, история и счётчики пусты; экспорт — 4 задачи, поля только конфигурации (нет status/totalTokens/lastRunAt/nextRunAt); dry-run импорта — `{add:0, replace:0, skip:4}`, импорт со стратегией add — 4 задачи, все на паузе; битый документ → 400 без изменений в хранилище. В браузере: секция «Active jobs» свёрнута по умолчанию, раскрывается, показывает 3 активные задачи с временем следующего запуска, состояние сохраняется; клик по задаче открывает панель и подсвечивает её; фильтры — 8 → 6 (type=script) → 2 (+channel=ntfy) с подписью «Showing N of 8» и сбросом; дублирование из строки — 9 задач и подтверждение «copy created and paused»; поле таймаута доставки: ввод 4000 сохраняется, очистка возвращает 15000.
47
+ - Ограничение приёмки: смена ширины окна в текущем браузерном транспорте недоступна, поэтому адаптив (#39) проверен наличием и разбором media-query-правил и тестами, а не визуально на 375 px. Это единственный пункт блока без живой визуальной проверки.
48
+ - 2026-09-11 — после независимого ревью (FAIL): импорт переведён на жёсткий whitelist (статус из файла игнорируется, лишние ключи отбрасываются), добавлен confirm-заголовок для документов с code-задачами, зарезервированы id `export`/`import` на создании и импорте, добавлен откат при частичном сбое записи, сайдбар и панель разведены разными атрибутами, добавлен 16px для полей на телефоне, `mountSidebarJobs` разбит на мелкие функции. Проверено живьём: без заголовка 403, с заголовком задача приходит paused с обнулёнными счётчиками и отброшенными лишними ключами; подсветка из сайдбара указывает на строку панели (`.dsh-cron-container .dsh-cron-task-highlight`). 127/127 тестов.
49
+ - 2026-09-11 — релиз: bump 0.2.4 → 0.2.5, тег v0.2.5, npm publish, GitHub Release, установка точной версии в production web-профиль MiniAI и production-проверки. Закрыты #34, #39, #40, #41, #42, #115.
50
+ - Отклонения от первоначального ТЗ (зафиксированы в issue): #41 — сделан отдельный маршрут вместо клиентской сборки payload (сервер знает, что конфигурация, а что состояние запуска, и это тестируемо); #42 — только JSON вместо JSON/YAML (парсера YAML в проекте нет, зависимость ради формата не добавляется); состояние фильтров не пишется в URL-хеш, чтобы не конфликтовать с роутингом SPA.