# 📦 @goodandready/dsh-cron

<div align="center">

<h3>面向 DeepSeek Harness 的定时 Cron 调度、后台自动化与智能体任务执行引擎</h3>

<p align="center">
  <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>
  <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>
  <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>
  <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>
</p>

<p align="center">
  <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>
</p>

<p align="center">
  <a href="README.md"><b>🇬🇧 English</b></a> •
  <a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
  <a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
</p>

<table align="center">
  <tr>
    <td align="center">
      ⭐ <strong>如果您喜欢这个插件，请在 GitHub 上为它点亮 Star</strong> — 这能让我知道插件对您有用，并鼓励我继续开发和维护它。
      <br><br>
      🐛 <strong>如果您发现 Bug 或希望增加功能</strong>，请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议，并在后续版本中实现有价值的改进。
    </td>
  </tr>
</table>

</div>

---

## ⚡ 概述与问题

自主 AI 智能体经常需要执行周期性任务：生成每日晨报、整理缺陷跟踪、检查 API 健康状态、同步数据库或定期执行 Git 清理。如果 Harness 内没有专用调度器，用户只能依赖外部 crontab 封装、复杂的 webhook 方案或手动干预。

**`@goodandready/dsh-cron`** 是 DeepSeek Harness 的原生全栈调度与后台自动化插件。它将标准 cron 表达式、自然语言间隔语法与自主智能体执行连接起来：

1. **完善的可视化任务管理器** —— 侧边栏按钮带可折叠的活跃任务列表（下次运行时间或实时状态，行数有上限且状态可记忆），以及功能齐全的面板：按类型、模型、渠道筛选，暂停、立即运行、复制、导出/导入与创建任务。
2. **交互式“由 DSH 创建”流程** —— 与智能体对话，把高层需求转化为规范的定时任务。
3. **自主工具调用** —— 原生 `cron_*` 工具让智能体在会话中自行安排后续执行。
4. **健壮的调度器与原子存储** —— 基于 `croner`：间隔别名、一次性延时任务、原子写入、运行历史与成本追踪。
5. **六种执行运行时** —— shell、Node.js、Python、HTTP/webhook、远程 SSH 与 Docker，并支持按任务的环境变量、工作区绑定以及面向代码修改任务的隔离 git worktree。
6. **多渠道路由与模板** —— 一次运行可投递到 Telegram、dsh-kanban、Discord、Slack、ntfy、Bark、PushPlus、语音（`dsh-tts`）与 Gitea，支持 `{变量}` 消息模板与按 DSH 凭据名称引用的密钥。

---

## 🏗️ 架构

```mermaid
graph TD
    subgraph Client ["Web 客户端 (DSH UI)"]
        SidebarBtn["侧边栏时钟按钮<br/>(DSH 客户端插槽)"]
        Overlay["任务管理面板<br/>(标签: 全部 / 活跃 / 暂停 / 已完成)"]
        CreateWithDSH["“由 DSH 创建”对话框<br/>(自然语言任务)"]
        ManualForm["手动任务表单<br/>(运行时、cron、超时、重叠策略、渠道)"]
        SettingsCard["设置卡片<br/>(渠道、模板、凭据)"]
    end

    subgraph Server ["服务端 (Cordis 与 DSH 服务)"]
        HttpRoutes["HTTP REST API<br/>(/dsh-cron/*)"]
        AgentTools["工具调用网关<br/>(cron)"]
        Scheduler["TaskScheduler 引擎<br/>(Croner 实例 + one-shot 定时器)"]
        Store["原子 TaskStore<br/>(tasks.json 原子写入)"]
        AgentRunner["智能体会话调度器<br/>(以指定模型执行提示词)"]
        Runtimes["执行运行时<br/>(shell、node、python、http、ssh、docker)"]
        Notify["投递路由<br/>(模板 + 9 个渠道)"]
        Secrets["凭据引用<br/>(DSH credentials / ENV)"]
    end

    SidebarBtn --> Overlay
    Overlay --> CreateWithDSH
    Overlay --> ManualForm
    SettingsCard --> HttpRoutes
    CreateWithDSH -->|POST /chat/start| HttpRoutes
    ManualForm -->|POST /tasks| HttpRoutes
    HttpRoutes --> Scheduler
    AgentTools --> Scheduler
    Scheduler --> Store
    Scheduler -->|按间隔/一次性触发| AgentRunner
    Scheduler --> Notify
```

---

## ✨ 功能与能力

### 🐝 群蚁协同委托与多智能体并行执行 (Swarm Delegation) (#350)
- **受控工作池**：配置并发限制 `concurrencyLimit`（1..10，默认 3），在不耗尽系统资源的前提下并行处理独立子任务。
- **高容错分解**：单个子任务异常完全隔离，群体自动聚合所有成功输出，并在存在异常时清晰上报。
- **智能结果汇编**：支持将所有工作节点的执行输出聚合成结构化 Markdown 报告或交由综合模型提炼总结。
- **群体指标与历史**：运行历史完整记录成功/失败统计、耗时及每个子节点的执行快照与日志。

### 🔍 Git Diff 变更查看与交互式补丁审查 (#351)
- **自动 Diff 捕获**：在 Git 工作区执行的任务会自动捕获变更文件列表、状态统计及完整 Unified Diff（最高 256 KiB）。
- **一键丢弃 (Discard)**：一键清空工作区未提交改动，安全重置仓库状态（`git reset --hard HEAD && git clean -fd`）。
- **一键应用 (Apply)**：将保存的 Diff 补丁直接重新注入并应用到工作区（`git apply --whitespace=fix`）。
- **交互式审阅面板**：在运行详情抽屉中直接查看带有色彩区分的高亮 Diff 变更对比。

### 🔀 决策节点与条件分支 IF/ELSE (#352)
- **条件路由**：任务可配置 `branchRules`（评估 `exitCode`、`output`、`output.json.<path>` 等字段），支持 `equals`、`notequals`、`contains`、`gt`、`lt`、`regex` 等操作符。
- **回退分支**：若无匹配规则，自动执行 `elseTask`。
- **死锁防护**：未触发的分支自动标记为 `skipped`，确保后续汇聚栅栏（`joinPolicy: 'all_success'`）正常通过。
- **DAG 可视化**：图谱界面直观展示决策标识（`🔀 Decision`）与连线判断条件。

### 🩺 自愈医生（Self-Healing Doctor）(#353)
- **故障诊断分类**：自动分类 8 类运行故障（语法错误、缺少依赖、频率限制、认证失效、架构变更、超时、网络错误、运行时异常）。
- **统一代码差分**：生成直观的标准 Unified Diff 对比。
- **一键应用与回滚**：提供 `/doctor/apply` 与 `/doctor/revert` 接口，自动保留多达 10 个历史版本（`task.revisions`）。
- **受控自主修复**：启用 `autoHeal: true` 时，高可信度（`high`）修复自动生效，且强制 24 小时频次限制防止死循环。

### 1. 可视化任务管理器
点击 DSH 侧边栏中的时钟图标（位于“新会话”按钮旁）打开管理面板：
* **状态过滤标签**：**全部**、**活跃**、**已暂停**、**已完成**。
* **即时操作**：立即运行（**Run Now**）、暂停/恢复调度、带确认的删除。
* **一键预设模板**：*每日摘要*、*每周回顾*、*待办监控*。
* **运行历史**：打开任务卡片查看历史运行 —— 时间、耗时、状态（成功 / 失败 / 超时 / 跳过 / 错过）、输出与错误。
* **快捷计划预设**：在任务编辑弹窗中通过预设按钮一键填入常用频率（`15m`、`1h`、`Daily 09:00`、`Weekdays`、`Weekly Mon`），并即时更新自然语言预览。
* **自动暂停与预算保护徽章**：当任务因预算熔断机制（Burn Guard）自动暂停或手动暂停时，任务卡片上显著展示包含具体原因的状态徽章。
* **汇总统计栏**：活跃任务数、总运行次数、总 token 消耗与估算美元成本。

