@goodandready/dsh-cron 0.2.14 → 0.2.15

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
@@ -382,6 +382,15 @@ Developer-facing, no behaviour change. `parseScheduleExpression` was split into
382
382
 
383
383
  ---
384
384
 
385
+ ### 27. Modal Dialog Viewport Bounds & Package Hygiene (v0.2.15, #153, #148, #151, #152)
386
+ - **Viewport Height Bounding & Sticky Footer**: Modal forms (including task creation, editing, and ready recipe presets) now strictly respect viewport boundaries with `max-height: min(90vh, calc(100vh - 36px))` and smooth internal scrolling. The modal action footer (`Cancel`, `Save`, `Create`) is pinned via sticky positioning (`position: sticky`), guaranteeing that critical buttons remain immediately accessible and never clipped regardless of form complexity or display scale.
387
+ - **Overlay Scroll Protection**: Modal overlays now provide safe viewport padding and overflow handling (`overflow-y: auto`), preventing flexbox centering clipping on small screens.
388
+ - **Package Weight Optimization**: Removed redundant documentation duplicates from npm distribution files, trimming tarball weight by over 32 kB and unpacked size by ~102 kB.
389
+ - **Cordis Client Inject Compliance**: Explicitly declared `locale` and `slots` dependencies in `package.json` client injection manifest (`dsh.client.inject`).
390
+
391
+ ---
392
+
393
+
385
394
 
386
395
  ## 📦 Installation
387
396
 
