@goodandready/dsh-cron 0.2.9 → 0.2.11

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
@@ -17,10 +17,20 @@
17
17
 
18
18
  <p align="center">
19
19
  <a href="README.md"><b>🇬🇧 English</b></a> •
20
- <a href="docs/README.ru.md"><b>🇷🇺 Русский</b></a> •
21
- <a href="docs/README.zh.md"><b>🇨🇳 中文说明</b></a>
20
+ <a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
21
+ <a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
22
22
  </p>
23
23
 
24
+ <table align="center">
25
+ <tr>
26
+ <td align="center">
27
+ ⭐ <strong>If you like this plugin, please star it on GitHub</strong> — it shows me that the plugin is useful to you and motivates me to keep developing it.
28
+ <br><br>
29
+ 🐛 <strong>If you find a bug or would like to request a feature</strong>, open a GitHub issue in any language — I will review your proposal and implement useful suggestions in a future plugin version.
30
+ </td>
31
+ </tr>
32
+ </table>
33
+
24
34
  </div>
25
35
 
26
36
  ---
@@ -325,6 +335,21 @@ Developer-facing, no behaviour change. `parseScheduleExpression` was split into
325
335
  - **History Rotation & Archival**: Active store holds the latest 100 runs per task; older entries are automatically archived into `tasks-history-archive.json`.
