# 可复用脚本任务

## 使用方式

入口：**设置 → Agent Hub → 脚本任务**。默认没有任务，也不会自动领取任何福利。

1. 新建任务；或者（仅在装有 Qoder 中国版桌面端的 Windows 上）点模板卡片上的 **用此模板**（Qoder 中国版每日积分，只填充草稿，不保存、不执行）。其他电脑上不显示这张卡片；已有该任务时也不再显示。
2. 填写程序的绝对路径、参数 JSON 数组、工作目录和超时，选择执行模型或兼容 Bot。不要把密码、Token 或密钥放进参数。
3. 明确保存定义。保存不会创建定时任务，也不会启动脚本。
4. 核对运行程序和参数后，确认启用。DSH 为任务建立一个隔离会话及一个原生每日任务，初始时间为北京时间 10:05。
5. 点击管理入口，在 DSH 原生任务管理器中修改时间、时区、暂停或删除。插件不另设 timer，也不会在删除后自动重建任务。
6. 查看最近执行状态和脱敏结果。修改或移除定义前先停用任务；存在陈旧编辑草稿时刷新后重新编辑。

### 执行记录看哪里

以脚本任务卡片为准。卡片顶部是最近一次结果：图标、颜色和文字同时标明成功、失败、结果未知或正在执行，随后是时间、定时触发还是手动运行、耗时、非零退出码和脚本摘要。更早的记录（定时和手动合计最多 20 条）折叠在下方。原生调度已投递、但超过 10 分钟仍未开始，会显示为「已投递，但未执行」，通常说明辅助模型没有调用 `automation_run`。

DSH 原生任务管理器中的「任务运行记录」只保存投递时的提示词（固定的 `[agent-hub:automation:v1] …`），不包含脚本结果，因此不能用它判断成败。任务会话里的工具结果显示为一句可读结论，例如 `Succeeded: Claim confirmed; 100 credits verified.`。

### 立即运行一次（测试效果）

每个任务卡片有 **立即运行一次**，不需要启用调度，也不需要等待原生任务触发：

- 点击后先展示将执行的程序和参数并要求确认；确认后直接在本机运行**已保存的定义**，不经过模型、不创建会话，也不写入每日调度的投递记录。
- 运行期间同一个按钮变为 **取消运行**（终止进程树，不能撤销已发生的远端操作）。运行中不能编辑、删除或启用该任务，也不会同时再运行第二次。
- 结果显示在卡片的“手动运行”一行；运行中界面每 2 秒刷新一次，结束后恢复低频刷新。DSH 在运行中退出，重启后该次记为“结果未知”，不会自动重跑。
- Qoder 中国版预设默认只检查（不带 `--claim`），因此手动运行它只会列出活动并确认凭证、接口是否兼容；只有在参数里显式加入领取参数才会真实领取。

DSH 后端必须运行，电脑必须可运行且联网。关闭浏览器标签不等于关闭后端；电脑关机或休眠时不能保证准点执行，恢复后只能处理原生调度器投递的任务，不能补回已经结束的活动窗口。执行模型不可用或没有调用工具时，投递不等于业务成功。

脚本以 **DSH 当前用户的权限运行，不是脚本沙箱**。唯一工具限制约束的是模型能启动什么，不能约束脚本内部任意文件和网络访问。只注册审查并信任的程序；程序内容被外部修改会影响后续执行。任务定义默认不参与 profile 同步。在本机始终可管理；通过 Tailscale 从手机访问时，只有在这台电脑上打开了「允许手机修改设置」才能查看、编辑、启用和立即运行（等同于允许手机在本机执行程序）。

## 模型与 Bot

- 普通模型必须能通过 DSH 原生工具调用链调用 `automation_run`；外部会话 Agent 路由暂不支持。
- 使用 Bot 时，复用现有 Bot 的模型与人设，不启动 Team。
- Bot 必须显式将 `automation_run` 放入工具白名单；任务会话最终仍只允许这个工具，Bot 的其他工具不会获得放行。
- Bot 的模型变更会重新验证；不兼容或不可用时明确失败，不静默切换到其他模型。
- 后台压缩/标题等请求沿用宿主机制；脚本结果由工具直接展示并结束本轮，不再调用模型生成总结。

## 新脚本的结果协议

脚本 stdout 只输出一个 JSON 对象，结构如下。诊断日志可写 stderr，但执行器不会把原始 stderr 回显给模型。

```json
{"status":"ok","summary":"Completed successfully."}
```

`status` 可为 `ok`、`error`、`unknown`；`summary` 必须是非空、限长、已经脱敏的文本。退出码非零不允许被判定为成功；结果未知应返回 `unknown` 并使用非零退出码。不要输出凭证、用户名、完整服务响应或调用堆栈。执行器不会自动识别并删除任意脚本主动写入 summary 的秘密，因此脚本自身必须遵守协议。