### 2. “由 DSH 创建”对话框
无需猜测 cron 语法，用自然语言即可创建任务：
1. 点击 **Create ⌄** ➔ **Create with DSH**。
2. 描述要自动化的内容（例如：*“每个工作日早上 9 点检查未处理的 PR 并起草评论”*）。
3. 插件会创建一个注入了调度器指令的专属智能体会话。智能体会与你确认细节 —— LLM 还是 NO-LLM shell 任务、准确的 cron 表达式、在你的 DSH 安装中可用的经济型模型，以及是否启用“静默规则”（仅在新事件或故障时告警）—— 评估属于对话内定时提醒（`schedule_create`）还是后台自动化（`cron`），并在获得您的确认后通过 `cron` 工具（`action: 'create'`）注册任务。

### 2. DSH 核心内置 schedule 与 dsh-cron 对比

DeepSeek Harness 内置了轻量级扩展 `@deepseek-ai/dsh-schedule`，用于会话内的基础定时提醒。下表帮助您根据场景选择合适的工具：

| 功能维度 | DSH 核心 `schedule` (`@deepseek-ai/dsh-schedule`) | `@goodandready/dsh-cron` |
|:---|:---|:---|
| **主要定位** | 当前会话内的定时提醒与催办消息 | 无人值守的后台自动化执行器与任务编排引擎 |
| **执行上下文** | 当前活动会话内 | 独立的隔离智能体会话或外部后台进程 |
| **执行运行时** | 仅当前会话提示词（LLM） | 9 种运行时：`llm`、`script` (bash/sh)、`node`、`python`、`http` (REST/webhook)、`ssh`、`docker`、`skill`、`workflow` |
| **模型工具** | `schedule_create`、`schedule_list`、`schedule_delete` | 统一 `cron` 工具（action: `create`、`list`、`get`、`update`、`pause`、`resume`、`run`、`delete`） |
| **工具模式体积** | 约 1.5k 字符 | 约 1.5k 字符（由 9 个工具合并为 1 个，节省约 12k 字符上下文） |
| **推送渠道** | 仅限当前会话 | 多渠道：Telegram、Discord、Slack、Webhook、Kanban、ntfy、Bark、PushPlus、语音 (TTS)、Gitea |
| **代码修改隔离** | 无 | 临时或保留的 git worktree 隔离环境（`worktree: true`） |
| **成本与 Token 限制** | 无 | 成本熔断防护：`costLimitUsd`、`dailyCostLimitUsd`、`tokenLimit` 自动暂停 |
| **容错与健康检查** | 无 | 指数退避自动重试、失败自动诊断、心跳监测 (Dead Man's Snitch / Better Uptime) |
| **静默规则 (Silent Rule)** | 无 | 无新事件或变更时完全静默（杜绝通道垃圾消息） |
| **任务管理** | 基础列表与删除 | 完整 UI 管理器、运行历史、日志查看器、指标统计、手动触发、导入导出、配置同步 |

### 3. 智能体工具 (`cron`)

自主智能体通过单个统一的 `cron` 工具直接管理定时任务，大幅降低模型模式开销：

| 动作 (action) | 说明 | 核心参数 |
|:---|:---|:---|
| `create` | 创建新的后台定时任务或自动化作业 | `title`、`schedule`、`prompt`、`type`、`model`、`channels`、`delivery` 等 |
| `list` | 列出任务的状态、下次运行时间、token 总量与成本估算 | `status` (`'all'`、`'active'`、`'paused'`、`'completed'`) |
| `get` | 根据任务 ID 获取单项任务的完整配置 | `id` |
| `update` | 就地修改现有任务（切换到代码执行运行时需 `confirmCodeSwitch: true`） | `id`、修改字段 |
| `pause` | 暂停调度而不删除配置 | `id` |
| `resume` | 恢复已暂停的调度 | `id` |
| `run` | 触发一次立即的带外运行 | `id` |
| `delete` | 永久删除任务及其历史 | `id` |

> [!NOTE]
> **上下文优化与平滑迁移**：此前 9 个单独的工具模式在每次模型轮次中消耗约 13.6k 字符。整合为单一 `cron` 工具后，模式开销减少约 88%（降至约 1.5k 字符）。旧工具名（`cron_create_task`、`cron_schedule_task`、`cron_list_tasks` 等）被优雅拦截，并返回清晰迁移提示，引导模型使用带对应 `action` 的 `cron` 工具。对于简单的会话内提醒，模型将建议使用核心内置的 `schedule_create`。

会话中模型可进行的调用示例：

```json
cron({
  "action": "create",
  "title": "Morning digest",
  "schedule": "0 8 * * 1-5",
  "prompt": "Prepare a brief morning digest of active tasks and open tickets.",
  "type": "llm",
  "delivery": "isolated"
})
```

### 4. 调度表达式语法
基于 `croner`，支持标准 5 段 cron 表达式与友好的别名：

* `0 9 * * 1-5` —— 工作日 09:00
* `*/15 * * * *` —— 每 15 分钟
* `0 0 * * 0` —— 每周日午夜
* `every 10m` / `every 2h` / `every 30s` —— 自然语言间隔
* `daily` / `hourly` / `weekdays` 快捷方式，以及标准 `@hourly` / `@daily` / `@weekly` / `@monthly` / `@yearly` 与 `@every 30m`
* **任务级时区** —— 可为任务设置 IANA 时区（如 `Europe/Berlin`）；未设置时按服务器本地时间调度
* **一次性任务**：`at: 2026-09-05T15:00:00Z`（精确 ISO 时间戳）或相对延时 `in 20m` / `in 2h`（也接受 `через 15 минут` 之类的俄语输入）。一次性任务在单次运行后自动转为 `completed`，显示在 **已完成** 标签下。

### 5. 执行可靠性
* **自动重试** —— 按任务设置 `maxRetries` 与基础 `retryBackoffMs`：失败（`error`/`timeout`）的运行按指数退避自动重试，成功后计数归零。
* **Misfire 策略** —— 选择守护进程离线期间错过的运行如何处理：`skip`（默认 —— 记录缺口）、`runOnce`（迟执行一次）或 `catchUpAll`（迟执行并记录缺口）。`skip` 下错过的一次性任务直接转为 `completed`，不再过期触发。
* **并发上限** —— 插件设置 `maxConcurrent` 限制并行运行数；超出的运行记录为 `skipped` 并附原因。
* **实时执行指示** —— 任务列表中的脉冲状态图标与运行计时器。

### 6. 执行运行时
每个任务可选择自己的运行时；非 LLM 运行时不需要模型，也不消耗 token：

* **Shell**（`script`）—— 通过 Harness shell 执行命令或脚本，支持 `env` 与 `cwd`。
* **Node.js**（`node`）与 **Python**（`python`）—— 指定解释器（`nodePath`、`pythonPath`）运行片段；Python 会自动识别项目虚拟环境。
* **HTTP**（`http`）—— 以自定义请求头与请求体访问 URL，状态码与响应写入运行历史。
* **SSH**（`ssh`）—— 通过 `dsh-remote-workspace` 配置（`sshProfileId`）或独立 host/key 字段在远程主机执行命令。
* **Docker**（`docker`）—— 在镜像容器（`dockerImage`）中执行命令。
* **环境变量** —— 按任务的 `env` 映射（界面中每行 KEY VALUE）应用于外部运行时；请勿在此存放密钥。
* **工作区与 worktree** —— 将任务绑定到 Harness 工作区（`workspaceId`，通过宿主 `workspaceRegistry` 服务解析）或直接路径；运行创建的智能体会话会登记进该工作区的账本（`attachSession`），因此出现在项目分组下而非「未分组」。对会修改代码的智能体任务，可在隔离的 git worktree 中运行（`worktree`、`keepWorktree`）。
* **写入范围锁与冲突避免 (Scope Locks)** —— 声明 `writeScopes`（例如 `src/`、`docs/`）防止同一工作区内并发任务互相覆盖代码；重叠任务安全排队等待释放锁。
* **输入哈希监控与跳过 (Input Hash)** —— 配置 `inputHashPaths`（与 `skipIfUnchanged`）在运行前计算确定性 SHA-256；若输入未变则直接跳过 (`skipped (inputs unchanged)`)，节省 100% 的 Token 与调用成本。

### 7. 成本控制：回退模型与支出保护（Burn Guard）
* **回退模型** —— 任务可以默认使用便宜模型，失败时改用更强模型完成：设置 `fallbackModel`（可选 `fallbackProvider`），失败（`error` 或 `timeout`）的运行会在该模型上重试一次，之后才进入常规重试退避。历史记录会标明最终产出结果的模型以及是否使用了回退，两次尝试的用量与成本都会累计，模板变量 `{model}` 渲染完成运行的模型。回退仅适用于智能体类型（`llm`、`skill`、`workflow`）。
* **Token 与成本支出保护（Burn Guard）** —— 为任务配置严格预算上限：`costLimitUsd`（总支出美元上限）、`dailyCostLimitUsd`（24小时滚动支出上限）和 `tokenLimit`（Token总数上限）。一旦达到任一阈值，任务将自动暂停并记录 `pausedReason`（`cost_limit_exceeded`、`daily_cost_limit_exceeded` 或 `token_limit_exceeded`），同时向所有配置的通知渠道发送报警通知。
* **基于会话事件的 Token 与成本统计** —— 直接从 DSH 会话流式事件（`assistant/message`、`assistant/chunk` usage、`assistant/attempt`）中提取实际 Token 消耗，精确计入未命中输入、命中缓存读取、生成输出及重试过程中已计费的失败尝试。
* **滑动 24 小时成本账本（24h Cost Ledger）** —— 日耗保护（`dailyCostLimitUsd`）在 `store.json` 中为每个任务维护独立的滑动 24 小时成本记录。即使历史记录超过 100 条触发归档，24 小时内的所有花费依然完整保留并能跨服务重启持续生效。

### 8. 会话集成与权限
* **真实智能体输出与终端状态捕获** —— 隔离当前轮次的会话事件，提取最终真实的智能体回复文本（持久会话中自动排除以往历史轮次），并严格校验轮次终止状态（带有错误或中断的 `turn/end`），确保任务失败能准确反映到执行历史、链路调用及结构化指令中。
* **按任务的权限预设** —— `default`、`read-only`、`workspace-write` 或 `full` 在提示词执行前应用于任务会话。
* **会话自动归档** —— 隔离的 cron 会话在运行后自动归档（尽力而为，通过宿主 `workspaceRegistry.archiveSession`），不干扰聊天列表。
* **历史 → 会话** —— 每次 LLM 运行都会记录会话，可直接从历史记录打开对话。

### 9. 按规则保持安静
有输出的任务可以设置用自然语言描述的**静默规则**（例如“当没有分区使用率超过 80% 时保持安静”）。运行成功时，由便宜模型对照该规则判断输出，若结论为保持安静则跳过报告，并在运行历史中记录原因。遵循 fail-open：没有规则、没有模型、调用失败或答案无法解析时都会照常投递报告。插件设置 `silentRuleModel` 指定用于判断的模型。

### 10. 失败诊断
智能体任务可以请求诊断：设置 `inspectOnFailure` 后，失败（`error` 或 `timeout`）的运行会连同任务提示词与截断输出一起交给模型，运行历史中会保存简短诊断与具体的提示词修改建议。历史记录提供按钮把该建议载入编辑表单 —— 不会自动应用。模型由 `inspectorModel` 指定，消息模板中可使用 `{diagnosis}`。模型不可用或调用失败时，失败的运行保持原样。

### 11. 通知渠道与消息模板
运行完成后，报告会发送到该任务配置的所有渠道 —— Telegram、dsh-kanban、Discord、Slack、ntfy、Bark、PushPlus、语音（`dsh-tts`）以及 Gitea issue：

* **任务迁移** —— 将全部配置导出为版本化 JSON，并在别处导入（含预览摘要）；导入的任务处于暂停状态。
* **按任务选择渠道** —— 在任务表单中勾选渠道；显式选择会覆盖旧版 `notifyTelegram`/`kanbanMode` 开关，留空则回退到它们。
* **故障隔离** —— 某个渠道不可用会记录在调度器日志中，其余渠道仍会收到报告；失效的 webhook 不会吞掉整份报告。
* **消息模板** —— 支持全局模板、按渠道覆盖或按任务模板，变量为 `{title} {id} {status} {output} {error} {duration} {schedule} {time} {tokens} {cost}`。未知占位符保持原样，失败运行默认使用失败模板。
* **`onlyOnFailure`** —— 全局或按任务生效：成功运行静默，仅发送 `error`/`timeout`。
* **凭据按名称引用** —— webhook token 与 Telegram bot token 填写 DSH 凭据的名称（`botTokenRef`、`ntfyTokenRef`、`pushplusTokenRef`、`giteaTokenRef`），发送时通过 DSH credentials 服务解析，并可回退到环境变量，且绝不会经过插件设置。webhook URL 与 Bark 设备键本身内嵌密钥，因此保存在插件设置文件中，但返回浏览器时始终为掩码，界面回传的掩码值也不会覆盖已保存的值。
* **投递超时** —— 每个渠道请求都有上限（`deliveryTimeoutMs`，默认 15000 毫秒，可在设置面板或 `settings.yaml` 中调整），且各渠道并发发送：无响应的端点只记录为失败，不会拖慢其他渠道或下一次调度。限制作用于整个渠道处理过程，也覆盖凭据解析——它不支持 abort 信号。
* **Telegram** —— 带状态徽标（✅ / ❌）、耗时、调度描述与等宽输出块的 Markdown 报告；动态值会被转义。凭据可直接填写，或从 DSH `settings.yaml` 的 `dsh-messenger-gateway` 段继承（尽力而为）。
* **Discord / Slack** —— 通过 webhook 投递：Discord 使用按运行状态着色的 embed，Slack 使用纯文本正文。
* **ntfy / Bark / PushPlus** —— 移动推送，支持主题/设备键与可选 bearer token；Bark 的标题与正文放在请求路径中，PushPlus 端点可指向自建代理。
* **语音** —— `dsh-tts` 通过其 HTTP 路由朗读报告（`ttsBaseUrl`，默认 `http://127.0.0.1:3080`）。
* **Gitea** —— 创建包含运行报告的 issue（`giteaBaseUrl`、`giteaRepo`、token 凭据）；失败运行标记为 `cron`、`bug`、`alert`。
* **测试发送按钮** —— 在安排关键任务前现场验证 Telegram 连通性。

### 12. Kanban 集成与成本统计
* **自动创建 Kanban 卡片** —— 当 `kanbanMode` 为 `on_failure` 或 `always` 时，插件在 `dsh-kanban` 中创建卡片（`on_failure` → `error`/`timeout` 时进入 *Backlog*；`always` → 完成后进入 *Done*/*Backlog*）。
* **Token 与执行成本计量** —— 按运行与任务统计 token 消耗（输入、输出、缓存读取），基于内置价格表估算美元成本，并提供汇总分析栏。

### 13. 重叠策略与执行超时

* **执行超时（`timeoutSeconds`）** —— 达到限制后，shell 子进程通过 abort 信号立即终止，智能体会话被释放以停止消耗 token。默认 `1800`（30 分钟）。
* **重叠策略（`overlapPolicy`）** —— 上一次运行尚未结束时再次触发调度时的行为：
  * **`skip`**（默认）：丢弃重叠的运行，在历史中记录 `skipped`；
  * **`queue`**：将下一次运行排队，当前任务完成后自动开始；
  * **`replace`**：通过 `AbortController` 中止当前运行并启动新的执行。
* **调度器重叠策略直达** —— Croner 定时触发不再在上游被静默抑制（`Croner protect: false`），确保定时触发的重叠事件能够完整传递至调度器，严格执行 `skip`（记录历史）、`queue`（延迟排队）与 `replace`（优雅终止）。
* **队列上下文与链路深度延续** —— 全局并发限制队列与任务重叠队列均完整保留不可变执行参数（`chainDepth`、`prevOutput`、`prevTaskId`、`prevStatus`、`prevCostUsd`），防止高负载或排队时任务链路数据丢失。

如果守护进程在计划时刻处于离线状态，启动时该次运行会被记录为 `missed`，历史空档始终可见。

### 14. 心跳监控（Dead man's switch）
* 在插件设置中配置 `heartbeatUrl` 与 `heartbeatIntervalSec`，调度器会按间隔 GET 该地址 —— 外部监控可在心跳停止时告警。
* 内置 `GET /dsh-cron/heartbeat` 端点返回存活状态、活跃任务数与最近运行时间，便于自建看门狗。
* **`onlyOnFailure` 模式支持心跳超时告警** — 遗漏心跳（`status === 'missed'`）在各通知渠道过滤判定（`channels.js`、`telegram.js`、`integrations.js`）中被统一视作失败状态，确保外部守护进程中断时 Telegram、Discord、Webhook 与看板即刻告警。

### 15. 来自配置的声明式任务（#50）
长期运行的任务可以直接声明在配置文件里，而无需在界面中手工重建。配置文件拥有这些任务：每次插件启动时会创建或更新它们，从文件中消失的任务会被删除。

在配置文件（`cordis.patch.yml`）的插件段加入 `jobs` 列表：

```yaml
dsh-cron:
  jobs:
    - id: nightly-backup
      title: Nightly backup
      schedule: "0 3 * * *"
      type: script
      prompt: "bash /path/to/backup.sh"
      channels: ["telegram"]
      timeoutSeconds: 3600
    - id: morning-digest
      title: Morning digest
      schedule: "0 8 * * 1-5"
      type: llm
      prompt: "Prepare a brief morning digest of active tasks."
      provider: my-provider
      model: provider-id/model-id
```

* 每条必填：`id`、`title`、`schedule`；以提示词承载有效载荷的类型（`script`、`node`、`python`、`ssh`、`docker`、`llm`、`skill`、`workflow`）还需非空 `prompt`。`http` 例外：目标由 `httpUrl`（或 `prompt`）给出。
* 其余任务字段按原样透传，校验与 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`）。
* 声明式任务标记为**由配置管理**；面板中显示来源标签而不是编辑/删除按钮。
* 对配置任务的编辑、暂停、恢复、切换与删除在面板和 API 上返回 `409`，携带配置任务现有 `id` 的创建或更新请求 `POST /dsh-cron/tasks` 同样被拒绝 —— 配置文件的来源为唯一真值。**立即运行**仍然可用。
* 通过 UI、API 或智能体工具创建的、`id` 相同的任务绝不会被覆盖：该条目会被跳过，冲突写入日志。
* 会执行代码的类型照常激活，但启动时插件会向日志写警告，使通过配置引入的代码路径可见。
* 条目逐条校验并带下标（`config.jobs[i]: …`）；一条坏条目会被跳过，不会阻止其余任务或整个配置。

### 16. 外部 REST API（`/dsh-cron/api/*`，#54）
外部系统（CI、宿主机 cron、`curl`）无需打开面板即可驱动调度器。这是唯一由 bearer 令牌保护的接口；面板路由保持本地且防跨站。

令牌是插件设置 `apiToken`（与所有密钥一样掩码显示）。认证与错误：
* 未配置令牌 → 整个接口返回 `503`；
* 缺少或错误的 `Authorization: Bearer <token>` → `401`，比较为常量时间。

| 方法 | 路径 | 说明 |
|:---|:---|:---|
| `GET` | `/dsh-cron/api/tasks` | 任务列表（`status` / `query` 过滤，同面板） |
| `GET` | `/dsh-cron/api/tasks/:id` | 读取单个任务 |
| `POST` | `/dsh-cron/api/tasks` | 创建任务；带 `id` 时更新现有任务 |
| `DELETE` | `/dsh-cron/api/tasks/:id` | 删除任务 |
| `POST` | `/dsh-cron/api/tasks/:id/run` | 强制执行一次 |

这些操作复用面板处理器，因此对会执行代码类型的 `x-dsh-cron-confirm: script` 门禁以及对配置任务的 `409` 拒绝与 UI 完全一致。
* **Bearer Token 远程访问支持** — 远程客户端凭有效 `Authorization: Bearer <token>` 凭据可直接创建/修改任务、打卡心跳（`/dsh-cron/api/heartbeat/:id`）及预览调度（`/dsh-cron/api/schedule/preview`），解除非本机限制。

```bash
BASE="http://127.0.0.1:3080"
TOKEN="<API_TOKEN>"

# 列表
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks"

# 创建；请求体带 id 时为更新
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"id":"cleanup","title":"Cleanup","schedule":"0 4 * * *","prompt":"Remove stale temporary files."}' \
  "$BASE/dsh-cron/api/tasks"

# 强制执行
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks/cleanup/run"

# 删除
curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$BASE/dsh-cron/api/tasks/cleanup"

# 会执行代码的任务还需确认头
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "x-dsh-cron-confirm: script" \
  -H "Content-Type: application/json" \
  -d '{"title":"Disk check","schedule":"0 * * * *","type":"script","prompt":"df -h"}' \
  "$BASE/dsh-cron/api/tasks"
```

### 17. Prometheus 指标（#53）
`GET /dsh-cron/metrics` 返回 Prometheus 文本格式，无需新增依赖即可被抓取：

* `dsh_cron_tasks_total{status}` —— 按状态统计的任务数（gauge）。
* `dsh_cron_task_last_duration_seconds{task}` —— 任务最近一次完成运行的耗时（秒，gauge）。
* `dsh_cron_runs_total{status}` —— 自插件进程启动以来完成的运行数（counter）；状态为 `success`、`error`、`timeout`、`skipped`、`missed`。
* `dsh_cron_run_records` —— 当前保存在内存中的运行记录数（gauge）。

导出内容只有计数、状态和耗时；提示词、运行输出与任务配置不会出现在其中。

```yaml
scrape_configs:
  - job_name: dsh-cron
    static_configs:
      - targets: ["127.0.0.1:3080"]
    metrics_path: /dsh-cron/metrics
```

### 18. 严格的渠道校验（#121）
创建或更新任务时若包含未知的投递渠道 id，现在会返回 `400` 并列出违规项：

```json
{ "ok": false, "error": "Unknown channel ids: email_ping", "unknownChannels": ["email_ping"] }
```

Changed in v0.2.7：此前未知 id 会被静默丢弃，客户端即使有拼写错误也会得到 `ok: true`，最终得到一个不投递任何地方的任务。

导入有意保持宽容（文件可能来自旧版本）：未知 id 会从导入的任务中丢弃，但会在响应（`unknownChannels`）中列出并写入调度器日志，而不是无声消失。

### 19. 安装后校验（#126）
`deploy.sh` 新增仅校验模式，用于检查已安装的配置而不安装任何东西：

```bash
bash deploy.sh verify [exact-version]
```

它确认配置报告了指定版本（默认取 `package.json` 的版本），登录 Web UI，然后下载客户端 bundle 并确认其中包含包名。

为什么需要它：Web 配置可能位于认证插件之后并对匿名请求返回 `401`，而插件客户端 bundle 只能通过认证后索引中打印的精确组合 `??` URL 获取 —— 裸的 `/plugins/<name>/client.js` 会返回 `404`。因此校验需要先建立已认证会话。

校验使用的环境变量：`DSH_WEB_BASE`（默认 `http://127.0.0.1:3080`）、`DSH_WEB_TOKEN`（令牌；未设置时脚本从单元日志读取最后一个）、`DSH_WEB_UNIT`（默认 `dsh-web.service`）。脚本中不含任何密钥。

### 20. 内部重构：调度解析与排程（#97）
面向开发者，行为不变。`parseScheduleExpression` 被拆分为保持相同分支顺序的小函数 —— `parseAtExpression`、`parseRelativeOneShot`、`parseIntervalExpression`、`parseAliasExpression`、`parseCronExpression`，`scheduleTask` 拆分为 `clearScheduled`、`scheduleOneShot`、`scheduleCron`。原有测试全部通过，并新增了针对分支优先级与错误的测试。

### 21. 性能与进程隔离增强包（v0.2.9，#134）
- **进程树终止隔离**：Shell 和 Script 任务在独立进程组启动（POSIX 下 `detached: true`）；中止或超时向整组发送 `-child.pid SIGTERM -> SIGKILL`，杜绝孤儿进程与僵尸进程。
- **并发控制限流**：默认安全阈值 `maxConcurrent = 2`，避免定时重叠引发 CPU 和内存峰值。
- **瞬态错误重试**：针对网络抖动和模型速率限制（`429`、`502`、`503`、`504`、`ECONNRESET`）提供指数退避重试（最多3次）。
- **网络与前端优化**：`GET /dsh-cron/tasks` 支持 `ETag` 与 `304 Not Modified`；前端页面根据 `visibilityState` 自适应轮询（前台 8s，后台 30s）。
- **历史记录轮换与归档**：活动任务仅保留最新 100 次运行，超出部分自动归档至 `tasks-history-archive.json`。
- **自主 PR 审查配方 (#33)**：Template Hub 预置配方与 `prReviewerEnabled` 设置项。

### 22. 自动化、任务链与可观测性包（v0.2.10，#137）
- **Telegram 双向交互控制**：任务通知附带内嵌操作按钮（`🚀 立即运行`、`⏸️ 暂停/恢复`、`📋 最新日志`）。由 `POST /dsh-cron/telegram/webhook` 处理，严格鉴权 Chat ID 并调用 `answerCallbackQuery` 反馈。
- **任务链上下文与动态变量插值**：配置 `onSuccess` 与 `onFailure` 下游触发器。父任务的执行结果与元数据自动传递给子任务，在 Shell 任务中提供 `$DSH_PREV_OUTPUT`、`$DSH_PREV_TASK_ID`、`$DSH_PREV_STATUS` 环境变量，在 LLM Prompt 中支持 `{{prev.output}}`（或 `{{prevOutput}}`）、`{{prev.taskId}}`、`{{prev.status}}` 占位符插值。Prompt 额外支持动态运行时时间与元数据变量：`{{date}}`、`{{time}}`、`{{datetime}}`、`{{timestamp}}`、`{{year}}`、`{{month}}`、`{{day}}`、`{{taskId}}`、`{{taskName}}`、`{{runCount}}`。内置最大 5 级深度递归防护，杜绝死循环。
- **模型结构化动作指令**：自主分析任务可输出 JSON 指令触发级联任务（`trigger_task`）、定向告警（`notify`）或创建 Issue。受 `llmActionsEnabled: false` 严格保护。`trigger_task` 指令共享全局链路深度上限（`chainDepth < 4`，最大 5 层调用），彻底阻断自调用死循环与 A → B → A 循环递归，拒绝原因完整记入历史与操作日志中。
- **历史归档与延迟洞察**：REST 接口 `GET /dsh-cron/tasks/:id/archive`（支持分页）与 `GET /dsh-cron/tasks/:id/stats`；UI 任务卡片展示耗时彩色徽章（<5s 绿，<30s 黄，≥30s 红）。
- **Prometheus 监控增强**：`/dsh-cron/metrics` 导出当前活动并发量 `dsh_cron_concurrent_running`、各任务 Token 计数器及成本预估指标。

### 23. 高级可靠性、自愈、心跳与体验包（v0.2.11，#139）
- **心跳与寂静监控（Heartbeat / Dead Man's Snitch）**：针对外部备份与后台作业提供反向监控。外部脚本定期向 `/dsh-cron/heartbeat/:id` 发送请求；超出 `heartbeatIntervalSeconds` + 宽限期未打卡时，任务标记为 `missed`，即刻推送失联告警并触发 `onFailure` 应急流程。
- **执行前置检查（Pre-flight Gates）**：执行前先验证条件（HTTP 状态 2xx、命令退出码 0、最低可用磁盘 MB）。未通过直接置为 `skipped`，杜绝因外部环境异常产生无意义的模型 Token 消耗与错误干扰。
- **试运行与调度模拟器（Dry-Run & Simulator）**：接口 `POST /dsh-cron/tasks/:id/dry-run` 与 UI `🧪 试运行` 按钮支持无副作用执行（不入库历史、不发渠道通知）；`POST /dsh-cron/schedule/preview` 实时计算未来 5 次运行时间。
- **优先级队列与并发池（Priority Queues）**：并发满载时，等待队列严格依据任务 `priority`（1 最高，10 最低）调度。
- **自愈脚本与 AI 根因诊断（Self-Healing）**：任务失败后自动执行补偿指令 `selfHealingCommand`（例如重启服务或清理临时空间）；`autoDiagnose` 自动生成 AI 故障根因摘要。
- **UI 交互式归档与管道全景**：支持分页浏览任务历史运行全量输出，直观展示 `➜ 成功触发` 与 `↳ 失败触发` 关联关系。
- **Shell 与磁盘前置检查强化**：前置检查统一支持 `command` 与 `shell` 类型，未知类型或异常格式一律安全关闭（fail-closed）；磁盘检查支持完整路径与阈值（如 `/:10%`、`/data:500MB`）。
- **可靠性与会话字段完整持久化**：HTTP POST 任务创建/修改接口与 `cron_create_task` 智能体工具完整保留并存储全部 13 个可靠性与会话高级字段（`agentPreset`、`targetSessionId`、`targetSessionReset`、`onSuccess`、`onFailure`、`heartbeatIntervalSeconds`、`gracePeriodSeconds`、`preflightType`、`preflightTarget`、`priority`、`concurrencyGroup`、`selfHealingCommand`、`autoDiagnose`、`fallbackProvider`、`fallbackModel`、`silentRule`、`inspectOnFailure`）。

---

### 24. 自动挂载智能体预设与工具支持 (#141 / GH-1，v0.2.12 新增)
- **自动挂载智能体预设**：计划执行的自主 `llm` 任务和交互式启动现在会自动解析并挂载系统智能体预设（默认通过 `setup` 钩子中的 `presets.mount(agentCtx, preset.id)` 挂载用户的标准预设）。计划会话现已具备完整的工具调用能力（文件读写、工作区操作、Shell 终端等），彻底解决此前空会话无工具调用的问题。
- **单任务预设覆盖**：可在 Web 管理界面、REST API 或配置文件中为具体任务配置独立的 `agentPreset` 标识（例如 `coding`、`system`、`minimal`）。未设置时自动继承系统默认预设。
- **优雅降级保障**：当未安装 `agentPresets` 服务或指定了未知的预设 ID 时，调度器仅记录友好的警告日志，并安全平稳地继续执行基础模型会话，避免定时任务中断。

---

### 25. 常驻持久会话与上下文延续 (`targetSessionId`，v0.2.13 新增，#143)
- **跨周期会话上下文延续**：支持在任务中配置 `targetSessionId`。设置后，调度器将在每次定时触发时通过 `agents.resume()` 唤醒已有会话，而不再每次生成孤立的临时会话（`cron-exec-${id}-${uuid}`）。智能体能够完整继承上一轮对话的历史记忆与分析结论。
- **上下文窗口保护与轮转机制 (`targetSessionReset`)**：为防止高频执行导致模型上下文窗口超限与 Token 成本暴增，支持智能轮转策略：
  - `never`：持续累积单一会话，不重置。
  - `daily`：每日自动开启全新子会话（后缀 `<id>-YYYY-MM-DD`）。
  - `weekly`：每周自动开启全新子会话（后缀 `<id>-YYYY-Www`）。
  - 日期变量插值：`targetSessionId` 中支持 `{{date}}` 占位符，自动注入当前日期 `YYYY-MM-DD`。
- **主界面原生可见交互**：持久会话不会被标记为 `ephemeral`/`internal`，且在执行后跳过自动归档（`workspaceRegistry.archiveSession()`），用户可在 DSH 聊天列表中直接查看并继续手动对话。
- **预设工具链无缝适配**：恢复会话时同样完整挂载 `agentPresets`，保障文件读写、代码编辑与终端工具持续可用。
- *致谢*：功能灵感源自社区开发者 [@RaulLazaro](https://github.com/RaulLazaro)。

---

### 26. 系统稳定性、硬化与自愈维护包 (v0.2.14, #145)
- **重试预算自动重置**：彻底修复重试耗尽后的计数残留问题。当任务耗尽配置的重试次数（`maxRetries`）后，`attempts` 计数器自动清零，确保后续周期的定时调度享有完整的重试预算。常规计划执行或手动触发也均保证以干净的重试预算启动。
- **清除队列僵尸任务**：通过界面/API 暂停或删除任务时，调度器会立即将其从并发等待队列（`this.queue`）中剔除；并发槽位释放出队时，非激活或已删除的任务也会被自动安全跳过。
- **历史归档容量上限保护**：针对长期运行和高频调度的生产环境，`tasks-history-archive.json` 针对每个任务安全限制保留最新的 1,000 条运行记录，消除无限制磁盘占用与同步 JSON 序列化卡顿。
- **灾难恢复与存储自动备份**：`TaskStore` 在每次成功持久化时自动维护原子的 `tasks.json.bak` 备份副本。若发生进程异常导致数据损坏，存储引擎会自动保存现场切片 `tasks.json.corrupted.<timestamp>` 供故障分析，并无缝从备份中自愈恢复。
- **上下文超限自愈与平滑轮转**：在常驻持久会话（`targetSessionId`）中，若智能体因模型上下文窗口溢出（`context_length_exceeded`）失败，运行器将精准捕获超限错误，归档已满会话，自动轮转至全新子会话并平滑重试，避免任务中断。
- **Windows 进程树彻底终止**：在 Windows 系统上，外部进程任务被取消或超时终止时，改为执行 `taskkill /pid <pid> /T /F`，杜绝孤儿进程和后台残留外壳。

---

### 27. 弹窗视口高度自适应与包体积精简 (v0.2.15, #153, #148, #151, #152)
- **视口高度约束与粘性底部操作栏**：所有弹窗（包括任务编辑、新建及推荐模板预设）现已严格受控于视口尺寸 `max-height: min(90vh, calc(100vh - 36px))`，并内置平滑纵向滚动条。弹窗操作栏（“取消”、“保存”、“创建”）采用 `position: sticky` 底部悬浮固定，确保无论表单项多长或屏幕分辨率高低，操作按钮始终清晰可见且可随时点击。
- **遮罩层滚动溢出保护**：弹窗遮罩层增加了安全边距与 `overflow-y: auto`，防止小屏设备在 Flex 居中时发生头部或底部截断。
- **npm 包体积深度精简**：从 npm 分发清单中剔除了多余的重复文档副本，使 tarball 体积立减 32 kB 以上，解压后体积减少约 102 kB。
- **Cordis 客户端注入依赖规范化**：在 `package.json` 的 `dsh.client.inject` 中完整声明了 `locale` 和 `slots` 服务依赖。

---



### 31. 质量加固与 CI 预检标准包 (v0.2.19, #149, #152, #155, #156, #162)
- **彻底消除空 catch 块 (#156)**：引入符合标准规范的 `lib/best-effort.js` 模块，全面支持同步/异步安全调用、回退返回值和可选日志记录，清除了 runner、scheduler、store 及客户端中的所有 63 处空 catch 块。
- **CI 工作流与本地预检门禁 (#162)**：新增自动化 CI 工作流（`.gitea/workflows/ci.yml` 与 `.github/workflows/ci.yml`），集成本地预检门禁脚本 `scripts/ci-preflight.mjs`，在出现语法错误、空 catch、颜色硬编码或信息泄露时自动阻断。
- **主题设计令牌现代化 (#149)**：将模态对话框与设置卡片中遗留的 `rgba(...)` 全部重构为原生的 `color-mix(in srgb, var(--token) N%, transparent)`。
- **插件清单注入声明规范化 (#152)**：在 `package.json` 的 `dsh.client.inject` 中统一声明完整的 Cordis 包名（`@deepseek-ai/dsh-client-locale`、`@deepseek-ai/dsh-client-ui-slots`）。
- **生产管线导出连接 (#155)**：将此前仅在测试中调用的导出（`findDestructiveRecipe`、`shouldNotifyTask` 和 `TEMPLATE_VARIABLES`）全面接入配方过滤、通知分发及模板生成核心逻辑。

---

### 30. 自更新模块英文与中文多语言支持 (v0.2.18, #160)
- **设置面板自更新多语言 (#160)**：在 `lib/client-src/10-locales.js` 的英文 (`en`) 与中文 (`zh`) 字典中补全了全部 10 个自更新键值 (`updater.title`, `updater.btnCheck`, `updater.checking`, `updater.btnUpdate`, `updater.updating`, `updater.desc`, `updater.current`, `updater.available`, `updater.upToDate`, `updater.success`)。遵循 DSH 插件规范，插件核心内置英文与中文，俄语多语言由 `dsh-russian-lang` 统一扩展。

---

### 29. 客户端模块化解耦与原生主题标准化 (v0.2.17, #149, #150)
- **客户端模块化架构 (#150)**: 将庞大的单文件 `lib/client.js` (约 3950 行) 拆分为 `lib/client-src/` 下的 14 个高内聚模块文件 (各模块严格 <= 580 行)。集成零依赖构建脚本 `scripts/build-client.mjs` 并接入 `package.json` (`build:client`, `pretest`)，通过 `"files": ["lib/*.js", ...]` 避免开发源码冗余打包进 npm 发布包。
- **DSH 语义化主题变量对齐 (#149)**: 将任务类型标签 (`onSuccess`, `onFailure`, `heartbeat`, `targetSession`, `preflight`) 的内联样式全部替换为基于主题变量的 `.dsh-cron-tag-*` 类；模态框遮罩层接入自适应主题遮罩变量 `var(--dsw-alias-bg-mask, rgba(0, 0, 0, 0.75))`，移除脉冲动画关键帧中的硬编码 RGBA。
- **标签治理与工单审计 (#95)**: 审计并确认全仓库 100% 统一规范使用仓库级标签集。
- **动态 DOM 清理下的样式自动恢复（#263 / GH-4）**: 通过 `MutationObserver`、2 秒兜底定时器与 `visibilitychange` 事件（`startStyleSelfHeal`）监听 `<head>` 与 `<html>`，当宿主重绘 `<head>`、切换主题或邻近插件清理样式时，自动重新注入并修复 `<style id="dsh-cron-styles">` 标签。

---

### 28. 应用内一键自更新与错误容错增强 (v0.2.16, #147, #155, #156)
- **插件自更新模块 (#147)**：新增应用内一键自更新机制 (`lib/updater.js`)、`/api/dsh-cron/update` 接口与设置面板专属卡片。自动轮询 npmjs 仓库最新版本，精准比对 Semver 版本号（含预发布版本），通过 DSH CLI 就地升级 `@goodandready/dsh-cron`，无需 SSH 终端操作。POST 请求受跨域与来源保护 (`rejectCrossOrigin`)。
- **消除静默异常与错误追踪 (#156)**：全面消除空 `catch` 异常压制：任务导入事务回滚失败记录 `warn` 级别日志、动态核心模块降级原因记录诊断日志、会话自动唤出失败向用户呈现友好提示。
- **死代码清理与模块导出精简 (#155)**：清理未引用的旧版实体 (`CHANNEL_LABELS`, `makeInspectAsk`)，收回 16 个内部工具函数的暴露权限，并将 `supportsSilentRule` 接入静默规则核心执行流水线。

---

## 📦 安装

```bash
dsh plugin --profile web add @goodandready/dsh-cron
```

重启 DeepSeek Harness 实例并刷新浏览器。

---

## ⚙️ 配置（`settings.yaml`）

可以在 `settings.yaml` 中配置，也可以通过 DSH 中的插件设置卡片交互式管理：

```yaml
# settings.yaml
dsh-cron:
  botToken: ""                 # Telegram Bot API 令牌（保密字段）
  chatId: ""                   # 接收报告的 Telegram chat ID
  notifyTelegram: false        # 全局投递所有任务的报告
  onlyOnFailure: false         # 仅失败时投递报告
  kanbanBaseUrl: "http://127.0.0.1:3000"  # dsh-kanban HTTP API 基础地址
  defaultTimezone: ""          # 默认 IANA 时区（空 = 服务器本地）
  maxConcurrent: 2             # 最大并行运行数（默认 2，0 = 不限）
  heartbeatUrl: ""             # 心跳上报 URL（dead man's snitch）
  heartbeatIntervalSec: 0      # 心跳间隔秒数（0 = 关闭）
  # --- 投递渠道 ---
  botTokenRef: ""              # Telegram bot token 的凭据名称
  template: ""                 # 全局消息模板，例如 "⏰ {title} — {status}"
  channelTemplates: {}         # 按渠道覆盖模板
  deliveryTimeoutMs: 15000     # 每个渠道的投递超时；慢端点记为失败，不影响其他渠道
  discordWebhookUrl: ""        # Discord webhook
  slackWebhookUrl: ""          # Slack incoming webhook
  ntfyUrl: "https://ntfy.sh"   # ntfy 服务器；ntfyTopic / ntfyTokenRef
  ntfyTopic: ""
  ntfyTokenRef: ""
  barkServerUrl: "https://api.day.app"  # Bark 服务器；barkKey = 设备键
  barkKey: ""
  pushplusUrl: "https://www.pushplus.plus/send"  # pushplusTokenRef
  pushplusTokenRef: ""
  ttsBaseUrl: "http://127.0.0.1:3080"   # dsh-tts 基础地址
  giteaBaseUrl: ""             # giteaRepo = owner/repo，giteaTokenRef = 凭据名称
  giteaRepo: ""
  giteaTokenRef: ""
  # --- 外部 REST API（#54）---
  apiToken: ""                 # 外部 /dsh-cron/api/* 接口的 bearer 令牌（掩码；空 = 503）
```

### 配置参数

| 参数 | 类型 | 默认值 | 说明 |
|:---|:---|:---|:---|
| `botToken` | `string` | `""` | Telegram Bot API 令牌。留空时插件会尽力继承 DSH 设置中 `dsh-messenger-gateway` 配置的机器人。保密字段：界面只显示掩码值 |
| `chatId` | `string` | `""` | 接收报告的 Telegram chat ID。留空时回退到 `dsh-messenger-gateway` 的第一个允许会话 |
| `notifyTelegram` | `boolean` | `false` | 全局开关：向 Telegram 投递运行报告 |
| `onlyOnFailure` | `boolean` | `false` | 全局开关：仅对 `error`/`timeout` 运行投递报告 |
| `kanbanBaseUrl` | `string` | `"http://127.0.0.1:3000"` | 用于自动卡片的 `dsh-kanban` HTTP API 基础地址 |
| `defaultTimezone` | `string` | `""` | 任务调度的默认 IANA 时区；空 = 服务器本地时间 |
| `maxConcurrent` | `number` | `2` | 并行运行上限；超出的运行记录为 `skipped`（默认 2，0 = 不限） |
| `heartbeatUrl` | `string` | `""` | 心跳上报 URL，调度器存活期间按 `heartbeatIntervalSec` 间隔 GET |
| `heartbeatIntervalSec` | `number` | `0` | 心跳间隔秒数（0 = 关闭） |
| `botTokenRef` | `string` | `""` | 保存 Telegram bot token 的 DSH 凭据名称；发送时解析（回退顺序：`botToken` → messenger-gateway 设置 → 环境变量 `CRON_TELEGRAM_BOT_TOKEN`） |
| `template` | `string` | `""` | 带 `{title}`/`{status}`/`{duration}` 等占位符的全局消息模板；留空使用内置文本 |
| `channelTemplates` | `object` | `{}` | 按渠道 ID 覆盖模板（`telegram`、`discord` 等） |
| `deliveryTimeoutMs` | `number` | `15000` | 每个渠道的投递超时；超时的端点记为失败，不拖慢其他渠道或下一次调度 |
| `discordWebhookUrl` / `slackWebhookUrl` | `string` | `""` | Discord 与 Slack 渠道的 webhook 地址 |
| `ntfyUrl` / `ntfyTopic` / `ntfyTokenRef` | `string` | `"https://ntfy.sh"` / `""` / `""` | ntfy 服务器、主题与可选的 token 凭据名称（以 `Authorization: Bearer …` 发送） |
| `barkServerUrl` / `barkKey` | `string` | `"https://api.day.app"` / `""` | Bark 服务器与设备键（键、标题和正文位于请求路径中） |
| `pushplusUrl` / `pushplusTokenRef` | `string` | `"https://www.pushplus.plus/send"` / `""` | PushPlus 端点（可指向自建代理）与 token 凭据名称 |
| `ttsBaseUrl` | `string` | `"http://127.0.0.1:3080"` | 用于语音播报的 `dsh-tts` 基础地址 |
| `giteaBaseUrl` / `giteaRepo` / `giteaTokenRef` | `string` | `""` | Gitea 渠道：基础地址、`owner/repo` 与 API token 的凭据名称 |
| `telegramAllowedChatIds` | `string` | `""` | 允许执行交互式机器人命令的 Telegram Chat ID 或 User ID（英文逗号分隔） |
| `apiToken` | `string` | `""` | 外部 `/dsh-cron/api/*` 接口的 Bearer 令牌。保密字段，返回时掩码；为空时接口返回 503，错误值返回 401 |

说明：

* 运行历史上限为**每任务 50 条**（固定）；每条记录最多保留 4000 字符输出。
* 任务在**服务器本地时区**执行；cron 表达式由 `croner` 按主机时钟计算。
* 任务持久化在 DSH 数据目录（`cron/tasks.json`），重启后保留；启动时会检测错过的一次性任务。

---

## 🔌 HTTP API 参考

所有端点由 DSH Web 服务器在 `/dsh-cron/` 下提供。所有端点均受到强化的 HTTP 来源防护（`isTrustedRequest`）：非回环远程客户端必须携带有效令牌（`Authorization: Bearer <token>` 或 `x-dsh-cron-token`），浏览器请求严格校验 `Host` 与 `Origin` 一致性并拒绝 `Origin: null`，同时限制 `Sec-Fetch-Site` 仅允许 `same-origin` 或 `none`。心跳 ping 端点严格要求 `POST` 方法。任务 `GET` 响应自动将敏感字段（`env`、`httpHeaders`、`httpBody`）掩码为 `'[REDACTED]'`，并在更新操作提交 `'[REDACTED]'` 时安全保留已有原密钥。通过 HTTP 创建 `script` 类型任务还需要 `x-dsh-cron-confirm: script` 请求头。请求体大小上限为 1 MB。

| 方法 | 路径 | 说明 |
|:---|:---|:---|
| `GET` | `/dsh-cron/tasks` | 任务列表；查询参数 `status`（`all/active/paused/completed`）、`query`（子串搜索）。返回任务、推荐模板与汇总统计 |
| `POST` | `/dsh-cron/tasks` | 创建或更新任务（携带 `id` 时为更新）。需要 `title`、`schedule`、`prompt` |
| `GET` | `/dsh-cron/tasks/:id/history` | 运行历史，`?limit=20` |
| `POST` | `/dsh-cron/tasks/:id/run` | 立即手动运行 |
| `POST` | `/dsh-cron/tasks/:id/pause` | 暂停调度 |
| `POST` | `/dsh-cron/tasks/:id/resume` | 恢复调度 |
| `POST` | `/dsh-cron/tasks/:id/toggle` | 切换活跃/暂停 |
| `POST` | `/dsh-cron/tasks/:id/duplicate` | 创建暂停状态的副本：复制配置，重置运行历史与计数 |
| `GET` | `/dsh-cron/recipes` | 内置配方目录：按类别分组的现成监控预设，全部为只读操作 |
| `GET` | `/dsh-cron/tasks/export` | 仅含任务配置的版本化 JSON —— 不含历史与计数。渠道按名称引用凭据，但手动填写在任务中的 `env` 与 HTTP 请求头属于配置，会出现在文件里 |
| `POST` | `/dsh-cron/tasks/import` | 校验文档并以 `add`、`replace` 或 `skip` 策略导入；支持 `dryRun` 预览。导入的任务始终为**暂停**状态，恢复不会自动触发 |
| `PATCH` | `/dsh-cron/tasks/:id` | 部分更新（仅白名单字段：`title`、`schedule`、`prompt`、`type`、`delivery`、`provider`、`model`、通知/超时/重叠/Kanban 设置、`status`、`oneShot`） |
| `DELETE` | `/dsh-cron/tasks/:id` | 删除任务 |
| `GET` | `/dsh-cron/models` | 列出 LLM 提供方；`?provider=<id>` 列出模型 |
| `POST` | `/dsh-cron/chat/start` | 启动带任务配置指令的“由 DSH 创建”智能体会话 |
| `GET` | `/dsh-cron/settings` | 客户端安全设置（令牌掩码显示） |
| `POST` | `/dsh-cron/settings` | 更新集成设置（通过设置服务应用） |
| `GET` | `/dsh-cron/heartbeat` | 存活探针：活跃任务数与最近运行时间 |
| `POST` | `/dsh-cron/telegram/test` | 发送 Telegram 测试消息 |
| `POST` | `/dsh-cron/kanban/test` | 创建 Kanban 连通性测试卡片 |
| `*` | `/dsh-cron/action/:id/:action` | 任务操作路由的兼容别名（`run`、`toggle`、`delete`、`history`） |
| `GET` | `/dsh-cron/metrics` | Prometheus 文本格式的任务与运行计数 —— 不含提示词与输出（#53） |
| `GET` / `POST` | `/api/dsh-cron/update` | 插件一键自更新：查询仓库最新版本并就地平滑升级 (#147) |
| `GET` / `POST` | `/dsh-cron/api/tasks` | 令牌保护的外部接口：列表 / 创建或更新（#54） |
| `GET` / `DELETE` | `/dsh-cron/api/tasks/:id` | 令牌保护的外部接口：读取 / 删除（#54） |
| `POST` | `/dsh-cron/api/tasks/:id/run` | 令牌保护的外部接口：强制执行（#54） |
| `POST` | `/dsh-cron/webhook/:id` | 入站 HTTP Webhook 触发器：支持 HMAC SHA-256 / 令牌鉴权、防抖与 JSON 负载提示词变量插值（#348） |
| `POST` | `/dsh-cron/api/webhook/:id` | 令牌保护的入站 Webhook 外部接口（#348） |

---

### 32. 入站 HTTP Webhook 与接口鉴权加固 (v0.3.12, #348, #357, #358)

- **入站 HTTP Webhook 触发器 (`/dsh-cron/webhook/:id`)**：外部事件（GitHub、Gitea、CI/CD 及第三方服务）实时触发任务执行。
  - **安全鉴权**：支持基于任务级 `webhookSecret` 的 HMAC SHA-256 签名（`x-hub-signature-256`、`x-signature-sha256`）、任务级 `webhookToken`（仅限标头：`x-dsh-cron-webhook-token`、`x-webhook-token`、`Authorization: Bearer`）以及全局 `apiToken` 鉴权。严格禁止从 URL 查询参数（`?token=`）传递令牌以防止日志泄露（#358）。
  - **深度 JSON 负载插值**：提示词中通过 `{webhook.body.path.to.key}`、`{webhook.headers.key}`、`{webhook.query.param}` 及 `{webhook.body}` 自动解构提取入站数据。
  - **环境注入**：脚本与 shell 任务自动注入 `DSH_CRON_WEBHOOK_BODY` 和 `DSH_CRON_WEBHOOK_HEADERS` 环境变量。
  - **防抖保护**：可配置 `webhookDebounceMs`（默认 500ms）有效防止 Webhook 事件风暴与瞬态重复触发。
- **Fail-Closed 接口鉴权与 RCE 防御（#357）**：所有 REST 端点通过 DSH `ctx.connection.requestRejection(req)` 校验会话权限，并对无有效令牌及非本地回环地址的请求实行 Fail-closed 严格拦截（401/403），杜绝未授权远程任务创建与代码执行。

---

## 🧪 测试与预检门禁 (Preflight)

运行全套自动化测试（调度表达式解析、调度器引擎、原子存储、HTTP 辅助函数、通知与工具契约）：

```bash
npm test
```

本地执行质量预检门禁（语法检查、零空 catch 块、主题设计规范、npm 发布归档合规性及防泄漏检查）：

```bash
node scripts/ci-preflight.mjs
```

---

## 📄 许可证

MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)

### 定时器、间隔与并发分组精度强化 (0.2.39)

- **32位定时器溢出防护**：计划在 24.85 天以后的单次任务（超出 Node.js `setTimeout` 32位有符号整数上限 2,147,483,647 毫秒）通过分段定时器安全挂起，杜绝立即误触发。
- **相对单次任务持久截止时间**：相对单次任务（如 `in 30m`、`in 2h`）在服务重启、存储重载、暂停/恢复以及修改元数据时完整保留初始目标绝对时间戳，仅在显式修改调度表达式时重新计算。
- **严格的时间间隔校验与转换**：对于无法用标准 Cron 分钟步进表示的超限分钟间隔（如 `every 90m`），在持久化前返回 HTTP 400 拦截；规整整倍数（如 `every 120m` -> `every 2h`、`every 24h` -> `every 1d`）自动转为合法 Cron 表达式。
- **统一安全并发默认值**：配置规范（Config）与调度器（TaskScheduler）全面对齐默认并发上限 `maxConcurrent = 2`（0 仅作为显式无限制选项）。
- **并发分组隔离调度**：任务的 `concurrencyGroup` 属性全面接入调度仲裁与队列排队（同名分组默认限流 1 个并发执行），不同分组可并发运行直至 `maxConcurrent`，队列任务随所属分组资源释放立即触发。


### 事件驱动唤醒触发器 (v0.3.33)

基于本地系统事件响应式唤醒智能体任务：
- **文件数量阈值 (`file_count`)**: 目标目录下积累 `>= N` 个匹配文件时自动触发批量处理。
- **标记/Flag文件到达 (`marker_file`)**: 标志文件出现时触发并自动删除标记文件。
- **进程退出监听 (`process_exit`)**: 监听 PID 或 `.pid` 文件，后台任务退出后立即唤醒任务进行后续处理。
上下文自动注入到提示词变量 (`{{trigger.type}}`, `{{trigger.fileCount}}`) 及环境变量中。

### 时间预算与运行窗口 (v0.3.34)

- **Time-Boxing (`timeBoxMinutes`)**: 设置单次执行的最长分钟上限，超时时通过 AbortSignal 优雅中止。
- **时间窗口 (`workWindow`)**: 限制高负载任务仅在指定时段（如 `22:00-07:00`）或特定星期内运行。
- **窗口外策略 (`outsidePolicy`)**: 支持 `skip`（跳过本次运行）或 `defer`（推迟到窗口开启时自动执行）。

### 每日/定期简报与成本汇总 (v0.3.35)

- **简报专用任务类型 (`type: "digest"`)**: 可定时调度汇总报告（如每日早晨 09:00），聚合所有任务的运行状态、失败诊断、Token 消耗与估算成本。
- **REST API**: 支持 `GET /dsh-cron/digest` 查询统计报告，以及 `POST /dsh-cron/digest/send` 主动推送到各通知渠道。

### 代码化 Cron（`.dsh/cron.yaml`）
直接在项目仓库中声明定时任务。`dsh-cron` 自动发现并同步 `.dsh/cron.yaml`、`.dsh/cron.yml` 或 `.dsh/cron.json`：
```yaml
tasks:
  - id: nightly-audit
    title: 每夜安全检查
    schedule: "0 2 * * *"
    command: npm audit
    timeBoxMinutes: 15
```
Git 管理的任务具有隔离标识，绝不影响或覆盖 UI 手动创建的任务。

### 会话快捷操作栏（In-Session Quick Action Bar）
在 DSH 活跃聊天会话中直接管理定时任务（`conversation.session.header.actions`）：
- **一键转为定时任务（`+ 定时任务`）**：将当前对话或提示词一键转换为预填参数的定时周期任务。
- **关联任务状态与即时触发**：在会话顶部直接显示关联后台任务的执行状态，并支持一键立即执行。