326
336
  - **Autonomous PR Reviewer Recipe (#33)**: Preconfigured recipe in Template Hub with optional toggle `prReviewerEnabled` in settings.
327
337
 
338
+ ### 22. Automation, Task Chaining & Observability Pack (Added in v0.2.10, #137)
339
+ - **Two-Way Telegram Interactive Controls**: Run completion notifications include inline keyboard buttons (`🚀 Run Now`, `⏸️ Pause` / `▶️ Resume`, `📋 Last Output`). Actions are securely routed via `POST /dsh-cron/telegram/webhook` with Chat ID authorization matching plugin settings or harness defaults.
340
+ - **Task Chaining & Pipelines**: Tasks can declare `onSuccess` and `onFailure` downstream task triggers. Upstream output is automatically forwarded to child tasks via `$DSH_PREV_OUTPUT` environment variable for shell/script tasks and `{{prevOutput}}` variable interpolation in LLM prompts. Infinite execution loops are strictly prevented with a recursion depth limit (max 5 consecutive executions).
341
+ - **Structured LLM Actions**: Autonomous model runs can output structured JSON directives to trigger secondary tasks, dispatch channel notifications, or open issues. Controlled via `llmActionsEnabled: false` settings toggle (strictly disabled by default).
342
+ - **History Archival & Latency Insights**: Active task store retains the most recent 100 runs for instant performance, while older runs are archived in `tasks_archive.json`. New REST endpoints `GET /dsh-cron/tasks/:id/archive` and `GET /dsh-cron/tasks/:id/stats` expose historical records and aggregated latency statistics. Task UI displays execution duration latency badges with color thresholds (<5s green, <30s yellow, >=30s red).
343
+ - **Enriched Prometheus Observability**: The `/dsh-cron/metrics` endpoint exports the active concurrency gauge `dsh_cron_concurrent_running`, per-task prompt/completion token consumption counters `dsh_cron_task_tokens_total{task,model,type}`, and per-task cost estimation counters `dsh_cron_task_cost_usd_total{task,model}`.
344
+
345
+ ### 23. Advanced Reliability, Self-Healing, Heartbeats & UX Pack (Added in v0.2.11, #139)
346
+ - **Heartbeat & Dead Man's Snitch**: Support for inverted cron monitoring where external backup scripts or background jobs ping `/dsh-cron/heartbeat/:id` (or `/dsh-cron/api/heartbeat/:id`). If a ping is missed within `heartbeatIntervalSeconds` + `gracePeriodSeconds`, the task is flagged as `missed`, dispatches an overdue failure alert, and triggers an `onFailure` recovery pipeline.
347
+ - **Pre-flight Execution Gates**: Guard against wasted model tokens and noisy failures with preliminary checks (`preflightType`: `http` status 2xx, `command` exit code 0, or `disk` free MB space). Failing the gate cleanly marks the task as `skipped` without invoking LLMs or dispatching channel errors.
348
+ - **Dry-Run Mode & Schedule Simulator**: Execute tasks on demand via `POST /dsh-cron/tasks/:id/dry-run` or UI `🧪 Dry Run` button without persisting run history or delivering messages. Preview next calculated execution dates via `POST /dsh-cron/schedule/preview`.
349
+ - **Priority Queues & Concurrency Pools**: When the concurrent run limit is reached, queued tasks are ordered by `priority` (1 = highest, 10 = lowest) to ensure critical system alerts execute ahead of bulk background jobs.
350
+ - **Self-Healing Runbooks & Auto-Diagnosis**: Failed tasks automatically execute an optional compensatory `selfHealingCommand` (e.g. system service restart or temp cleanup). Model failures can trigger `autoDiagnose: true` to append an instant root-cause diagnosis.
351
+ - **Web UI Archive & Pipeline Visualization**: Interactive archive drawer with pagination and full log viewing; visual indicators for `➜ onSuccess` and `↳ onFailure` task connections.
352
+
328
353
  ---
329
354
 
330
355
  ## 📦 Installation
package/README.ru.md ADDED
@@ -0,0 +1,481 @@
1
+ # 📦 @goodandready/dsh-cron
2
+
3
+ <div align="center">
4
+
5
+ <h3>Планировщик cron-задач, фоновая автоматизация и выполнение сценариев агентом для DeepSeek Harness</h3>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@goodandready/dsh-cron"><img src="https://img.shields.io/npm/v/@goodandready/dsh-cron.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
9
+ <a href="../LICENSE"><img src="https://img.shields.io/github/license/GooDAnDReaDY/dsh-cron.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
10
+ <a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
11
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://goodandready.app/"><img src="https://img.shields.io/badge/Все_проекты_автора-goodandready.app-ff4500.svg?style=for-the-badge&logo=rocket&logoColor=white&labelColor=1a1a2e" alt="Все проекты автора"></a>
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="README.md"><b>🇬🇧 English</b></a> •
20
+ <a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
21
+ <a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
22
+ </p>
23
+
24
+ <table align="center">
25
+ <tr>
26
+ <td align="center">
27
+ ⭐ <strong>Если вам нравится этот плагин, поставьте ему звезду на GitHub</strong> — это покажет мне, что плагин вам полезен, и будет мотивировать меня развивать его дальше.
28
+ <br><br>
29
+ 🐛 <strong>Если вы нашли баг или хотите предложить новый функционал</strong>, создайте issue на GitHub на любом языке — я рассмотрю ваше предложение и реализую полезные идеи в одной из следующих версий плагина.
30
+ </td>
31
+ </tr>
32
+ </table>
33
+
34
+ </div>
35
+
36
+ ---
37
+
38
+ ## ⚡ Обзор и проблема
39
+
40
+ Автономным AI-агентам регулярно нужны повторяющиеся действия: утренние сводки, разбор трекеров задач, проверка доступности API, синхронизация баз данных, периодическая гигиена Git. Без штатного планировщика внутри харнесса приходится использовать внешние обёртки над crontab, сложные webhook-схемы или ручной запуск.
41
+
42
+ **`@goodandready/dsh-cron`** — нативный полноформатный плагин планирования и фоновой автоматизации для DeepSeek Harness. Он связывает стандартные cron-выражения и естественные интервалы с автономным исполнением агентами:
43
+
44
+ 1. **Развитый визуальный менеджер задач** — кнопка в сайдбаре со сворачиваемым списком активных задач (следующий запуск или живой статус, с ограничением и запоминанием состояния) и полноценная панель: фильтры по типу, модели и каналу, пауза, немедленный запуск, дублирование, экспорт/импорт и создание задач.
45
+ 2. **Интерактивный сценарий «Создать с DSH»** — опишите задачу словами, агент уточнит детали и оформит расписание.
46
+ 3. **Автономный tool calling** — нативные инструменты `cron_*` позволяют агентам планировать собственные последующие действия прямо в диалоге.
47
+ 4. **Надёжный планировщик и атомарное хранилище** — на базе `croner`: интервалы, разовые задачи с задержкой, атомарная запись, история запусков, учёт стоимости.
48
+ 5. **Шесть рантаймов исполнения** — shell, Node.js, Python, HTTP/webhook, удалённый SSH и Docker, плюс переменные окружения на задачу, привязка workspace и изолированные git worktree для изменяющих код агентских задач.
49
+ 6. **Многоканальная доставка с шаблонами** — один запуск расходится в Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, голос (`dsh-tts`) и Gitea, с шаблонами сообщений `{переменные}` и секретами по имени credential в DSH.
50
+
51
+ ---
52
+
53
+ ## 🏗️ Архитектура
54
+
55
+ ```mermaid
56
+ graph TD
57
+ subgraph Client ["Клиентская поверхность (DSH UI)"]
58
+ SidebarBtn["Кнопка-часы в сайдбаре<br/>(слот DSH Client UI)"]
59
+ Overlay["Панель управления задачами<br/>(табы: Все, Активные, На паузе, Завершённые)"]
60
+ CreateWithDSH["Диалог «Создать с DSH»<br/>(задача на естественном языке)"]
61
+ ManualForm["Ручная форма задачи<br/>(рантайм, cron, таймаут, overlap, каналы)"]
62
+ SettingsCard["Карточка настроек<br/>(каналы, шаблоны, credentials)"]
63
+ end
64
+
65
+ subgraph Server ["Серверная часть (Cordis и сервисы DSH)"]
66
+ HttpRoutes["HTTP REST API<br/>(/dsh-cron/*)"]
67
+ AgentTools["Шлюз tool calling<br/>(cron_create_task, cron_list_tasks, ...)"]
68
+ Scheduler["Движок TaskScheduler<br/>(экземпляры Croner + таймеры one-shot)"]
69
+ Store["Атомарный TaskStore<br/>(tasks.json, атомарная запись)"]
70
+ AgentRunner["Диспетчер агентских сессий<br/>(запуск промпта выбранной моделью)"]
71
+ Runtimes["Рантаймы исполнения<br/>(shell, node, python, http, ssh, docker)"]
72
+ Notify["Маршрутизатор доставки<br/>(шаблоны + 9 каналов)"]
73
+ Secrets["Credential-ссылки<br/>(DSH credentials / ENV)"]
74
+ end
75
+
76
+ SidebarBtn --> Overlay
77
+ Overlay --> CreateWithDSH
78
+ Overlay --> ManualForm
79
+ SettingsCard --> HttpRoutes
80
+ CreateWithDSH -->|POST /chat/start| HttpRoutes
81
+ ManualForm -->|POST /tasks| HttpRoutes
82
+ HttpRoutes --> Scheduler
83
+ AgentTools --> Scheduler
84
+ Scheduler --> Store
85
+ Scheduler -->|Запуск по интервалу/one-shot| AgentRunner
86
+ Scheduler --> Notify
87
+ ```
88
+
89
+ ---
90
+
91
+ ## ✨ Возможности
92
+
93
+ ### 1. Визуальный менеджер задач
94
+ Нажмите на иконку-часы в сайдбаре DSH (рядом с кнопкой новой сессии), чтобы открыть панель:
95
+ * **Табы фильтрации**: **Все**, **Активные**, **На паузе**, **Завершённые**.
96
+ * **Мгновенные действия**: немедленный запуск (**Запустить**), пауза/возобновление расписания, удаление с подтверждением.
97
+ * **Готовые шаблоны в один клик**: *Ежедневная сводка*, *Еженедельный обзор*, *Мониторинг дальнейших действий*.
98
+ * **История запусков**: в карточке задачи — время, длительность и статусы предыдущих запусков (успех / сбой / таймаут / пропуск / пропущен по простою), вывод и ошибки.
99
+ * **Сводная статистика**: активные задачи, всего запусков, израсходованные токены и оценочная стоимость в долларах.
100
+
101
+ ### 2. Диалог «Создать с DSH»
102
+ Превратите естественный язык в задачу без подбора cron-синтаксиса:
103
+ 1. Нажмите **Создать ⌄** ➔ **Создать с DSH**.
104
+ 2. Опишите, что нужно автоматизировать (например: *«Проверяй открытые PR по будням в 9:00 и готовь черновики комментариев»*).
105
+ 3. Плагин создаст отдельную агентскую сессию с системными инструкциями планировщика. Агент уточнит детали — LLM или NO-LLM shell-задача, точное cron-выражение, экономичная модель из доступных в вашей установке DSH, нужно ли «правило тишины» (алерт только при новых событиях или сбоях) — и создаст задачу через инструмент `cron_create_task` только после вашего подтверждения.
106
+
107
+ ### 3. Инструменты агентов (tool calling)
108
+
109
+ | Инструмент | Описание |
110
+ |:---|:---|
111
+ | `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` |
112
+ | `cron_schedule_task` | Псевдоним `cron_create_task` для совместимости с существующими промптами |
113
+ | `cron_list_tasks` | Список задач со статусами, временем следующего запуска, токенами и стоимостью |
114
+ | `cron_pause_task` | Приостанавливает расписание без удаления конфигурации |
115
+ | `cron_resume_task` | Возобновляет приостановленное расписание |
116
+ | `cron_delete_task` | Полностью удаляет задачу и её историю |
117
+ | `cron_run_task` | Немедленный внеплановый запуск |
118
+ | `cron_get_task` | Полная конфигурация одной задачи, включая поля, которых нет в списке |
119
+ | `cron_update_task` | Изменяет существующую задачу на месте (whitelisted-поля, та же валидация, что у HTTP-маршрута); модели предписано сперва подтверждать с пользователем изменения, исполняющие код |
120
+
121
+ Пример вызова модели в диалоге:
122
+
123
+ ```
124
+ cron_create_task({
125
+ "title": "Утренняя сводка",
126
+ "schedule": "0 8 * * 1-5",
127
+ "prompt": "Подготовь короткую утреннюю сводку активных задач и открытых тикетов.",
128
+ "type": "llm",
129
+ "delivery": "isolated"
130
+ })
131
+ ```
132
+
133
+ ### 4. Синтаксис расписаний
134
+ На базе `croner`: стандартные 5-полевые cron-выражения и дружелюбные алиасы:
135
+
136
+ * `0 9 * * 1-5` — по будням в 09:00
137
+ * `*/15 * * * *` — каждые 15 минут
138
+ * `0 0 * * 0` — каждое воскресенье в полночь
139
+ * `every 10m` / `every 2h` / `every 30s` — естественные интервалы
140
+ * алиасы `daily` / `hourly` / `weekdays`, а также стандартные `@hourly` / `@daily` / `@weekly` / `@monthly` / `@yearly` и `@every 30m`
141
+ * **Часовые пояса** — для задачи можно указать IANA-зону (например, `Europe/Berlin`); без неё расписание живёт в серверном времени
142
+ * **Разовые задачи**: `at: 2026-09-05T15:00:00Z` (точный ISO-таймстемп) или относительные задержки `in 20m` / `in 2h` (принимаются и русские варианты вроде `через 15 минут`). После единственного запуска задача автоматически переходит в `completed` и отображается на табе **Завершённые**.
143
+
144
+ ### 5. Надёжность исполнения
145
+ * **Автоповторы** — `maxRetries` и база `retryBackoffMs` на задачу: упавшие запуски (error/timeout) повторяются с экспоненциальной задержкой, счётчик сбрасывается после успеха.
146
+ * **Misfire-политики** — что делать с пропущенным за время простоя запуском: `skip` (по умолчанию — записать пропуск), `runOnce` (выполнить один раз с опозданием) или `catchUpAll` (выполнить и зафиксировать пропуск). Пропущенный one-shot при `skip` уходит в `completed` без выполнения.
147
+ * **Лимит параллельности** — настройка `maxConcurrent` ограничивает число одновременных запусков; лишние помечаются `skipped` с причиной.
148
+ * **Живой индикатор** — в списке задач пульсирует статус и идёт таймер текущего запуска.
149
+
150
+ ### 6. Рантаймы исполнения
151
+ Каждая задача выбирает собственный рантайм; не-LLM рантаймы не используют модель и не тратят токены:
152
+
153
+ * **Shell** (`script`) — команда или скрипт через shell харнесса, с `env` и `cwd`.
154
+ * **Node.js** (`node`) и **Python** (`python`) — запуск сниппета с указанием интерпретатора (`nodePath`, `pythonPath`); для Python определяется виртуальное окружение проекта.
155
+ * **HTTP** (`http`) — GET/POST/… по URL с собственными заголовками и телом; статус и вывод ответа попадают в историю запуска.
156
+ * **SSH** (`ssh`) — выполнение команды на удалённом хосте через профиль `dsh-remote-workspace` (`sshProfileId`) или отдельные поля host/key.
157
+ * **Docker** (`docker`) — выполнение команды в контейнере образа (`dockerImage`).
158
+ * **Переменные окружения** — карта `env` на задачу (в UI — строки KEY VALUE) для внешних рантаймов; секретам здесь не место.
159
+ * **Workspace и worktree** — привязка задачи к workspace харнесса (`workspaceId`) и, для изменяющих код агентских задач, запуск в изолированном git worktree (`worktree`, `keepWorktree`).
160
+
161
+ ### 7. Экономия: fallback-модель
162
+ Задача может идти на дешёвой модели по умолчанию и всё же завершиться на сильной: задайте `fallbackModel` (и при необходимости `fallbackProvider`), и сбойный запуск (`error` или `timeout`) один раз повторится на этой модели, прежде чем включится обычный retry с задержкой. В истории видно, какая модель произвела результат и был ли использован fallback; расход и стоимость обеих попыток суммируются; переменная шаблона `{model}` подставляет модель, завершившую запуск. Fallback доступен только агентским типам (`llm`, `skill`, `workflow`).
163
+
164
+ ### 8. Интеграция сессий и права
165
+ * **Permission-пресеты на задачу** — `default`, `read-only`, `workspace-write` или `full` применяются к сессии агента перед запуском промпта.
166
+ * **Автоархивация сессий** — изолированные cron-сессии архивируются после запуска (best-effort), не засоряя список чатов.
167
+ * **История → сессия** — каждый LLM-запуск хранит свою сессию; открыть диалог можно прямо из записи истории.
168
+
169
+ ### 9. Тишина по правилу
170
+ У задачи с выводом может быть **правило тишины**, написанное словами («молчи, если ни один раздел не занят больше 80%»). На успешном запуске дешёвая модель сверяет вывод с правилом, и отчёт пропускается, если вердикт — молчать; причина сохраняется в истории запуска. Работает fail-open: нет правила, нет модели, сбой вызова или нечитаемый ответ — отчёт доставляется. Настройка `silentRuleModel` задаёт модель для проверки.
171
+
172
+ ### 10. Диагностика сбоев
173
+ Агентские задачи могут заказывать диагноз: с включённым `inspectOnFailure` сбойный запуск (`error` или `timeout`) вместе с промптом задачи и обрезанным выводом читает модель, и в историю запуска попадают короткий диагноз и конкретная правка промпта. В записи истории есть кнопка, подставляющая эту правку в форму редактирования — автоматически ничего не применяется. Модель задаётся настройкой `inspectorModel`, в шаблонах доступна переменная `{diagnosis}`. Недоступная модель оставляет сбойный запуск ровно таким, каким он был.
174
+
175
+ ### 11. Каналы доставки и шаблоны сообщений
176
+ Отчёт о завершённом запуске уходит во все каналы, выбранные для задачи — Telegram, dsh-kanban, Discord, Slack, ntfy, Bark, PushPlus, голос через `dsh-tts` и issue в Gitea:
177
+
178
+ * **Перенос задач** — экспорт всей конфигурации в версионированный JSON и импорт с предварительной сводкой; импортированные задачи приходят на паузе.
179
+ * **Каналы на задачу** — отметьте каналы в форме задачи; явный выбор перекрывает legacy-переключатели `notifyTelegram`/`kanbanMode`, а пустой выбор возвращается к ним.
180
+ * **Изоляция сбоев** — недоступный канал фиксируется в логе планировщика, остальные каналы получают отчёт; сломанный webhook не поглощает доставку целиком.
181
+ * **Шаблоны сообщений** — глобальный шаблон, переопределения по каналам или шаблон на задачу с переменными `{title} {id} {status} {output} {error} {duration} {schedule} {time} {tokens} {cost}`. Неизвестные плейсхолдеры остаются как есть, для сбойных запусков по умолчанию используется шаблон ошибки.
182
+ * **`onlyOnFailure`** — глобально или на задачу: успешные запуски молчат, уходят только `error`/`timeout`.
183
+ * **Креденшелы по ссылке** — токены webhook'ов и токен Telegram вводятся как ИМЯ credential в DSH (`botTokenRef`, `ntfyTokenRef`, `pushplusTokenRef`, `giteaTokenRef`); значение резолвится в момент отправки через credentials-сервис DSH с фолбэком на переменную окружения и никогда не проходит через настройки плагина. Webhook-URL и ключ устройства Bark содержат секрет внутри, поэтому хранятся в настройках плагина, но всегда отдаются в браузер замаскированными, а замаскированное значение из UI никогда не перезаписывает сохранённое.
184
+ * **Таймаут доставки** — каждый запрос канала ограничен (`deliveryTimeoutMs`, по умолчанию 15000 мс, задаётся в панели настроек или `settings.yaml`), каналы отправляются параллельно: недоступный endpoint фиксируется как сбой и не задерживает остальные каналы и следующий тик расписания. Ограничение действует на весь обработчик канала, включая резолв credential'ов, который не поддерживает abort-сигнал.
185
+ * **Telegram** — Markdown-отчёт со статусными значками (✅ / ❌), длительностью, описанием расписания и monospace-блоком вывода; динамические значения экранируются. Креденшелы можно ввести напрямую или унаследовать из секции `dsh-messenger-gateway` вашего DSH `settings.yaml` (best-effort).
186
+ * **Discord / Slack** — доставка через webhook: Discord получает embed с цветом по статусу запуска, Slack — обычный текст.
187
+ * **ntfy / Bark / PushPlus** — мобильные пуши: тема/ключ устройства и опциональный bearer-токен; у Bark заголовок и текст идут в пути запроса, у PushPlus endpoint настраивается (self-hosted прокси).
188
+ * **Голос** — `dsh-tts` озвучивает отчёт через свой HTTP-маршрут (`ttsBaseUrl`, по умолчанию `http://127.0.0.1:3080`).
189
+ * **Gitea** — создаёт issue с отчётом (`giteaBaseUrl`, `giteaRepo`, credential токена); сбойные запуски помечаются метками `cron`, `bug`, `alert`.
190
+ * **Кнопка проверки** — проверьте доставку в Telegram до запуска критичных задач.
191
+
192
+ ### 12. Интеграция с Kanban и учёт стоимости
193
+ * **Автоматические карточки Kanban** — при `kanbanMode` = `on_failure` или `always` плагин создаёт карточки в `dsh-kanban` (`on_failure` → *Backlog* при `error`/`timeout`; `always` → *Done*/*Backlog* по завершении).
194
+ * **Счётчик токенов и стоимости** — потребление токенов (ввод, вывод, чтения из кэша) учитывается по запускам и задачам с оценкой в USD по встроенной таблице цен и сводной панелью аналитики.
195
+
196
+ ### 13. Политики наложения и таймаут выполнения
197
+
198
+ * **Таймаут (`timeoutSeconds`)** — по достижении лимита shell-процесс немедленно завершается через abort-сигнал, а агентская сессия закрывается, чтобы не расходовать токены. По умолчанию `1800` (30 минут).
199
+ * **Политика наложения (`overlapPolicy`)** — что делать, когда тик срабатывает при ещё активном предыдущем запуске:
200
+ * **`skip`** (по умолчанию): накладывающийся запуск отбрасывается, в истории появляется запись `skipped`;
201
+ * **`queue`**: следующий запуск ставится в очередь и стартует по завершении активного;
202
+ * **`replace`**: активный запуск прерывается через `AbortController`, запускается свежий.
203
+
204
+ Если сервис был выключен в момент планового запуска, при старте в истории появится запись `missed` — пробелы в истории остаются видимыми.
205
+
206
+ ### 21. Пакет производительности и изоляции процессов (v0.2.9, #134)
207
+ - **Изоляция дерева процессов**: Shell и Script задачи запускаются в отдельной группе процессов (POSIX `detached: true`); при отмене или таймауте сигнал `-child.pid SIGTERM -> SIGKILL` завершает всё дерево, исключая зомби-процессы.
208
+ - **Троттлинг параллелизма**: Безопасный лимит `maxConcurrent = 2` по умолчанию предотвращает всплески нагрузки на CPU и RAM.
209
+ - **Повторы транзиентных сбоев**: Экспоненциальный backoff для ошибок 429 и 5xx (до 3 попыток).
210
+ - **Сетевая и UI-оптимизация**: `GET /dsh-cron/tasks` поддерживает `ETag` и `304 Not Modified`; адаптивный опрос UI (30с в фоне, 8с на активной вкладке).
211
+ - **Ротация истории и архив**: В памяти удерживается до 100 последних запусков на задачу, остальные архивируются в `tasks-history-archive.json`.
212
+ - **Рецепт автономного PR-ревьюера (#33)**: Готовый шаблон в Template Hub и тумблер `prReviewerEnabled`.
213
+
214
+ ### 22. Автоматизация, цепочки задач и наблюдаемость (v0.2.10, #137)
215
+ - **Двухсторонний интерактивный Telegram**: Кнопки действий под уведомлениями (`🚀 Run Now`, `⏸️ Pause`, `📋 Last Output`), вебхук `POST /dsh-cron/telegram/webhook` с валидацией прав по Chat ID и откликом `answerCallbackQuery`.
216
+ - **Цепочки задач и конвейеры**: Триггеры `onSuccess` и `onFailure` для связывания задач. Передача вывода родительской задачи в переменную `$DSH_PREV_OUTPUT` (для shell) и `{{prevOutput}}` (для LLM). Ограничение глубины (максимум 5 уровней) против зацикливания.
217
+ - **Структурированные действия LLM**: Парсер директив модели (`trigger_task`, `notify`, `create_issue`) под опцией `llmActionsEnabled: false`.
218
+ - **Архивация и задержка в UI**: REST API `/dsh-cron/tasks/:id/archive` с пагинацией и статистика `/stats`. Бейджи латентности на карточках задач (<5с зелёный, <30с жёлтый, ≥30с красный).
219
+ - **Расширенные Prometheus-метрики**: Gauge `dsh_cron_concurrent_running`, счетчики токенов и стоимости в USD на задачу.
220
+
221
+ ### 23. Расширенная надёжность, самовосстановление, Heartbeat и UX (v0.2.11, #139)
222
+ - **Мониторинг тишины (Heartbeat / Dead Man's Snitch)**: Эндпоинты `/dsh-cron/heartbeat/:id` и `/dsh-cron/api/heartbeat/:id` для приёма внешних пингов от бэкапов и демонов. При отсутствии пинга в пределах `heartbeatIntervalSeconds` + `gracePeriodSeconds` фиксируется статус `missed`, рассылается тревога и запускается `onFailure`.
223
+ - **Pre-flight проверки (условный запуск)**: Предварительная проверка HTTP-статуса 2xx, exit-кода команды или свободного места на диске. При непрохождении задача переходит в `skipped` без траты токенов LLM.
224
+ - **Dry-Run и симулятор расписания**: Тестовый запуск `POST /dsh-cron/tasks/:id/dry-run` и кнопка `🧪 Dry Run` в UI без записи в историю и без отправки в каналы; расчет следующих тиков через `POST /dsh-cron/schedule/preview`.
225
+ - **Очереди с приоритетами**: При достижении лимита параллелизма задачи упорядочиваются по полю `priority` (1 — наивысший, 10 — низший).
226
+ - **Команды самоисцеления и авто-диагностика (Self-Healing)**: Автоматический запуск компенсирующей команды `selfHealingCommand` при падении задачи; опция `autoDiagnose` для генерации AI-диагностики причин сбоя.
227
+ - **Интерактивный архив логов в UI**: Модальное окно просмотра истории с пагинацией и полным выводом логов, визуальные ссылки конвейеров `➜ onSuccess` и `↳ onFailure`.
228
+
229
+ ---
230
+
231
+ ## 📦 Установка
232
+
233
+ ```bash
234
+ dsh plugin --profile web add @goodandready/dsh-cron
235
+ ```
236
+
237
+ Перезапустите DeepSeek Harness и обновите страницу в браузере.
238
+
239
+ ---
240
+
241
+ ## ⚙️ Конфигурация (`settings.yaml`)
242
+
243
+ Конфигурацию можно задать в `settings.yaml` или интерактивно через карточку настроек плагина в DSH:
244
+
245
+ ```yaml
246
+ # settings.yaml
247
+ dsh-cron:
248
+ botToken: "" # токен Telegram Bot API (секретное поле)
249
+ chatId: "" # ID чата Telegram для отчётов
250
+ notifyTelegram: false # глобально отправлять отчёты о всех задачах
251
+ onlyOnFailure: false # отправлять отчёты только при сбоях
252
+ kanbanBaseUrl: "http://127.0.0.1:3000" # базовый URL HTTP API dsh-kanban
253
+ defaultTimezone: "" # IANA-зона по умолчанию (пусто = серверное время)
254
+ maxConcurrent: 0 # максимум параллельных запусков (0 = без лимита)
255
+ heartbeatUrl: "" # URL dead man's snitch, пингуется по интервалу
256
+ heartbeatIntervalSec: 0 # интервал heartbeat-пинга в секундах (0 = выключено)
257
+ # --- каналы доставки ---
258
+ botTokenRef: "" # ИМЯ credential для токена Telegram-бота
259
+ template: "" # глобальный шаблон сообщения, напр. "⏰ {title} — {status}"
260
+ channelTemplates: {} # переопределения шаблонов по каналам
261
+ deliveryTimeoutMs: 15000 # таймаут доставки на канал; медленный канал = сбой, остальные не ждут
262
+ discordWebhookUrl: "" # webhook Discord
263
+ slackWebhookUrl: "" # incoming webhook Slack
264
+ ntfyUrl: "https://ntfy.sh" # сервер ntfy; ntfyTopic / ntfyTokenRef
265
+ ntfyTopic: ""
266
+ ntfyTokenRef: ""
267
+ barkServerUrl: "https://api.day.app" # сервер Bark; barkKey — ключ устройства
268
+ barkKey: ""
269
+ pushplusUrl: "https://www.pushplus.plus/send" # pushplusTokenRef
270
+ pushplusTokenRef: ""
271
+ ttsBaseUrl: "http://127.0.0.1:3080" # базовый URL dsh-tts
272
+ giteaBaseUrl: "" # giteaRepo = owner/repo, giteaTokenRef = ИМЯ credential
273
+ giteaRepo: ""
274
+ giteaTokenRef: ""
275
+ # --- внешний REST API (#54) ---
276
+ apiToken: "" # bearer-токен внешнего префикса /dsh-cron/api/* (маскируется; пусто = 503)
277
+ ```
278
+
279
+ ### 14. Мониторинг heartbeat (dead man's switch)
280
+ * Задайте `heartbeatUrl` и `heartbeatIntervalSec` в настройках плагина — планировщик будет пинговать этот адрес по расписанию, и внешний монитор сообщит, когда пинги прекратятся.
281
+ * Встроенный эндпоинт `GET /dsh-cron/heartbeat` сообщает живость, число активных задач и время последнего запуска для ваших собственных сторожей.
282
+
283
+ ### 15. Задачи из конфига профиля (#50)
284
+ Долгоживущие эксплуатационные задачи можно объявлять в конфиге профиля, а не пересоздавать руками в интерфейсе. Владелец объявленных задач — файл конфига: при каждом старте плагина они создаются или обновляются, а задача, исчезнувшая из файла, удаляется.
285
+
286
+ Добавьте список `jobs` в секцию плагина конфига профиля (`cordis.patch.yml`):
287
+
288
+ ```yaml
289
+ dsh-cron:
290
+ jobs:
291
+ - id: nightly-backup
292
+ title: Nightly backup
293
+ schedule: "0 3 * * *"
294
+ type: script
295
+ prompt: "bash /path/to/backup.sh"
296
+ channels: ["telegram"]
297
+ timeoutSeconds: 3600
298
+ - id: morning-digest
299
+ title: Morning digest
300
+ schedule: "0 8 * * 1-5"
301
+ type: llm
302
+ prompt: "Prepare a brief morning digest of active tasks."
303
+ provider: my-provider
304
+ model: provider-id/model-id
305
+ ```
306
+
307
+ * Обязательные поля записи: `id`, `title`, `schedule`; типам, у которых полезная нагрузка — это промпт (`script`, `node`, `python`, `ssh`, `docker`, `llm`, `skill`, `workflow`), нужен ещё непустой `prompt`. `http` — исключение: цель задаётся `httpUrl` (или `prompt`).
308
+ * Остальные поля задачи проходят как есть с той же валидацией, что и в API: `channels`, `model`, `provider`, `fallbackModel`, `silentRule`, `inspectOnFailure`, `timezone`, `timeoutSeconds`, `template`, `env`, `cwd` и рантайм-поля (`nodePath`, `pythonPath`, `httpUrl`, `httpMethod`, `httpHeaders`, `httpBody`, `sshProfileId`, `sshTarget`, `dockerImage`, `workspaceId`, `worktree`, `keepWorktree`, `skillName`, `workflowName`).
309
+ * Объявленные задачи помечаются как **управляемые конфигом**; в панели вместо действий правки и удаления выводится метка источника.
310
+ * Правка, пауза, возобновление, переключение и удаление конфиг-задачи отклоняются с `409` в панели и по API, и создание-обновление через `POST /dsh-cron/tasks` с существующим `id` конфиг-задачи отклоняется так же — источник правды файл конфига. **Запустить сейчас** остаётся доступным.
311
+ * Задача с тем же `id`, созданная через UI, API или инструмент агента, никогда не перезаписывается: запись пропускается, конфликт пишется в лог.
312
+ * Код-исполняющие типы активируются как обычные объявленные задачи, но при старте плагин пишет предупреждение в лог — путь исполнения кода, добавленный правкой конфига, остаётся видимым.
313
+ * Записи валидируются по одной с указанием индекса (`config.jobs[i]: …`); одна плохая запись пропускается и не может остановить остальные задачи или профиль.
314
+
315
+ ### 16. Внешний REST API (`/dsh-cron/api/*`, #54)
316
+ Внешние системы (CI, cron хоста, `curl`) могут управлять планировщиком без открытия панели. Это единственная поверхность за bearer-токеном; маршруты панели остаются локальными и защищёнными от cross-origin.
317
+
318
+ Токен задаётся настройкой плагина `apiToken` (маскируется, как любой секрет). Аутентификация и ошибки:
319
+ * токен не задан → вся поверхность отвечает `503`;
320
+ * нет заголовка `Authorization: Bearer <token>` или токен неверный → `401`; сравнение постоянное по времени.
321
+
322
+ | Метод | Путь | Описание |
323
+ |:---|:---|:---|
324
+ | `GET` | `/dsh-cron/api/tasks` | Список задач (фильтры `status` / `query`, как в панели) |
325
+ | `GET` | `/dsh-cron/api/tasks/:id` | Чтение одной задачи |
326
+ | `POST` | `/dsh-cron/api/tasks` | Создание задачи или обновление существующей при наличии `id` |
327
+ | `DELETE` | `/dsh-cron/api/tasks/:id` | Удаление задачи |
328
+ | `POST` | `/dsh-cron/api/tasks/:id/run` | Принудительный немедленный запуск |
329
+
330
+ Операции переиспользуют обработчики панели, поэтому гейт `x-dsh-cron-confirm: script` для код-исполняющих типов и отказ `409` для конфиг-задач действуют здесь так же, как в UI.
331
+
332
+ ```bash
333
+ BASE="http://127.0.0.1:3080"
334
+ TOKEN="<API_TOKEN>"
335
+
336
+ # список
337
+ curl -s -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks"
338
+
339
+ # создание или обновление, если в теле есть id
340
+ curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
341
+ -d '{"id":"cleanup","title":"Cleanup","schedule":"0 4 * * *","prompt":"Remove stale temporary files."}' \
342
+ "$BASE/dsh-cron/api/tasks"
343
+
344
+ # принудительный запуск
345
+ curl -s -X POST -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks/cleanup/run"
346
+
347
+ # удаление
348
+ curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks/cleanup"
349
+
350
+ # код-исполняющей задаче нужен ещё заголовок подтверждения
351
+ curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "x-dsh-cron-confirm: script" \
352
+ -H "Content-Type: application/json" \
353
+ -d '{"title":"Disk check","schedule":"0 * * * *","type":"script","prompt":"df -h"}' \
354
+ "$BASE/dsh-cron/api/tasks"
355
+ ```
356
+
357
+ ### 17. Метрики Prometheus (#53)
358
+ `GET /dsh-cron/metrics` отдаёт текст в формате Prometheus, поэтому планировщик можно снимать scrape'ом без новых зависимостей:
359
+
360
+ * `dsh_cron_tasks_total{status}` — число задач по статусам (gauge).
361
+ * `dsh_cron_task_last_duration_seconds{task}` — длительность последнего завершённого запуска задачи в секундах (gauge).
362
+ * `dsh_cron_runs_total{status}` — завершённые запуски с момента старта процесса плагина (counter); статусы `success`, `error`, `timeout`, `skipped`, `missed`.
363
+ * `dsh_cron_run_records` — число записей о запусках, хранимых в памяти (gauge).
364
+
365
+ В экспозицию попадают только счётчики, статусы и длительности; промпты, вывод запусков и конфигурация задач в неё не входят.
366
+
367
+ ```yaml
368
+ scrape_configs:
369
+ - job_name: dsh-cron
370
+ static_configs:
371
+ - targets: ["127.0.0.1:3080"]
372
+ metrics_path: /dsh-cron/metrics
373
+ ```
374
+
375
+ ### 18. Строгая проверка каналов (#121)
376
+ Создание или обновление задачи с неизвестным идентификатором канала теперь отклоняется с `400`, а виновники перечисляются в ответе:
377
+
378
+ ```json
379
+ { "ok": false, "error": "Unknown channel ids: email_ping", "unknownChannels": ["email_ping"] }
380
+ ```
381
+
382
+ Changed in v0.2.7: раньше неизвестный идентификатор молча отбрасывался, поэтому клиент с опечаткой получал `ok: true` и задачу, которая никуда не доставляет.
383
+
384
+ Импорт намеренно остаётся терпимым (файл может быть из старой версии): неизвестные идентификаторы отбрасываются у импортируемой задачи, но перечисляются в ответе (`unknownChannels`) и пишутся в лог планировщика, а не исчезают молча.
385
+
386
+ ### 19. Проверка после установки (#126)
387
+ У `deploy.sh` есть режим только-проверки уже установленного профиля, ничего не устанавливающий:
388
+
389
+ ```bash
390
+ bash deploy.sh verify [exact-version]
391
+ ```
392
+
393
+ Он проверяет, что профиль сообщает нужную версию (по умолчанию — версия из `package.json`), аутентифицируется в web UI, затем скачивает клиентский бандл и убеждается, что имя пакета в нём присутствует.
394
+
395
+ Зачем это нужно: web-профиль может стоять за плагином аутентификации и отвечать `401` на анонимный запрос, а клиентский бандл плагина отдаётся только по точному combined-URL вида `??` из аутентифицированного индекса — голый `/plugins/<name>/client.js` отвечает `404`. Поэтому проверка сначала строит аутентифицированную сессию.
396
+
397
+ Переменные окружения проверки: `DSH_WEB_BASE` (по умолчанию `http://127.0.0.1:3080`), `DSH_WEB_TOKEN` (токен; если не задан, скрипт берёт последний из журнала юнита), `DSH_WEB_UNIT` (по умолчанию `dsh-web.service`). Секретов в скрипте нет.
398
+
399
+ ### 20. Внутренняя разбивка: разбор расписания и постановка (#97)
400
+ Только для разработчиков, поведение не меняется. `parseScheduleExpression` разбит на маленькие функции с тем же порядком ветвей — `parseAtExpression`, `parseRelativeOneShot`, `parseIntervalExpression`, `parseAliasExpression`, `parseCronExpression`, — а `scheduleTask` — на `clearScheduled`, `scheduleOneShot` и `scheduleCron`. Прежний набор тестов прошёл без правок, добавлены точечные тесты на приоритет ветвей и ошибки.
401
+
402
+ ### Параметры
403
+
404
+ | Параметр | Тип | По умолчанию | Описание |
405
+ |:---|:---|:---|:---|
406
+ | `botToken` | `string` | `""` | Токен Telegram Bot API. Если пусто, плагин пытается унаследовать бота, настроенного для `dsh-messenger-gateway` в настройках DSH (best-effort). Секретное поле: в интерфейсе отображается только замаскированное значение |
407
+ | `chatId` | `string` | `""` | ID чата Telegram для отчётов. Пустое значение — откат к первому разрешённому чату `dsh-messenger-gateway` |
408
+ | `notifyTelegram` | `boolean` | `false` | Глобальный выключатель доставки отчётов в Telegram |
409
+ | `onlyOnFailure` | `boolean` | `false` | Глобальный режим «только при сбоях» (`error`/`timeout`) |
410
+ | `kanbanBaseUrl` | `string` | `"http://127.0.0.1:3000"` | Базовый URL HTTP API `dsh-kanban` для автоматических карточек |
411
+ | `defaultTimezone` | `string` | `""` | IANA-зона по умолчанию для расписаний; пусто = серверное время |
412
+ | `maxConcurrent` | `number` | `0` | Лимит параллельных запусков; лишние помечаются `skipped` (0 = без лимита) |
413
+ | `heartbeatUrl` | `string` | `""` | URL dead man's snitch, пингуемый каждый `heartbeatIntervalSec`, пока жив планировщик |
414
+ | `heartbeatIntervalSec` | `number` | `0` | Интервал heartbeat-пинга в секундах (0 = выключено) |
415
+ | `botTokenRef` | `string` | `""` | Имя credential DSH с токеном Telegram-бота; резолвится при отправке (фолбэк: `botToken` → настройки messenger-gateway → переменная окружения `CRON_TELEGRAM_BOT_TOKEN`) |
416
+ | `template` | `string` | `""` | Глобальный шаблон сообщения с плейсхолдерами `{title}`/`{status}`/`{duration}`/…; пусто = встроенный текст |
417
+ | `channelTemplates` | `object` | `{}` | Переопределения шаблонов по каналам (`telegram`, `discord`, …) |
418
+ | `deliveryTimeoutMs` | `number` | `15000` | Таймаут доставки на канал; более медленный endpoint фиксируется как сбой и не задерживает остальные каналы и следующий тик |
419
+ | `discordWebhookUrl` / `slackWebhookUrl` | `string` | `""` | Webhook-URL каналов Discord и Slack |
420
+ | `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | Сервер ntfy, тема и опциональное имя credential токена (`Authorization: Bearer …`) |
421
+ | `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Сервер Bark и ключ устройства (ключ, заголовок и текст идут в пути запроса) |
422
+ | `pushplusUrl` / `pushplusTokenRef` | `string` | `"https://www.pushplus.plus/send"` / `""` | Endpoint PushPlus (переопределяется для self-hosted прокси) и имя credential токена |
423
+ | `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | Базовый URL плагина `dsh-tts` для голосовых объявлений |
424
+ | `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Канал Gitea: базовый URL, `owner/repo` и имя credential API-токена |
425
+ | `apiToken` | `string` | `""` | Bearer-токен внешней поверхности `/dsh-cron/api/*`. Секретное поле, отдаётся замаскированным; пусто отключает поверхность (503), неверное значение — 401 |
426
+
427
+ Примечания:
428
+
429
+ * История запусков ограничена **50 записями на задачу** (фиксировано); в записи хранится до 4000 символов вывода.
430
+ * Задачи выполняются в **локальном часовом поясе сервера**, если для задачи не указана своя IANA-зона; cron-выражения вычисляет `croner` по часам хоста.
431
+ * Задачи сохраняются в каталоге данных DSH (`cron/tasks.json`) и переживают перезапуск; пропущенные one-shot запуски обнаруживаются при старте.
432
+
433
+ ---
434
+
435
+ ## 🔌 HTTP API
436
+
437
+ Все эндпоинты обслуживаются веб-сервером DSH под `/dsh-cron/`. Чтение открыто локальному интерфейсу; **мутирующие эндпоинты отклоняют cross-origin запросы** и принимают тела до 1 МБ. Для создания `script`-задач по HTTP дополнительно требуется заголовок `x-dsh-cron-confirm: script`, который подделанный межсайтовый запрос приложить не может.
438
+
439
+ | Метод | Путь | Описание |
440
+ |:---|:---|:---|
441
+ | `GET` | `/dsh-cron/tasks` | Список задач; параметры `status` (`all/active/paused/completed`), `query` (подстрока). Возвращает задачи, шаблоны рекомендаций и сводную статистику |
442
+ | `POST` | `/dsh-cron/tasks` | Создание или обновление задачи (при наличии `id` — обновление). Обязательны `title`, `schedule`, `prompt` |
443
+ | `GET` | `/dsh-cron/tasks/:id/history` | История запусков, `?limit=20` |
444
+ | `POST` | `/dsh-cron/tasks/:id/run` | Немедленный ручной запуск |
445
+ | `POST` | `/dsh-cron/tasks/:id/pause` | Пауза расписания |
446
+ | `POST` | `/dsh-cron/tasks/:id/resume` | Возобновление расписания |
447
+ | `POST` | `/dsh-cron/tasks/:id/toggle` | Переключение активна/на паузе |
448
+ | `POST` | `/dsh-cron/tasks/:id/duplicate` | Копия задачи в статусе «на паузе»: настройки копируются, история и счётчики сбрасываются |
449
+ | `GET` | `/dsh-cron/recipes` | Встроенный каталог рецептов: готовые мониторинговые пресеты по категориям, все только на чтение |
450
+ | `GET` | `/dsh-cron/tasks/export` | Версионированный JSON только с конфигурацией задач — без истории и счётчиков. Каналы ссылаются на credential по имени, но введённые вручную `env` и HTTP-заголовки задачи являются частью конфигурации и попадают в файл |
451
+ | `POST` | `/dsh-cron/tasks/import` | Проверяет документ и применяет его стратегией `add`, `replace` или `skip`; поддерживает `dryRun`. Импортированные задачи всегда приходят **на паузе** — восстановление не сработает само |
452
+ | `PATCH` | `/dsh-cron/tasks/:id` | Частичное обновление (только whitelisted-поля: `title`, `schedule`, `prompt`, `type`, `delivery`, `provider`, `model`, настройки уведомлений/таймаута/overlap/kanban, `status`, `oneShot`) |
453
+ | `DELETE` | `/dsh-cron/tasks/:id` | Удаление задачи |
454
+ | `GET` | `/dsh-cron/models` | Список LLM-провайдеров; `?provider=<id>` — модели |
455
+ | `POST` | `/dsh-cron/chat/start` | Старт агентской сессии «Создать с DSH» с инструкциями планировщика |
456
+ | `GET` | `/dsh-cron/settings` | Настройки для клиента (токен замаскирован) |
457
+ | `POST` | `/dsh-cron/settings` | Обновление настроек интеграций (через службу настроек) |
458
+ | `GET` | `/dsh-cron/heartbeat` | Probe живости: число активных задач, время последнего запуска |
459
+ | `POST` | `/dsh-cron/telegram/test` | Тестовое сообщение в Telegram |
460
+ | `POST` | `/dsh-cron/kanban/test` | Тестовая карточка в Kanban |
461
+ | `*` | `/dsh-cron/action/:id/:action` | Legacy-алиас действий над задачей (`run`, `toggle`, `delete`, `history`) |
462
+ | `GET` | `/dsh-cron/metrics` | Текст в формате Prometheus: счётчики задач и запусков — без промптов и вывода (#53) |
463
+ | `GET` / `POST` | `/dsh-cron/api/tasks` | Внешняя поверхность под токеном: список / создание-обновление (#54) |
464
+ | `GET` / `DELETE` | `/dsh-cron/api/tasks/:id` | Внешняя поверхность под токеном: чтение / удаление (#54) |
465
+ | `POST` | `/dsh-cron/api/tasks/:id/run` | Внешняя поверхность под токеном: принудительный запуск (#54) |
466
+
467
+ ---
468
+
469
+ ## 🧪 Тестирование
470
+
471
+ ```bash
472
+ npm test
473
+ ```
474
+
475
+ Набор покрывает разбор расписаний, движок планировщика, атомарное хранилище, HTTP-хелперы, уведомления и контракт инструментов.
476
+
477
+ ---
478
+
479
+ ## 📄 Лицензия
480
+
481
+ MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)