package/README.ru.md CHANGED
@@ -258,6 +258,15 @@ cron_create_task({
258
258
 
259
259
  ---
260
260
 
261
+ ### 27. Ограничение высоты модальных окон и гигиена пакета (v0.2.15, #153, #148, #151, #152)
262
+ - **Ограничение по высоте экрана и липкий подвал**: Модальные окна (включая создание, редактирование и готовые шаблоны-рецепты) теперь строго ограничены высотой экрана `max-height: min(90vh, calc(100vh - 36px))` с плавным внутренним скроллом. Подвал окна с кнопками («Отмена», «Сохранить», «Создать») закреплён через `position: sticky`, гарантируя постоянную доступность кнопок действий при любой длине формы и любом разрешении экрана.
263
+ - **Защита оверлея от вылетов**: Оверлей модального окна получил безопасные отступы и свойство `overflow-y: auto`, исключая срезание контента при flexbox-центрировании на компактных дисплеях.
264
+ - **Оптимизация размера npm-пакета**: Удалены дублирующие файлы документации из списка дистрибуции npm, снизив вес архива более чем на 32 kB, а распакованный размер — на 102 kB.
265
+ - **Соответствие манифеста клиента Cordis**: В `package.json` явно задекларированы зависимости инжекции `locale` и `slots` в секции `dsh.client.inject`.
266
+
267
+ ---
268
+
269
+
261
270
 
262
271
  ## 📦 Установка
263
272
 
package/README.zh.md CHANGED
@@ -381,6 +381,15 @@ bash deploy.sh verify [exact-version]
381
381
 
382
382
  ---
383
383
 
384
+ ### 27. 弹窗视口高度自适应与包体积精简 (v0.2.15, #153, #148, #151, #152)
385
+ - **视口高度约束与粘性底部操作栏**:所有弹窗(包括任务编辑、新建及推荐模板预设)现已严格受控于视口尺寸 `max-height: min(90vh, calc(100vh - 36px))`,并内置平滑纵向滚动条。弹窗操作栏(“取消”、“保存”、“创建”)采用 `position: sticky` 底部悬浮固定,确保无论表单项多长或屏幕分辨率高低,操作按钮始终清晰可见且可随时点击。
386
+ - **遮罩层滚动溢出保护**:弹窗遮罩层增加了安全边距与 `overflow-y: auto`,防止小屏设备在 Flex 居中时发生头部或底部截断。
387
+ - **npm 包体积深度精简**:从 npm 分发清单中剔除了多余的重复文档副本,使 tarball 体积立减 32 kB 以上,解压后体积减少约 102 kB。
388
+ - **Cordis 客户端注入依赖规范化**:在 `package.json` 的 `dsh.client.inject` 中完整声明了 `locale` 和 `slots` 服务依赖。
389
+
390
+ ---
391
+
392
+
384
393
 
385
394
  ## 📦 安装
386
395
 
package/lib/client.js CHANGED
@@ -883,9 +883,13 @@ window.__ModuleLoader__.load({
883
883
  .dsh-cron-rec-time { font-size: 13px; color: var(--dsw-alias-label-secondary, #9ca3af); font-weight: normal; margin-left: 8px; }
884
884
  .dsh-cron-rec-desc { font-size: 13px; color: var(--dsw-alias-label-tertiary, #71717a); }
885
885
 
886
- .dsh-cron-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; z-index: 1000; }
887
- .dsh-cron-modal { background: var(--dsw-alias-bg-layer-2, #1f1f1f); border: 1px solid var(--dsw-alias-border-l2, #333); border-radius: 16px; width: 100%; max-width: 540px; padding: 24px; box-shadow: 0 20px 40px rgba(0,0,0,0.6); box-sizing: border-box; }
888
- .dsh-cron-modal-scroll { max-width: 620px; max-height: 88vh; overflow-y: auto; }
886
+ .dsh-cron-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.75); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 16px; box-sizing: border-box; overflow-y: auto; }
887
+ .dsh-cron-modal { background: var(--dsw-alias-bg-layer-2, #1f1f1f); border: 1px solid var(--dsw-alias-border-l2, #333); border-radius: 16px; width: 100%; max-width: 540px; max-height: min(90vh, calc(100vh - 36px)); overflow-y: auto; padding: 24px; box-shadow: 0 20px 40px rgba(0,0,0,0.6); box-sizing: border-box; }
888
+ .dsh-cron-modal-scroll { max-width: 640px; max-height: min(90vh, calc(100vh - 36px)); overflow-y: auto; }
889
+ .dsh-cron-modal::-webkit-scrollbar { width: 6px; }
890
+ .dsh-cron-modal::-webkit-scrollbar-track { background: transparent; }
891
+ .dsh-cron-modal::-webkit-scrollbar-thumb { background: var(--dsw-alias-border-l2, #383838); border-radius: 3px; }
892
+ .dsh-cron-modal::-webkit-scrollbar-thumb:hover { background: var(--dsw-alias-label-tertiary, #555); }
889
893
  .dsh-cron-section-head { display: flex; align-items: center; justify-content: space-between; width: 100%; background: none; border: none; padding: 0; margin-bottom: 10px; font: inherit; font-weight: 500; font-size: 12.5px; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; }
890
894
  .dsh-cron-section-head:hover { color: var(--dsw-alias-label-primary, #eee); }
891
895
  .dsh-cron-section-chevron { transition: transform 0.15s ease; display: inline-block; }
@@ -899,7 +903,7 @@ window.__ModuleLoader__.load({
899
903
  .dsh-cron-form-group select { cursor: pointer; }
900
904
  .dsh-cron-form-group textarea { min-height: 80px; resize: vertical; }
901
905
  .dsh-cron-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
902
- .dsh-cron-modal-foot { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin-top: 20px; }
906
+ .dsh-cron-modal-foot { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin-top: 20px; position: sticky; bottom: -24px; background: var(--dsw-alias-bg-layer-2, #1f1f1f); border-top: 1px solid var(--dsw-alias-border-l2, #2e2e2e); padding-top: 14px; padding-bottom: 4px; margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; z-index: 10; }
903
907
  .dsh-cron-btn-primary { background: var(--dsw-alias-label-primary, #ededed); color: var(--dsw-alias-bg-layer-3, #111); border: 1px solid transparent; border-radius: 8px; padding: 7px 14px; font-size: 13px; font-weight: 500; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
904
908
  .dsh-cron-btn-primary:hover:not(:disabled) { opacity: 0.88; }
905
909
  .dsh-cron-btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
@@ -984,6 +988,7 @@ window.__ModuleLoader__.load({
984
988
  .dsh-cron-channel-grid { grid-template-columns: 1fr; }
985
989
  .dsh-cron-filter-panel { grid-template-columns: 1fr; }
986
990
  .dsh-cron-modal { padding: 16px; border-radius: 12px; }
991
+ .dsh-cron-modal-foot { bottom: -16px; margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; }
987
992
  .dsh-cron-modal-scroll { max-height: 92vh; }
988
993
  .dsh-cron-task-item { flex-wrap: wrap; gap: 10px; }
989
994
  .dsh-cron-task-actions { margin-left: auto; }
@@ -2320,7 +2325,7 @@ window.__ModuleLoader__.load({
2320
2325
  // 1. Manual creation / Edit modal
2321
2326
  manualModalOpen && React.createElement('div', { className: 'dsh-cron-modal-overlay', onClick: () => setManualModalOpen(false) },
2322
2327
  React.createElement('div', {
2323
- className: 'dsh-cron-modal',
2328
+ className: 'dsh-cron-modal dsh-cron-modal-scroll',
2324
2329
  style: { maxWidth: '640px' },
2325
2330
  role: 'dialog',
2326
2331
  'aria-modal': 'true',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-cron",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "description": "Scheduled cron tasks, background automation and agent execution for DeepSeek Harness.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -14,8 +14,6 @@
14
14
  "lib/",
15
15
  "cordis.patch.yml",
16
16
  "README.md",
17
- "docs/README.ru.md",
18
- "docs/README.zh.md",
19
17
  "LICENSE"
20
18
  ],
21
19
  "scripts": {
@@ -44,7 +42,10 @@
44
42
  },
45
43
  "client": {
46
44
  "platform": "web",
47
- "inject": []
45
+ "inject": [
46
+ "locale",
47
+ "slots"
48
+ ]
48
49
  }
49
50
  },
50
51
  "dependencies": {
package/docs/README.ru.md DELETED
@@ -1,512 +0,0 @@
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
- ### 24. Автоматическое подключение пресетов агента и инструментов (#141 / GH-1, добавлено в v0.2.12)
232
- - **Автоматическое монтирование пресета агента**: Запланированные автономные `llm`-задачи и интерактивные запуски агента теперь автоматически определяют и подключают пресет агента системы (по умолчанию используется стандартный пресет пользователя через `presets.mount(agentCtx, preset.id)` внутри хука `setup`). Автономные сессии по расписанию получают полный доступ к инструментам (файлы, рабочее окружение, терминал и т.д.) вместо изолированного чата без инструментов.
233
- - **Индивидуальный пресет для задачи**: Для каждой задачи можно явно задать идентификатор `agentPreset` в веб-интерфейсе, через REST API или в декларативных задачах профиля (например, `coding`, `system`, `minimal`). Если поле не заполнено, автоматически применяется пресет по умолчанию из настроек харнесса.
234
- - **Безопасная деградация**: Если сервис `agentPresets` недоступен или указан несуществующий пресет, планировщик выводит информативное предупреждение и штатно продолжает выполнение модели без аварийной остановки задачи.
235
-
236
- ---
237
-
238
- ### 25. Долговременные сессии и непрерывность контекста (`targetSessionId`, добавлено в v0.2.13, #143)
239
- - **Непрерывный контекст диалога**: Для задач можно задать `targetSessionId`. При наличии этого идентификатора планировщик возобновляет существующую сессию через `agents.resume()` вместо создания одноразовой сессии (`cron-exec-${id}-${uuid}`) на каждом тике. Агент сохраняет память предыдущих ходов и может ссылаться на ранее обнаруженные данные и выводы.
240
- - **Защита от переполнения контекста и ротация (`targetSessionReset`)**: Чтобы контекстное окно и расход токенов не разрастались бесконечно при частых запусках, предусмотрены политики автоматической ротации:
241
- - `never`: единая непрерывная сессия без сброса.
242
- - `daily`: ежедневная автоматическая ротация (`<id>-YYYY-MM-DD`).
243
- - `weekly`: еженедельная автоматическая ротация (`<id>-YYYY-Www`).
244
- - Шаблоны дат: в `targetSessionId` поддерживается плейсхолдер `{{date}}`, который автоматически заменяется на текущую дату `YYYY-MM-DD`.
245
- - **Видимость в списке чатов DSH**: Долговременные сессии не помечаются как `ephemeral`/`internal` и исключены из автоматической архивации (`sessions.archive()`), поэтому они остаются доступны для чтения и прямого диалога в веб-интерфейсе DSH.
246
- - **Совместимость с пресетами и инструментами**: При возобновлении сессии автоматически подключаются инструменты пресета `agentPreset`, гарантируя доступ к терминалу, файлам и командам.
247
- - *Благодарность*: концепция вдохновлена разработкой [@RaulLazaro](https://github.com/RaulLazaro).
248
-
249
- ---
250
-
251
- ### 26. Пакет надежности, отказоустойчивости и самовосстановления (v0.2.14, #145)
252
- - **Автоматический сброс бюджета повторов**: Исправлена «амнезия повторов». Когда задача исчерпывает лимит попыток (`maxRetries`), счётчик `attempts` автоматически обнуляется, поэтому следующий плановый запуск по расписанию получает полный бюджет повторов с нуля. Любой регулярный или ручной запуск гарантированно начинает выполнение со сброшенным счётчиком попыток.
253
- - **Устранение «зомби»-задач в очереди**: Задачи, приостановленные через интерфейс/API или удалённые, мгновенно вычищаются из очереди ожидания параллелизма (`this.queue`). При освобождении слотов очереди неактивные или удалённые задачи безопасно пропускаются.
254
- - **Ограничение архива истории**: В длительно работающих инсталляциях с высокочастотными cron-задачами файл архива `tasks-history-archive.json` теперь надёжно ограничен последними 1 000 запусками на задачу, предотвращая неконтролируемый рост диска и синхронные задержки сериализации JSON.
255
- - **Аварийное восстановление и авто-бэкап хранилища**: `TaskStore` автоматически поддерживает атомарную резервную копию `tasks.json.bak` при каждом успешном сохранении. В случае сбоя или повреждения файла хранилище делает снимок `tasks.json.corrupted.<timestamp>` для диагностики и бесшовно восстанавливается из резервной копии.
256
- - **Самовосстановление при переполнении контекстного окна**: Если в долговременной сессии (`targetSessionId`) очередной ход агента завершается ошибкой переполнения контекста модели (`context_length_exceeded`), раннер распознаёт переполнение, архивирует исчерпанную сессию, автоматически выполняет ротацию на свежую сессию и прозрачно повторяет выполнение без срыва задачи.
257
- - **Корректное завершение дерева процессов в Windows**: На платформе Windows отмена или таймаут внешних скриптовых задач теперь вызывают `taskkill /pid <pid> /T /F`, гарантируя полное уничтожение всех дочерних процессов и оболочек без зависания зомби-процессов в системе.
258
-
259
- ---
260
-
261
-
262
- ## 📦 Установка
263
-
264
- ```bash
265
- dsh plugin --profile web add @goodandready/dsh-cron
266
- ```
267
-
268
- Перезапустите DeepSeek Harness и обновите страницу в браузере.
269
-
270
- ---
271
-
272
- ## ⚙️ Конфигурация (`settings.yaml`)
273
-
274
- Конфигурацию можно задать в `settings.yaml` или интерактивно через карточку настроек плагина в DSH:
275
-
276
- ```yaml
277
- # settings.yaml
278
- dsh-cron:
279
- botToken: "" # токен Telegram Bot API (секретное поле)
280
- chatId: "" # ID чата Telegram для отчётов
281
- notifyTelegram: false # глобально отправлять отчёты о всех задачах
282
- onlyOnFailure: false # отправлять отчёты только при сбоях
283
- kanbanBaseUrl: "http://127.0.0.1:3000" # базовый URL HTTP API dsh-kanban
284
- defaultTimezone: "" # IANA-зона по умолчанию (пусто = серверное время)
285
- maxConcurrent: 0 # максимум параллельных запусков (0 = без лимита)
286
- heartbeatUrl: "" # URL dead man's snitch, пингуется по интервалу
287
- heartbeatIntervalSec: 0 # интервал heartbeat-пинга в секундах (0 = выключено)
288
- # --- каналы доставки ---
289
- botTokenRef: "" # ИМЯ credential для токена Telegram-бота
290
- template: "" # глобальный шаблон сообщения, напр. "⏰ {title} — {status}"
291
- channelTemplates: {} # переопределения шаблонов по каналам
292
- deliveryTimeoutMs: 15000 # таймаут доставки на канал; медленный канал = сбой, остальные не ждут
293
- discordWebhookUrl: "" # webhook Discord
294
- slackWebhookUrl: "" # incoming webhook Slack
295
- ntfyUrl: "https://ntfy.sh" # сервер ntfy; ntfyTopic / ntfyTokenRef
296
- ntfyTopic: ""
297
- ntfyTokenRef: ""
298
- barkServerUrl: "https://api.day.app" # сервер Bark; barkKey — ключ устройства
299
- barkKey: ""
300
- pushplusUrl: "https://www.pushplus.plus/send" # pushplusTokenRef
301
- pushplusTokenRef: ""
302
- ttsBaseUrl: "http://127.0.0.1:3080" # базовый URL dsh-tts
303
- giteaBaseUrl: "" # giteaRepo = owner/repo, giteaTokenRef = ИМЯ credential
304
- giteaRepo: ""
305
- giteaTokenRef: ""
306
- # --- внешний REST API (#54) ---
307
- apiToken: "" # bearer-токен внешнего префикса /dsh-cron/api/* (маскируется; пусто = 503)
308
- ```
309
-
310
- ### 14. Мониторинг heartbeat (dead man's switch)
311
- * Задайте `heartbeatUrl` и `heartbeatIntervalSec` в настройках плагина — планировщик будет пинговать этот адрес по расписанию, и внешний монитор сообщит, когда пинги прекратятся.
312
- * Встроенный эндпоинт `GET /dsh-cron/heartbeat` сообщает живость, число активных задач и время последнего запуска для ваших собственных сторожей.
313
-
314
- ### 15. Задачи из конфига профиля (#50)
315
- Долгоживущие эксплуатационные задачи можно объявлять в конфиге профиля, а не пересоздавать руками в интерфейсе. Владелец объявленных задач — файл конфига: при каждом старте плагина они создаются или обновляются, а задача, исчезнувшая из файла, удаляется.
316
-
317
- Добавьте список `jobs` в секцию плагина конфига профиля (`cordis.patch.yml`):
318
-
319
- ```yaml
320
- dsh-cron:
321
- jobs:
322
- - id: nightly-backup
323
- title: Nightly backup
324
- schedule: "0 3 * * *"
325
- type: script
326
- prompt: "bash /path/to/backup.sh"
327
- channels: ["telegram"]
328
- timeoutSeconds: 3600
329
- - id: morning-digest
330
- title: Morning digest
331
- schedule: "0 8 * * 1-5"
332
- type: llm
333
- prompt: "Prepare a brief morning digest of active tasks."
334
- provider: my-provider
335
- model: provider-id/model-id
336
- ```
337
-
338
- * Обязательные поля записи: `id`, `title`, `schedule`; типам, у которых полезная нагрузка — это промпт (`script`, `node`, `python`, `ssh`, `docker`, `llm`, `skill`, `workflow`), нужен ещё непустой `prompt`. `http` — исключение: цель задаётся `httpUrl` (или `prompt`).
339
- * Остальные поля задачи проходят как есть с той же валидацией, что и в 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`).
340
- * Объявленные задачи помечаются как **управляемые конфигом**; в панели вместо действий правки и удаления выводится метка источника.
341
- * Правка, пауза, возобновление, переключение и удаление конфиг-задачи отклоняются с `409` в панели и по API, и создание-обновление через `POST /dsh-cron/tasks` с существующим `id` конфиг-задачи отклоняется так же — источник правды файл конфига. **Запустить сейчас** остаётся доступным.
342
- * Задача с тем же `id`, созданная через UI, API или инструмент агента, никогда не перезаписывается: запись пропускается, конфликт пишется в лог.
343
- * Код-исполняющие типы активируются как обычные объявленные задачи, но при старте плагин пишет предупреждение в лог — путь исполнения кода, добавленный правкой конфига, остаётся видимым.
344
- * Записи валидируются по одной с указанием индекса (`config.jobs[i]: …`); одна плохая запись пропускается и не может остановить остальные задачи или профиль.
345
-
346
- ### 16. Внешний REST API (`/dsh-cron/api/*`, #54)
347
- Внешние системы (CI, cron хоста, `curl`) могут управлять планировщиком без открытия панели. Это единственная поверхность за bearer-токеном; маршруты панели остаются локальными и защищёнными от cross-origin.
348
-
349
- Токен задаётся настройкой плагина `apiToken` (маскируется, как любой секрет). Аутентификация и ошибки:
350
- * токен не задан → вся поверхность отвечает `503`;
351
- * нет заголовка `Authorization: Bearer <token>` или токен неверный → `401`; сравнение постоянное по времени.
352
-
353
- | Метод | Путь | Описание |
354
- |:---|:---|:---|
355
- | `GET` | `/dsh-cron/api/tasks` | Список задач (фильтры `status` / `query`, как в панели) |
356
- | `GET` | `/dsh-cron/api/tasks/:id` | Чтение одной задачи |
357
- | `POST` | `/dsh-cron/api/tasks` | Создание задачи или обновление существующей при наличии `id` |
358
- | `DELETE` | `/dsh-cron/api/tasks/:id` | Удаление задачи |
359
- | `POST` | `/dsh-cron/api/tasks/:id/run` | Принудительный немедленный запуск |
360
-
361
- Операции переиспользуют обработчики панели, поэтому гейт `x-dsh-cron-confirm: script` для код-исполняющих типов и отказ `409` для конфиг-задач действуют здесь так же, как в UI.
362
-
363
- ```bash
364
- BASE="http://127.0.0.1:3080"
365
- TOKEN="<API_TOKEN>"
366
-
367
- # список
368
- curl -s -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks"
369
-
370
- # создание или обновление, если в теле есть id
371
- curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
372
- -d '{"id":"cleanup","title":"Cleanup","schedule":"0 4 * * *","prompt":"Remove stale temporary files."}' \
373
- "$BASE/dsh-cron/api/tasks"
374
-
375
- # принудительный запуск
376
- curl -s -X POST -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks/cleanup/run"
377
-
378
- # удаление
379
- curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks/cleanup"
380
-
381
- # код-исполняющей задаче нужен ещё заголовок подтверждения
382
- curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "x-dsh-cron-confirm: script" \
383
- -H "Content-Type: application/json" \
384
- -d '{"title":"Disk check","schedule":"0 * * * *","type":"script","prompt":"df -h"}' \
385
- "$BASE/dsh-cron/api/tasks"
386
- ```
387
-
388
- ### 17. Метрики Prometheus (#53)
389
- `GET /dsh-cron/metrics` отдаёт текст в формате Prometheus, поэтому планировщик можно снимать scrape'ом без новых зависимостей:
390
-
391
- * `dsh_cron_tasks_total{status}` — число задач по статусам (gauge).
392
- * `dsh_cron_task_last_duration_seconds{task}` — длительность последнего завершённого запуска задачи в секундах (gauge).
393
- * `dsh_cron_runs_total{status}` — завершённые запуски с момента старта процесса плагина (counter); статусы `success`, `error`, `timeout`, `skipped`, `missed`.
394
- * `dsh_cron_run_records` — число записей о запусках, хранимых в памяти (gauge).
395
-
396
- В экспозицию попадают только счётчики, статусы и длительности; промпты, вывод запусков и конфигурация задач в неё не входят.
397
-
398
- ```yaml
399
- scrape_configs:
400
- - job_name: dsh-cron
401
- static_configs:
402
- - targets: ["127.0.0.1:3080"]
403
- metrics_path: /dsh-cron/metrics
404
- ```
405
-
406
- ### 18. Строгая проверка каналов (#121)
407
- Создание или обновление задачи с неизвестным идентификатором канала теперь отклоняется с `400`, а виновники перечисляются в ответе:
408
-
409
- ```json
410
- { "ok": false, "error": "Unknown channel ids: email_ping", "unknownChannels": ["email_ping"] }
411
- ```
412
-
413
- Changed in v0.2.7: раньше неизвестный идентификатор молча отбрасывался, поэтому клиент с опечаткой получал `ok: true` и задачу, которая никуда не доставляет.
414
-
415
- Импорт намеренно остаётся терпимым (файл может быть из старой версии): неизвестные идентификаторы отбрасываются у импортируемой задачи, но перечисляются в ответе (`unknownChannels`) и пишутся в лог планировщика, а не исчезают молча.
416
-
417
- ### 19. Проверка после установки (#126)
418
- У `deploy.sh` есть режим только-проверки уже установленного профиля, ничего не устанавливающий:
419
-
420
- ```bash
421
- bash deploy.sh verify [exact-version]
422
- ```
423
-
424
- Он проверяет, что профиль сообщает нужную версию (по умолчанию — версия из `package.json`), аутентифицируется в web UI, затем скачивает клиентский бандл и убеждается, что имя пакета в нём присутствует.
425
-
426
- Зачем это нужно: web-профиль может стоять за плагином аутентификации и отвечать `401` на анонимный запрос, а клиентский бандл плагина отдаётся только по точному combined-URL вида `??` из аутентифицированного индекса — голый `/plugins/<name>/client.js` отвечает `404`. Поэтому проверка сначала строит аутентифицированную сессию.
427
-
428
- Переменные окружения проверки: `DSH_WEB_BASE` (по умолчанию `http://127.0.0.1:3080`), `DSH_WEB_TOKEN` (токен; если не задан, скрипт берёт последний из журнала юнита), `DSH_WEB_UNIT` (по умолчанию `dsh-web.service`). Секретов в скрипте нет.
429
-
430
- ### 20. Внутренняя разбивка: разбор расписания и постановка (#97)
431
- Только для разработчиков, поведение не меняется. `parseScheduleExpression` разбит на маленькие функции с тем же порядком ветвей — `parseAtExpression`, `parseRelativeOneShot`, `parseIntervalExpression`, `parseAliasExpression`, `parseCronExpression`, — а `scheduleTask` — на `clearScheduled`, `scheduleOneShot` и `scheduleCron`. Прежний набор тестов прошёл без правок, добавлены точечные тесты на приоритет ветвей и ошибки.
432
-
433
- ### Параметры
434
-
435
- | Параметр | Тип | По умолчанию | Описание |
436
- |:---|:---|:---|:---|
437
- | `botToken` | `string` | `""` | Токен Telegram Bot API. Если пусто, плагин пытается унаследовать бота, настроенного для `dsh-messenger-gateway` в настройках DSH (best-effort). Секретное поле: в интерфейсе отображается только замаскированное значение |
438
- | `chatId` | `string` | `""` | ID чата Telegram для отчётов. Пустое значение — откат к первому разрешённому чату `dsh-messenger-gateway` |
439
- | `notifyTelegram` | `boolean` | `false` | Глобальный выключатель доставки отчётов в Telegram |
440
- | `onlyOnFailure` | `boolean` | `false` | Глобальный режим «только при сбоях» (`error`/`timeout`) |
441
- | `kanbanBaseUrl` | `string` | `"http://127.0.0.1:3000"` | Базовый URL HTTP API `dsh-kanban` для автоматических карточек |
442
- | `defaultTimezone` | `string` | `""` | IANA-зона по умолчанию для расписаний; пусто = серверное время |
443
- | `maxConcurrent` | `number` | `0` | Лимит параллельных запусков; лишние помечаются `skipped` (0 = без лимита) |
444
- | `heartbeatUrl` | `string` | `""` | URL dead man's snitch, пингуемый каждый `heartbeatIntervalSec`, пока жив планировщик |
445
- | `heartbeatIntervalSec` | `number` | `0` | Интервал heartbeat-пинга в секундах (0 = выключено) |
446
- | `botTokenRef` | `string` | `""` | Имя credential DSH с токеном Telegram-бота; резолвится при отправке (фолбэк: `botToken` → настройки messenger-gateway → переменная окружения `CRON_TELEGRAM_BOT_TOKEN`) |
447
- | `template` | `string` | `""` | Глобальный шаблон сообщения с плейсхолдерами `{title}`/`{status}`/`{duration}`/…; пусто = встроенный текст |
448
- | `channelTemplates` | `object` | `{}` | Переопределения шаблонов по каналам (`telegram`, `discord`, …) |
449
- | `deliveryTimeoutMs` | `number` | `15000` | Таймаут доставки на канал; более медленный endpoint фиксируется как сбой и не задерживает остальные каналы и следующий тик |
450
- | `discordWebhookUrl` / `slackWebhookUrl` | `string` | `""` | Webhook-URL каналов Discord и Slack |
451
- | `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | Сервер ntfy, тема и опциональное имя credential токена (`Authorization: Bearer …`) |
452
- | `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Сервер Bark и ключ устройства (ключ, заголовок и текст идут в пути запроса) |
453
- | `pushplusUrl` / `pushplusTokenRef` | `string` | `"https://www.pushplus.plus/send"` / `""` | Endpoint PushPlus (переопределяется для self-hosted прокси) и имя credential токена |
454
- | `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | Базовый URL плагина `dsh-tts` для голосовых объявлений |
455
- | `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Канал Gitea: базовый URL, `owner/repo` и имя credential API-токена |
456
- | `apiToken` | `string` | `""` | Bearer-токен внешней поверхности `/dsh-cron/api/*`. Секретное поле, отдаётся замаскированным; пусто отключает поверхность (503), неверное значение — 401 |
457
-
458
- Примечания:
459
-
460
- * История запусков ограничена **50 записями на задачу** (фиксировано); в записи хранится до 4000 символов вывода.
461
- * Задачи выполняются в **локальном часовом поясе сервера**, если для задачи не указана своя IANA-зона; cron-выражения вычисляет `croner` по часам хоста.
462
- * Задачи сохраняются в каталоге данных DSH (`cron/tasks.json`) и переживают перезапуск; пропущенные one-shot запуски обнаруживаются при старте.
463
-
464
- ---
465
-
466
- ## 🔌 HTTP API
467
-
468
- Все эндпоинты обслуживаются веб-сервером DSH под `/dsh-cron/`. Чтение открыто локальному интерфейсу; **мутирующие эндпоинты отклоняют cross-origin запросы** и принимают тела до 1 МБ. Для создания `script`-задач по HTTP дополнительно требуется заголовок `x-dsh-cron-confirm: script`, который подделанный межсайтовый запрос приложить не может.
469
-
470
- | Метод | Путь | Описание |
471
- |:---|:---|:---|
472
- | `GET` | `/dsh-cron/tasks` | Список задач; параметры `status` (`all/active/paused/completed`), `query` (подстрока). Возвращает задачи, шаблоны рекомендаций и сводную статистику |
473
- | `POST` | `/dsh-cron/tasks` | Создание или обновление задачи (при наличии `id` — обновление). Обязательны `title`, `schedule`, `prompt` |
474
- | `GET` | `/dsh-cron/tasks/:id/history` | История запусков, `?limit=20` |
475
- | `POST` | `/dsh-cron/tasks/:id/run` | Немедленный ручной запуск |
476
- | `POST` | `/dsh-cron/tasks/:id/pause` | Пауза расписания |
477
- | `POST` | `/dsh-cron/tasks/:id/resume` | Возобновление расписания |
478
- | `POST` | `/dsh-cron/tasks/:id/toggle` | Переключение активна/на паузе |
479
- | `POST` | `/dsh-cron/tasks/:id/duplicate` | Копия задачи в статусе «на паузе»: настройки копируются, история и счётчики сбрасываются |
480
- | `GET` | `/dsh-cron/recipes` | Встроенный каталог рецептов: готовые мониторинговые пресеты по категориям, все только на чтение |
481
- | `GET` | `/dsh-cron/tasks/export` | Версионированный JSON только с конфигурацией задач — без истории и счётчиков. Каналы ссылаются на credential по имени, но введённые вручную `env` и HTTP-заголовки задачи являются частью конфигурации и попадают в файл |
482
- | `POST` | `/dsh-cron/tasks/import` | Проверяет документ и применяет его стратегией `add`, `replace` или `skip`; поддерживает `dryRun`. Импортированные задачи всегда приходят **на паузе** — восстановление не сработает само |
483
- | `PATCH` | `/dsh-cron/tasks/:id` | Частичное обновление (только whitelisted-поля: `title`, `schedule`, `prompt`, `type`, `delivery`, `provider`, `model`, настройки уведомлений/таймаута/overlap/kanban, `status`, `oneShot`) |
484
- | `DELETE` | `/dsh-cron/tasks/:id` | Удаление задачи |
485
- | `GET` | `/dsh-cron/models` | Список LLM-провайдеров; `?provider=<id>` — модели |
486
- | `POST` | `/dsh-cron/chat/start` | Старт агентской сессии «Создать с DSH» с инструкциями планировщика |
487
- | `GET` | `/dsh-cron/settings` | Настройки для клиента (токен замаскирован) |
488
- | `POST` | `/dsh-cron/settings` | Обновление настроек интеграций (через службу настроек) |
489
- | `GET` | `/dsh-cron/heartbeat` | Probe живости: число активных задач, время последнего запуска |
490
- | `POST` | `/dsh-cron/telegram/test` | Тестовое сообщение в Telegram |
491
- | `POST` | `/dsh-cron/kanban/test` | Тестовая карточка в Kanban |
492
- | `*` | `/dsh-cron/action/:id/:action` | Legacy-алиас действий над задачей (`run`, `toggle`, `delete`, `history`) |
493
- | `GET` | `/dsh-cron/metrics` | Текст в формате Prometheus: счётчики задач и запусков — без промптов и вывода (#53) |
494
- | `GET` / `POST` | `/dsh-cron/api/tasks` | Внешняя поверхность под токеном: список / создание-обновление (#54) |
495
- | `GET` / `DELETE` | `/dsh-cron/api/tasks/:id` | Внешняя поверхность под токеном: чтение / удаление (#54) |
496
- | `POST` | `/dsh-cron/api/tasks/:id/run` | Внешняя поверхность под токеном: принудительный запуск (#54) |
497
-
498
- ---
499
-
500
- ## 🧪 Тестирование
501
-
502
- ```bash
503
- npm test
504
- ```
505
-
506
- Набор покрывает разбор расписаний, движок планировщика, атомарное хранилище, HTTP-хелперы, уведомления и контракт инструментов.
507
-
508
- ---
509
-
510
- ## 📄 Лицензия
511
-
512
- MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
package/docs/README.zh.md DELETED
@@ -1,512 +0,0 @@
1
- # 📦 @goodandready/dsh-cron
2
-
3
- <div align="center">
4
-
5
- <h3>面向 DeepSeek Harness 的定时 Cron 调度、后台自动化与智能体任务执行引擎</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 上为它点亮 Star</strong> — 这能让我知道插件对您有用,并鼓励我继续开发和维护它。
28
- <br><br>
29
- 🐛 <strong>如果您发现 Bug 或希望增加功能</strong>,请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议,并在后续版本中实现有价值的改进。
30
- </td>
31
- </tr>
32
- </table>
33
-
34
- </div>
35
-
36
- ---
37
-
38
- ## ⚡ 概述与问题
39
-
40
- 自主 AI 智能体经常需要执行周期性任务:生成每日晨报、整理缺陷跟踪、检查 API 健康状态、同步数据库或定期执行 Git 清理。如果 Harness 内没有专用调度器,用户只能依赖外部 crontab 封装、复杂的 webhook 方案或手动干预。
41
-
42
- **`@goodandready/dsh-cron`** 是 DeepSeek Harness 的原生全栈调度与后台自动化插件。它将标准 cron 表达式、自然语言间隔语法与自主智能体执行连接起来:
43
-
44
- 1. **完善的可视化任务管理器** —— 侧边栏按钮带可折叠的活跃任务列表(下次运行时间或实时状态,行数有上限且状态可记忆),以及功能齐全的面板:按类型、模型、渠道筛选,暂停、立即运行、复制、导出/导入与创建任务。
45
- 2. **交互式“由 DSH 创建”流程** —— 与智能体对话,把高层需求转化为规范的定时任务。
46
- 3. **自主工具调用** —— 原生 `cron_*` 工具让智能体在会话中自行安排后续执行。
47
- 4. **健壮的调度器与原子存储** —— 基于 `croner`:间隔别名、一次性延时任务、原子写入、运行历史与成本追踪。
48
- 5. **六种执行运行时** —— shell、Node.js、Python、HTTP/webhook、远程 SSH 与 Docker,并支持按任务的环境变量、工作区绑定以及面向代码修改任务的隔离 git worktree。
49
- 6. **多渠道路由与模板** —— 一次运行可投递到 Telegram、dsh-kanban、Discord、Slack、ntfy、Bark、PushPlus、语音(`dsh-tts`)与 Gitea,支持 `{变量}` 消息模板与按 DSH 凭据名称引用的密钥。
50
-
51
- ---
52
-
53
- ## 🏗️ 架构
54
-
55
- ```mermaid
56
- graph TD
57
- subgraph Client ["Web 客户端 (DSH UI)"]
58
- SidebarBtn["侧边栏时钟按钮<br/>(DSH 客户端插槽)"]
59
- Overlay["任务管理面板<br/>(标签: 全部 / 活跃 / 暂停 / 已完成)"]
60
- CreateWithDSH["“由 DSH 创建”对话框<br/>(自然语言任务)"]
61
- ManualForm["手动任务表单<br/>(运行时、cron、超时、重叠策略、渠道)"]
62
- SettingsCard["设置卡片<br/>(渠道、模板、凭据)"]
63
- end
64
-
65
- subgraph Server ["服务端 (Cordis 与 DSH 服务)"]
66
- HttpRoutes["HTTP REST API<br/>(/dsh-cron/*)"]
67
- AgentTools["工具调用网关<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["凭据引用<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 -->|按间隔/一次性触发| AgentRunner
86
- Scheduler --> Notify
87
- ```
88
-
89
- ---
90
-
91
- ## ✨ 功能与能力
92
-
93
- ### 1. 可视化任务管理器
94
- 点击 DSH 侧边栏中的时钟图标(位于“新会话”按钮旁)打开管理面板:
95
- * **状态过滤标签**:**全部**、**活跃**、**已暂停**、**已完成**。
96
- * **即时操作**:立即运行(**Run Now**)、暂停/恢复调度、带确认的删除。
97
- * **一键预设模板**:*每日摘要*、*每周回顾*、*待办监控*。
98
- * **运行历史**:打开任务卡片查看历史运行 —— 时间、耗时、状态(成功 / 失败 / 超时 / 跳过 / 错过)、输出与错误。
99
- * **汇总统计栏**:活跃任务数、总运行次数、总 token 消耗与估算美元成本。
100
-
101
- ### 2. “由 DSH 创建”对话框
102
- 无需猜测 cron 语法,用自然语言即可创建任务:
103
- 1. 点击 **Create ⌄** ➔ **Create with DSH**。
104
- 2. 描述要自动化的内容(例如:*“每个工作日早上 9 点检查未处理的 PR 并起草评论”*)。
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` | 列出任务的状态、下次运行时间、token 总量与成本估算 |
114
- | `cron_pause_task` | 暂停调度而不删除配置 |
115
- | `cron_resume_task` | 恢复已暂停的调度 |
116
- | `cron_delete_task` | 永久删除任务及其历史 |
117
- | `cron_run_task` | 触发一次立即的带外运行 |
118
- | `cron_get_task` | 读取单个任务的完整配置,包括列表中看不到的字段 |
119
- | `cron_update_task` | 就地修改现有任务(白名单字段,校验与 HTTP 路由一致);提示模型先与用户确认会执行代码的改动 |
120
-
121
- 会话中模型可进行的调用示例:
122
-
123
- ```
124
- cron_create_task({
125
- "title": "Morning digest",
126
- "schedule": "0 8 * * 1-5",
127
- "prompt": "Prepare a brief morning digest of active tasks and open tickets.",
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`(迟执行并记录缺口)。`skip` 下错过的一次性任务直接转为 `completed`,不再过期触发。
147
- * **并发上限** —— 插件设置 `maxConcurrent` 限制并行运行数;超出的运行记录为 `skipped` 并附原因。
148
- * **实时执行指示** —— 任务列表中的脉冲状态图标与运行计时器。
149
-
150
- ### 6. 执行运行时
151
- 每个任务可选择自己的运行时;非 LLM 运行时不需要模型,也不消耗 token:
152
-
153
- * **Shell**(`script`)—— 通过 Harness shell 执行命令或脚本,支持 `env` 与 `cwd`。
154
- * **Node.js**(`node`)与 **Python**(`python`)—— 指定解释器(`nodePath`、`pythonPath`)运行片段;Python 会自动识别项目虚拟环境。
155
- * **HTTP**(`http`)—— 以自定义请求头与请求体访问 URL,状态码与响应写入运行历史。
156
- * **SSH**(`ssh`)—— 通过 `dsh-remote-workspace` 配置(`sshProfileId`)或独立 host/key 字段在远程主机执行命令。
157
- * **Docker**(`docker`)—— 在镜像容器(`dockerImage`)中执行命令。
158
- * **环境变量** —— 按任务的 `env` 映射(界面中每行 KEY VALUE)应用于外部运行时;请勿在此存放密钥。
159
- * **工作区与 worktree** —— 将任务绑定到 Harness 工作区(`workspaceId`);对会修改代码的智能体任务,可在隔离的 git worktree 中运行(`worktree`、`keepWorktree`)。
160
-
161
- ### 7. 成本控制:回退模型
162
- 任务可以默认使用便宜模型,失败时改用更强模型完成:设置 `fallbackModel`(可选 `fallbackProvider`),失败(`error` 或 `timeout`)的运行会在该模型上重试一次,之后才进入常规重试退避。历史记录会标明最终产出结果的模型以及是否使用了回退,两次尝试的用量与成本都会累计,模板变量 `{model}` 渲染完成运行的模型。回退仅适用于智能体类型(`llm`、`skill`、`workflow`)。
163
-
164
- ### 8. 会话集成与权限
165
- * **按任务的权限预设** —— `default`、`read-only`、`workspace-write` 或 `full` 在提示词执行前应用于任务会话。
166
- * **会话自动归档** —— 隔离的 cron 会话在运行后自动归档(尽力而为),不干扰聊天列表。
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`)以及 Gitea issue:
177
-
178
- * **任务迁移** —— 将全部配置导出为版本化 JSON,并在别处导入(含预览摘要);导入的任务处于暂停状态。
179
- * **按任务选择渠道** —— 在任务表单中勾选渠道;显式选择会覆盖旧版 `notifyTelegram`/`kanbanMode` 开关,留空则回退到它们。
180
- * **故障隔离** —— 某个渠道不可用会记录在调度器日志中,其余渠道仍会收到报告;失效的 webhook 不会吞掉整份报告。
181
- * **消息模板** —— 支持全局模板、按渠道覆盖或按任务模板,变量为 `{title} {id} {status} {output} {error} {duration} {schedule} {time} {tokens} {cost}`。未知占位符保持原样,失败运行默认使用失败模板。
182
- * **`onlyOnFailure`** —— 全局或按任务生效:成功运行静默,仅发送 `error`/`timeout`。
183
- * **凭据按名称引用** —— webhook token 与 Telegram bot token 填写 DSH 凭据的名称(`botTokenRef`、`ntfyTokenRef`、`pushplusTokenRef`、`giteaTokenRef`),发送时通过 DSH credentials 服务解析,并可回退到环境变量,且绝不会经过插件设置。webhook URL 与 Bark 设备键本身内嵌密钥,因此保存在插件设置文件中,但返回浏览器时始终为掩码,界面回传的掩码值也不会覆盖已保存的值。
184
- * **投递超时** —— 每个渠道请求都有上限(`deliveryTimeoutMs`,默认 15000 毫秒,可在设置面板或 `settings.yaml` 中调整),且各渠道并发发送:无响应的端点只记录为失败,不会拖慢其他渠道或下一次调度。限制作用于整个渠道处理过程,也覆盖凭据解析——它不支持 abort 信号。
185
- * **Telegram** —— 带状态徽标(✅ / ❌)、耗时、调度描述与等宽输出块的 Markdown 报告;动态值会被转义。凭据可直接填写,或从 DSH `settings.yaml` 的 `dsh-messenger-gateway` 段继承(尽力而为)。
186
- * **Discord / Slack** —— 通过 webhook 投递:Discord 使用按运行状态着色的 embed,Slack 使用纯文本正文。
187
- * **ntfy / Bark / PushPlus** —— 移动推送,支持主题/设备键与可选 bearer token;Bark 的标题与正文放在请求路径中,PushPlus 端点可指向自建代理。
188
- * **语音** —— `dsh-tts` 通过其 HTTP 路由朗读报告(`ttsBaseUrl`,默认 `http://127.0.0.1:3080`)。
189
- * **Gitea** —— 创建包含运行报告的 issue(`giteaBaseUrl`、`giteaRepo`、token 凭据);失败运行标记为 `cron`、`bug`、`alert`。
190
- * **测试发送按钮** —— 在安排关键任务前现场验证 Telegram 连通性。
191
-
192
- ### 12. Kanban 集成与成本统计
193
- * **自动创建 Kanban 卡片** —— 当 `kanbanMode` 为 `on_failure` 或 `always` 时,插件在 `dsh-kanban` 中创建卡片(`on_failure` → `error`/`timeout` 时进入 *Backlog*;`always` → 完成后进入 *Done*/*Backlog*)。
194
- * **Token 与执行成本计量** —— 按运行与任务统计 token 消耗(输入、输出、缓存读取),基于内置价格表估算美元成本,并提供汇总分析栏。
195
-
196
- ### 13. 重叠策略与执行超时
197
-
198
- * **执行超时(`timeoutSeconds`)** —— 达到限制后,shell 子进程通过 abort 信号立即终止,智能体会话被释放以停止消耗 token。默认 `1800`(30 分钟)。
199
- * **重叠策略(`overlapPolicy`)** —— 上一次运行尚未结束时再次触发调度时的行为:
200
- * **`skip`**(默认):丢弃重叠的运行,在历史中记录 `skipped`;
201
- * **`queue`**:将下一次运行排队,当前任务完成后自动开始;
202
- * **`replace`**:通过 `AbortController` 中止当前运行并启动新的执行。
203
-
204
- 如果守护进程在计划时刻处于离线状态,启动时该次运行会被记录为 `missed`,历史空档始终可见。
205
-
206
- ### 14. 心跳监控(Dead man's switch)
207
- * 在插件设置中配置 `heartbeatUrl` 与 `heartbeatIntervalSec`,调度器会按间隔 GET 该地址 —— 外部监控可在心跳停止时告警。
208
- * 内置 `GET /dsh-cron/heartbeat` 端点返回存活状态、活跃任务数与最近运行时间,便于自建看门狗。
209
-
210
- ### 15. 来自配置的声明式任务(#50)
211
- 长期运行的任务可以直接声明在配置文件里,而无需在界面中手工重建。配置文件拥有这些任务:每次插件启动时会创建或更新它们,从文件中消失的任务会被删除。
212
-
213
- 在配置文件(`cordis.patch.yml`)的插件段加入 `jobs` 列表:
214
-
215
- ```yaml
216
- dsh-cron:
217
- jobs:
218
- - id: nightly-backup
219
- title: Nightly backup
220
- schedule: "0 3 * * *"
221
- type: script
222
- prompt: "bash /path/to/backup.sh"
223
- channels: ["telegram"]
224
- timeoutSeconds: 3600
225
- - id: morning-digest
226
- title: Morning digest
227
- schedule: "0 8 * * 1-5"
228
- type: llm
229
- prompt: "Prepare a brief morning digest of active tasks."
230
- provider: my-provider
231
- model: provider-id/model-id
232
- ```
233
-
234
- * 每条必填:`id`、`title`、`schedule`;以提示词承载有效载荷的类型(`script`、`node`、`python`、`ssh`、`docker`、`llm`、`skill`、`workflow`)还需非空 `prompt`。`http` 例外:目标由 `httpUrl`(或 `prompt`)给出。
235
- * 其余任务字段按原样透传,校验与 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`)。
236
- * 声明式任务标记为**由配置管理**;面板中显示来源标签而不是编辑/删除按钮。
237
- * 对配置任务的编辑、暂停、恢复、切换与删除在面板和 API 上返回 `409`,携带配置任务现有 `id` 的创建或更新请求 `POST /dsh-cron/tasks` 同样被拒绝 —— 配置文件的来源为唯一真值。**立即运行**仍然可用。
238
- * 通过 UI、API 或智能体工具创建的、`id` 相同的任务绝不会被覆盖:该条目会被跳过,冲突写入日志。
239
- * 会执行代码的类型照常激活,但启动时插件会向日志写警告,使通过配置引入的代码路径可见。
240
- * 条目逐条校验并带下标(`config.jobs[i]: …`);一条坏条目会被跳过,不会阻止其余任务或整个配置。
241
-
242
- ### 16. 外部 REST API(`/dsh-cron/api/*`,#54)
243
- 外部系统(CI、宿主机 cron、`curl`)无需打开面板即可驱动调度器。这是唯一由 bearer 令牌保护的接口;面板路由保持本地且防跨站。
244
-
245
- 令牌是插件设置 `apiToken`(与所有密钥一样掩码显示)。认证与错误:
246
- * 未配置令牌 → 整个接口返回 `503`;
247
- * 缺少或错误的 `Authorization: Bearer <token>` → `401`,比较为常量时间。
248
-
249
- | 方法 | 路径 | 说明 |
250
- |:---|:---|:---|
251
- | `GET` | `/dsh-cron/api/tasks` | 任务列表(`status` / `query` 过滤,同面板) |
252
- | `GET` | `/dsh-cron/api/tasks/:id` | 读取单个任务 |
253
- | `POST` | `/dsh-cron/api/tasks` | 创建任务;带 `id` 时更新现有任务 |
254
- | `DELETE` | `/dsh-cron/api/tasks/:id` | 删除任务 |
255
- | `POST` | `/dsh-cron/api/tasks/:id/run` | 强制执行一次 |
256
-
257
- 这些操作复用面板处理器,因此对会执行代码类型的 `x-dsh-cron-confirm: script` 门禁以及对配置任务的 `409` 拒绝与 UI 完全一致。
258
-
259
- ```bash
260
- BASE="http://127.0.0.1:3080"
261
- TOKEN="<API_TOKEN>"
262
-
263
- # 列表
264
- curl -s -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks"
265
-
266
- # 创建;请求体带 id 时为更新
267
- curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
268
- -d '{"id":"cleanup","title":"Cleanup","schedule":"0 4 * * *","prompt":"Remove stale temporary files."}' \
269
- "$BASE/dsh-cron/api/tasks"
270
-
271
- # 强制执行
272
- curl -s -X POST -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks/cleanup/run"
273
-
274
- # 删除
275
- curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks/cleanup"
276
-
277
- # 会执行代码的任务还需确认头
278
- curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "x-dsh-cron-confirm: script" \
279
- -H "Content-Type: application/json" \
280
- -d '{"title":"Disk check","schedule":"0 * * * *","type":"script","prompt":"df -h"}' \
281
- "$BASE/dsh-cron/api/tasks"
282
- ```
283
-
284
- ### 17. Prometheus 指标(#53)
285
- `GET /dsh-cron/metrics` 返回 Prometheus 文本格式,无需新增依赖即可被抓取:
286
-
287
- * `dsh_cron_tasks_total{status}` —— 按状态统计的任务数(gauge)。
288
- * `dsh_cron_task_last_duration_seconds{task}` —— 任务最近一次完成运行的耗时(秒,gauge)。
289
- * `dsh_cron_runs_total{status}` —— 自插件进程启动以来完成的运行数(counter);状态为 `success`、`error`、`timeout`、`skipped`、`missed`。
290
- * `dsh_cron_run_records` —— 当前保存在内存中的运行记录数(gauge)。
291
-
292
- 导出内容只有计数、状态和耗时;提示词、运行输出与任务配置不会出现在其中。
293
-
294
- ```yaml
295
- scrape_configs:
296
- - job_name: dsh-cron
297
- static_configs:
298
- - targets: ["127.0.0.1:3080"]
299
- metrics_path: /dsh-cron/metrics
300
- ```
301
-
302
- ### 18. 严格的渠道校验(#121)
303
- 创建或更新任务时若包含未知的投递渠道 id,现在会返回 `400` 并列出违规项:
304
-
305
- ```json
306
- { "ok": false, "error": "Unknown channel ids: email_ping", "unknownChannels": ["email_ping"] }
307
- ```
308
-
309
- Changed in v0.2.7:此前未知 id 会被静默丢弃,客户端即使有拼写错误也会得到 `ok: true`,最终得到一个不投递任何地方的任务。
310
-
311
- 导入有意保持宽容(文件可能来自旧版本):未知 id 会从导入的任务中丢弃,但会在响应(`unknownChannels`)中列出并写入调度器日志,而不是无声消失。
312
-
313
- ### 19. 安装后校验(#126)
314
- `deploy.sh` 新增仅校验模式,用于检查已安装的配置而不安装任何东西:
315
-
316
- ```bash
317
- bash deploy.sh verify [exact-version]
318
- ```
319
-
320
- 它确认配置报告了指定版本(默认取 `package.json` 的版本),登录 Web UI,然后下载客户端 bundle 并确认其中包含包名。
321
-
322
- 为什么需要它:Web 配置可能位于认证插件之后并对匿名请求返回 `401`,而插件客户端 bundle 只能通过认证后索引中打印的精确组合 `??` URL 获取 —— 裸的 `/plugins/<name>/client.js` 会返回 `404`。因此校验需要先建立已认证会话。
323
-
324
- 校验使用的环境变量:`DSH_WEB_BASE`(默认 `http://127.0.0.1:3080`)、`DSH_WEB_TOKEN`(令牌;未设置时脚本从单元日志读取最后一个)、`DSH_WEB_UNIT`(默认 `dsh-web.service`)。脚本中不含任何密钥。
325
-
326
- ### 20. 内部重构:调度解析与排程(#97)
327
- 面向开发者,行为不变。`parseScheduleExpression` 被拆分为保持相同分支顺序的小函数 —— `parseAtExpression`、`parseRelativeOneShot`、`parseIntervalExpression`、`parseAliasExpression`、`parseCronExpression`,`scheduleTask` 拆分为 `clearScheduled`、`scheduleOneShot`、`scheduleCron`。原有测试全部通过,并新增了针对分支优先级与错误的测试。
328
-
329
- ### 21. 性能与进程隔离增强包(v0.2.9,#134)
330
- - **进程树终止隔离**:Shell 和 Script 任务在独立进程组启动(POSIX 下 `detached: true`);中止或超时向整组发送 `-child.pid SIGTERM -> SIGKILL`,杜绝孤儿进程与僵尸进程。
331
- - **并发控制限流**:默认安全阈值 `maxConcurrent = 2`,避免定时重叠引发 CPU 和内存峰值。
332
- - **瞬态错误重试**:针对网络抖动和模型速率限制(`429`、`502`、`503`、`504`、`ECONNRESET`)提供指数退避重试(最多3次)。
333
- - **网络与前端优化**:`GET /dsh-cron/tasks` 支持 `ETag` 与 `304 Not Modified`;前端页面根据 `visibilityState` 自适应轮询(前台 8s,后台 30s)。
334
- - **历史记录轮换与归档**:活动任务仅保留最新 100 次运行,超出部分自动归档至 `tasks-history-archive.json`。
335
- - **自主 PR 审查配方 (#33)**:Template Hub 预置配方与 `prReviewerEnabled` 设置项。
336
-
337
- ### 22. 自动化、任务链与可观测性包(v0.2.10,#137)
338
- - **Telegram 双向交互控制**:任务通知附带内嵌操作按钮(`🚀 立即运行`、`⏸️ 暂停/恢复`、`📋 最新日志`)。由 `POST /dsh-cron/telegram/webhook` 处理,严格鉴权 Chat ID 并调用 `answerCallbackQuery` 反馈。
339
- - **任务管道与级联触发**:配置 `onSuccess` 与 `onFailure` 下游触发器。上游输出自动注入子任务环境变量 `$DSH_PREV_OUTPUT`,LLM 任务支持 `{{prevOutput}}` 插值。内置最大 5 级深度递归防护,杜绝死循环。
340
- - **模型结构化动作指令**:自主分析任务可输出 JSON 指令触发级联任务(`trigger_task`)、定向告警(`notify`)或创建 Issue。受 `llmActionsEnabled: false` 严格保护。
341
- - **历史归档与延迟洞察**:REST 接口 `GET /dsh-cron/tasks/:id/archive`(支持分页)与 `GET /dsh-cron/tasks/:id/stats`;UI 任务卡片展示耗时彩色徽章(<5s 绿,<30s 黄,≥30s 红)。
342
- - **Prometheus 监控增强**:`/dsh-cron/metrics` 导出当前活动并发量 `dsh_cron_concurrent_running`、各任务 Token 计数器及成本预估指标。
343
-
344
- ### 23. 高级可靠性、自愈、心跳与体验包(v0.2.11,#139)
345
- - **心跳与寂静监控(Heartbeat / Dead Man's Snitch)**:针对外部备份与后台作业提供反向监控。外部脚本定期向 `/dsh-cron/heartbeat/:id` 发送请求;超出 `heartbeatIntervalSeconds` + 宽限期未打卡时,任务标记为 `missed`,即刻推送失联告警并触发 `onFailure` 应急流程。
346
- - **执行前置检查(Pre-flight Gates)**:执行前先验证条件(HTTP 状态 2xx、命令退出码 0、最低可用磁盘 MB)。未通过直接置为 `skipped`,杜绝因外部环境异常产生无意义的模型 Token 消耗与错误干扰。
347
- - **试运行与调度模拟器(Dry-Run & Simulator)**:接口 `POST /dsh-cron/tasks/:id/dry-run` 与 UI `🧪 试运行` 按钮支持无副作用执行(不入库历史、不发渠道通知);`POST /dsh-cron/schedule/preview` 实时计算未来 5 次运行时间。
348
- - **优先级队列与并发池(Priority Queues)**:并发满载时,等待队列严格依据任务 `priority`(1 最高,10 最低)调度。
349
- - **自愈脚本与 AI 根因诊断(Self-Healing)**:任务失败后自动执行补偿指令 `selfHealingCommand`(例如重启服务或清理临时空间);`autoDiagnose` 自动生成 AI 故障根因摘要。
350
- - **UI 交互式归档与管道全景**:支持分页浏览任务历史运行全量输出,直观展示 `➜ 成功触发` 与 `↳ 失败触发` 关联关系。
351
-
352
- ---
353
-
354
- ### 24. 自动挂载智能体预设与工具支持 (#141 / GH-1,v0.2.12 新增)
355
- - **自动挂载智能体预设**:计划执行的自主 `llm` 任务和交互式启动现在会自动解析并挂载系统智能体预设(默认通过 `setup` 钩子中的 `presets.mount(agentCtx, preset.id)` 挂载用户的标准预设)。计划会话现已具备完整的工具调用能力(文件读写、工作区操作、Shell 终端等),彻底解决此前空会话无工具调用的问题。
356
- - **单任务预设覆盖**:可在 Web 管理界面、REST API 或配置文件中为具体任务配置独立的 `agentPreset` 标识(例如 `coding`、`system`、`minimal`)。未设置时自动继承系统默认预设。
357
- - **优雅降级保障**:当未安装 `agentPresets` 服务或指定了未知的预设 ID 时,调度器仅记录友好的警告日志,并安全平稳地继续执行基础模型会话,避免定时任务中断。
358
-
359
- ---
360
-
361
- ### 25. 常驻持久会话与上下文延续 (`targetSessionId`,v0.2.13 新增,#143)
362
- - **跨周期会话上下文延续**:支持在任务中配置 `targetSessionId`。设置后,调度器将在每次定时触发时通过 `agents.resume()` 唤醒已有会话,而不再每次生成孤立的临时会话(`cron-exec-${id}-${uuid}`)。智能体能够完整继承上一轮对话的历史记忆与分析结论。
363
- - **上下文窗口保护与轮转机制 (`targetSessionReset`)**:为防止高频执行导致模型上下文窗口超限与 Token 成本暴增,支持智能轮转策略:
364
- - `never`:持续累积单一会话,不重置。
365
- - `daily`:每日自动开启全新子会话(后缀 `<id>-YYYY-MM-DD`)。
366
- - `weekly`:每周自动开启全新子会话(后缀 `<id>-YYYY-Www`)。
367
- - 日期变量插值:`targetSessionId` 中支持 `{{date}}` 占位符,自动注入当前日期 `YYYY-MM-DD`。
368
- - **主界面原生可见交互**:持久会话不会被标记为 `ephemeral`/`internal`,且在执行后跳过自动归档(`sessions.archive()`),用户可在 DSH 聊天列表中直接查看并继续手动对话。
369
- - **预设工具链无缝适配**:恢复会话时同样完整挂载 `agentPresets`,保障文件读写、代码编辑与终端工具持续可用。
370
- - *致谢*:功能灵感源自社区开发者 [@RaulLazaro](https://github.com/RaulLazaro)。
371
-
372
- ---
373
-
374
- ### 26. 系统稳定性、硬化与自愈维护包 (v0.2.14, #145)
375
- - **重试预算自动重置**:彻底修复重试耗尽后的计数残留问题。当任务耗尽配置的重试次数(`maxRetries`)后,`attempts` 计数器自动清零,确保后续周期的定时调度享有完整的重试预算。常规计划执行或手动触发也均保证以干净的重试预算启动。
376
- - **清除队列僵尸任务**:通过界面/API 暂停或删除任务时,调度器会立即将其从并发等待队列(`this.queue`)中剔除;并发槽位释放出队时,非激活或已删除的任务也会被自动安全跳过。
377
- - **历史归档容量上限保护**:针对长期运行和高频调度的生产环境,`tasks-history-archive.json` 针对每个任务安全限制保留最新的 1,000 条运行记录,消除无限制磁盘占用与同步 JSON 序列化卡顿。
378
- - **灾难恢复与存储自动备份**:`TaskStore` 在每次成功持久化时自动维护原子的 `tasks.json.bak` 备份副本。若发生进程异常导致数据损坏,存储引擎会自动保存现场切片 `tasks.json.corrupted.<timestamp>` 供故障分析,并无缝从备份中自愈恢复。
379
- - **上下文超限自愈与平滑轮转**:在常驻持久会话(`targetSessionId`)中,若智能体因模型上下文窗口溢出(`context_length_exceeded`)失败,运行器将精准捕获超限错误,归档已满会话,自动轮转至全新子会话并平滑重试,避免任务中断。
380
- - **Windows 进程树彻底终止**:在 Windows 系统上,外部进程任务被取消或超时终止时,改为执行 `taskkill /pid <pid> /T /F`,杜绝孤儿进程和后台残留外壳。
381
-
382
- ---
383
-
384
-
385
- ## 📦 安装
386
-
387
- ```bash
388
- dsh plugin --profile web add @goodandready/dsh-cron
389
- ```
390
-
391
- 重启 DeepSeek Harness 实例并刷新浏览器。
392
-
393
- ---
394
-
395
- ## ⚙️ 配置(`settings.yaml`)
396
-
397
- 可以在 `settings.yaml` 中配置,也可以通过 DSH 中的插件设置卡片交互式管理:
398
-
399
- ```yaml
400
- # settings.yaml
401
- dsh-cron:
402
- botToken: "" # Telegram Bot API 令牌(保密字段)
403
- chatId: "" # 接收报告的 Telegram chat ID
404
- notifyTelegram: false # 全局投递所有任务的报告
405
- onlyOnFailure: false # 仅失败时投递报告
406
- kanbanBaseUrl: "http://127.0.0.1:3000" # dsh-kanban HTTP API 基础地址
407
- defaultTimezone: "" # 默认 IANA 时区(空 = 服务器本地)
408
- maxConcurrent: 0 # 最大并行运行数(0 = 不限)
409
- heartbeatUrl: "" # 心跳上报 URL(dead man's snitch)
410
- heartbeatIntervalSec: 0 # 心跳间隔秒数(0 = 关闭)
411
- # --- 投递渠道 ---
412
- botTokenRef: "" # Telegram bot token 的凭据名称
413
- template: "" # 全局消息模板,例如 "⏰ {title} — {status}"
414
- channelTemplates: {} # 按渠道覆盖模板
415
- deliveryTimeoutMs: 15000 # 每个渠道的投递超时;慢端点记为失败,不影响其他渠道
416
- discordWebhookUrl: "" # Discord webhook
417
- slackWebhookUrl: "" # Slack incoming webhook
418
- ntfyUrl: "https://ntfy.sh" # ntfy 服务器;ntfyTopic / ntfyTokenRef
419
- ntfyTopic: ""
420
- ntfyTokenRef: ""
421
- barkServerUrl: "https://api.day.app" # Bark 服务器;barkKey = 设备键
422
- barkKey: ""
423
- pushplusUrl: "https://www.pushplus.plus/send" # pushplusTokenRef
424
- pushplusTokenRef: ""
425
- ttsBaseUrl: "http://127.0.0.1:3080" # dsh-tts 基础地址
426
- giteaBaseUrl: "" # giteaRepo = owner/repo,giteaTokenRef = 凭据名称
427
- giteaRepo: ""
428
- giteaTokenRef: ""
429
- # --- 外部 REST API(#54)---
430
- apiToken: "" # 外部 /dsh-cron/api/* 接口的 bearer 令牌(掩码;空 = 503)
431
- ```
432
-
433
- ### 配置参数
434
-
435
- | 参数 | 类型 | 默认值 | 说明 |
436
- |:---|:---|:---|:---|
437
- | `botToken` | `string` | `""` | Telegram Bot API 令牌。留空时插件会尽力继承 DSH 设置中 `dsh-messenger-gateway` 配置的机器人。保密字段:界面只显示掩码值 |
438
- | `chatId` | `string` | `""` | 接收报告的 Telegram chat ID。留空时回退到 `dsh-messenger-gateway` 的第一个允许会话 |
439
- | `notifyTelegram` | `boolean` | `false` | 全局开关:向 Telegram 投递运行报告 |
440
- | `onlyOnFailure` | `boolean` | `false` | 全局开关:仅对 `error`/`timeout` 运行投递报告 |
441
- | `kanbanBaseUrl` | `string` | `"http://127.0.0.1:3000"` | 用于自动卡片的 `dsh-kanban` HTTP API 基础地址 |
442
- | `defaultTimezone` | `string` | `""` | 任务调度的默认 IANA 时区;空 = 服务器本地时间 |
443
- | `maxConcurrent` | `number` | `0` | 并行运行上限;超出的运行记录为 `skipped`(0 = 不限) |
444
- | `heartbeatUrl` | `string` | `""` | 心跳上报 URL,调度器存活期间按 `heartbeatIntervalSec` 间隔 GET |
445
- | `heartbeatIntervalSec` | `number` | `0` | 心跳间隔秒数(0 = 关闭) |
446
- | `botTokenRef` | `string` | `""` | 保存 Telegram bot token 的 DSH 凭据名称;发送时解析(回退顺序:`botToken` → messenger-gateway 设置 → 环境变量 `CRON_TELEGRAM_BOT_TOKEN`) |
447
- | `template` | `string` | `""` | 带 `{title}`/`{status}`/`{duration}` 等占位符的全局消息模板;留空使用内置文本 |
448
- | `channelTemplates` | `object` | `{}` | 按渠道 ID 覆盖模板(`telegram`、`discord` 等) |
449
- | `deliveryTimeoutMs` | `number` | `15000` | 每个渠道的投递超时;超时的端点记为失败,不拖慢其他渠道或下一次调度 |
450
- | `discordWebhookUrl` / `slackWebhookUrl` | `string` | `""` | Discord 与 Slack 渠道的 webhook 地址 |
451
- | `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | ntfy 服务器、主题与可选的 token 凭据名称(以 `Authorization: Bearer …` 发送) |
452
- | `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Bark 服务器与设备键(键、标题和正文位于请求路径中) |
453
- | `pushplusUrl` / `pushplusTokenRef` | `string` | `"https://www.pushplus.plus/send"` / `""` | PushPlus 端点(可指向自建代理)与 token 凭据名称 |
454
- | `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | 用于语音播报的 `dsh-tts` 基础地址 |
455
- | `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Gitea 渠道:基础地址、`owner/repo` 与 API token 的凭据名称 |
456
- | `apiToken` | `string` | `""` | 外部 `/dsh-cron/api/*` 接口的 Bearer 令牌。保密字段,返回时掩码;为空时接口返回 503,错误值返回 401 |
457
-
458
- 说明:
459
-
460
- * 运行历史上限为**每任务 50 条**(固定);每条记录最多保留 4000 字符输出。
461
- * 任务在**服务器本地时区**执行;cron 表达式由 `croner` 按主机时钟计算。
462
- * 任务持久化在 DSH 数据目录(`cron/tasks.json`),重启后保留;启动时会检测错过的一次性任务。
463
-
464
- ---
465
-
466
- ## 🔌 HTTP API 参考
467
-
468
- 所有端点由 DSH Web 服务器在 `/dsh-cron/` 下提供。读端点对本地 UI 开放;**变更端点拒绝跨域请求**且请求体最大 1 MB。通过 HTTP 创建 `script` 类型任务还需要 `x-dsh-cron-confirm: script` 请求头 —— 伪造的跨站请求无法附加该头。
469
-
470
- | 方法 | 路径 | 说明 |
471
- |:---|:---|:---|
472
- | `GET` | `/dsh-cron/tasks` | 任务列表;查询参数 `status`(`all/active/paused/completed`)、`query`(子串搜索)。返回任务、推荐模板与汇总统计 |
473
- | `POST` | `/dsh-cron/tasks` | 创建或更新任务(携带 `id` 时为更新)。需要 `title`、`schedule`、`prompt` |
474
- | `GET` | `/dsh-cron/tasks/:id/history` | 运行历史,`?limit=20` |
475
- | `POST` | `/dsh-cron/tasks/:id/run` | 立即手动运行 |
476
- | `POST` | `/dsh-cron/tasks/:id/pause` | 暂停调度 |
477
- | `POST` | `/dsh-cron/tasks/:id/resume` | 恢复调度 |
478
- | `POST` | `/dsh-cron/tasks/:id/toggle` | 切换活跃/暂停 |
479
- | `POST` | `/dsh-cron/tasks/:id/duplicate` | 创建暂停状态的副本:复制配置,重置运行历史与计数 |
480
- | `GET` | `/dsh-cron/recipes` | 内置配方目录:按类别分组的现成监控预设,全部为只读操作 |
481
- | `GET` | `/dsh-cron/tasks/export` | 仅含任务配置的版本化 JSON —— 不含历史与计数。渠道按名称引用凭据,但手动填写在任务中的 `env` 与 HTTP 请求头属于配置,会出现在文件里 |
482
- | `POST` | `/dsh-cron/tasks/import` | 校验文档并以 `add`、`replace` 或 `skip` 策略导入;支持 `dryRun` 预览。导入的任务始终为**暂停**状态,恢复不会自动触发 |
483
- | `PATCH` | `/dsh-cron/tasks/:id` | 部分更新(仅白名单字段:`title`、`schedule`、`prompt`、`type`、`delivery`、`provider`、`model`、通知/超时/重叠/Kanban 设置、`status`、`oneShot`) |
484
- | `DELETE` | `/dsh-cron/tasks/:id` | 删除任务 |
485
- | `GET` | `/dsh-cron/models` | 列出 LLM 提供方;`?provider=<id>` 列出模型 |
486
- | `POST` | `/dsh-cron/chat/start` | 启动带任务配置指令的“由 DSH 创建”智能体会话 |
487
- | `GET` | `/dsh-cron/settings` | 客户端安全设置(令牌掩码显示) |
488
- | `POST` | `/dsh-cron/settings` | 更新集成设置(通过设置服务应用) |
489
- | `GET` | `/dsh-cron/heartbeat` | 存活探针:活跃任务数与最近运行时间 |
490
- | `POST` | `/dsh-cron/telegram/test` | 发送 Telegram 测试消息 |
491
- | `POST` | `/dsh-cron/kanban/test` | 创建 Kanban 连通性测试卡片 |
492
- | `*` | `/dsh-cron/action/:id/:action` | 任务操作路由的兼容别名(`run`、`toggle`、`delete`、`history`) |
493
- | `GET` | `/dsh-cron/metrics` | Prometheus 文本格式的任务与运行计数 —— 不含提示词与输出(#53) |
494
- | `GET` / `POST` | `/dsh-cron/api/tasks` | 令牌保护的外部接口:列表 / 创建或更新(#54) |
495
- | `GET` / `DELETE` | `/dsh-cron/api/tasks/:id` | 令牌保护的外部接口:读取 / 删除(#54) |
496
- | `POST` | `/dsh-cron/api/tasks/:id/run` | 令牌保护的外部接口:强制执行(#54) |
497
-
498
- ---
499
-
500
- ## 🧪 测试
501
-
502
- ```bash
503
- npm test
504
- ```
505
-
506
- 测试覆盖调度表达式解析、调度器引擎、原子存储、HTTP 辅助函数、通知与工具契约。
507
-
508
- ---
509
-
510
- ## 📄 许可证
511
-
512
- MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)