程序与参数直接交给子进程，不拼接 Shell。执行器限制超时及输出量，并在取消或超时时终止进程树。停用会请求取消正在运行的脚本，但**取消不是事务回滚**，不能撤销已经发生的远端副作用。通过原生任务管理器删除/暂停时，运行中的取消取决于下一次状态对账；需要立即取消时使用脚本任务面板的停用。

## 运行日志（排查用）

位置：`~/.dsh/dsh-agent-hub/automation/automation.log`（JSON Lines，超过 512 KB 轮转为 `.log.1`，仅保留一份）。每次运行至少有：

- `run.start`：任务、手动/定时、程序与参数、runId。
- `runner`：退出码、是否超时/被取消/缺程序/输出超限，以及脚本 stderr 的前 8 KB 与 stdout 前 1.5 KB（仅本机日志，**不会**进入模型或界面结果；脚本不要往 stderr 写秘密）。
- `run.end`：状态、耗时、脱敏摘要。
- `run.denied` / `delivery.admitted` / `action`：为什么没有执行、何时收到投递、谁点了启用/停用/运行。

Qoder 脚本会在 stderr 输出带时间和耗时的步骤轨迹：凭证读取到哪一步、活动列表（key/状态/奖励）、是否发出领取、确认查询的结果。失败摘要带原因码，例如 `campaign:key-not-found`（并列出可用 key）、`http:status-401`、`credentials:dpapi`。日志不含 Token、密钥、用户名。

## 收据、失败与恢复

- 唯一工具 `automation_run()` 不接受参数。任务由可信的会话绑定确定，模型不能选择其他脚本或自由构造命令。
- 必须存在真实的原生投递收据。同一收据合并并发并缓存终态，修改下一次运行时间不会重跑已消费收据。
- 首版按原生目录暴露的最新 `lastDelivery.messageId` 识别投递，尚未将执行绑定到本轮具体 inbox 消息；只保证最新可见收据的消费去重，不保证逐条历史补跑。
- 先持久化执行中，再启动脚本。重启后残留执行中标为结果未知，不自动重放。
- 不承诺通用 exactly-once。脚本必须先查询业务状态或使用服务端幂等机制；POST 超时不等于远端未完成。
- 最近结果未知时先核查业务状态，不要为重试另外建立一个相同定时任务。

任务绑定和执行元信息保存在本机 DSH 数据目录下按 profile 隔离的 automation 状态文件；不存凭证。不要手工篡改状态或让多个 DSH 进程同时写同一 profile。插件热重载时，同进程内旧实例会取消运行并移交写入顺序，不能迟到覆盖新实例的状态。

如果任务状态文件损坏或不可读，系统无法可靠识别哪些会话必须受限，因此会失败关闭，并可能阻止普通会话组装提示词。此时查看宿主日志与脚本任务面板错误，优先修复文件权限或恢复可信备份；若必须重建状态，先由用户在原生任务管理器核实并停用/删除关联脚本任务，再恢复插件。不要直接忽略异常、删除去重记录或放开任务会话工具。

## Qoder 中国版

附带的 [独立脚本](../lib/bin/qoder-daily-credit.mjs) 仅适配 Windows 上已登录的 Qoder 中国版桌面客户端。只读取中国版 profile，服务地址固定为中国版；无国际版回退。使用当前 Windows 用户的 DPAPI 解密，AES 密钥不落盘，凭证不传给 helper。存储格式与内部 API 可能随版本改变；本项目不代表官方授权自动领取，启用前应确认中国版当前活动规则。

预设指向打包后的独立脚本，默认是检查模式。检查模式会读取本机登录凭证并查询活动，但绝不会发送领取 POST。脚本在收到 `--help` 时不读取凭证、不联网。

先人工运行帮助（仓库根目录下）：

```powershell
node scripts/qoder-daily-credit.mjs --help
```

经用户同意后执行检查：

```powershell
node scripts/qoder-daily-credit.mjs
```

检查活动结果，确认精确的 `campaignKey` 后，若确认要自动领取，停用任务、修改参数、保存，再启用。领取必须同时提供以下参数，不能只靠选择第一个福利活动：

```text
--claim --campaign-key <exact-campaign-key>
```

不要在不了解活动标识时猜测或硬编码示例 key。脚本核实活动类型与领取状态，领取后再次查询；不能明确证明奖励及状态时返回结果未知，不自动重发 POST。只输出已筛选的活动标识与状态，不输出原始账户信息。

脚本使用 profile 内独占锁阻止独立实例并发；锁不含凭证，异常强制退出可能留下锁文件。遇到锁占用，先确认没有脚本进程仍在运行，再人工处理残锁，不能盲目删除活跃锁。认证失效时回到中国版客户端重新登录，不自动实现账号登录或绕过验证。

## 验收边界

单元测试只使用模拟接口、合成密钥和临时目录。构建、帮助或测试通过不代表本机账户登录有效、当前 API 兼容、福利已领取或任务已启用。首次真实检查、首次真实领取与启用定时任务应分别确认。
