cron-up 26.10.32 → 26.10.34
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/CONTRIBUTING.md +61 -0
- package/README.md +228 -112
- package/docs/internals.md +190 -0
- package/docs/migration.md +47 -0
- package/docs/release.md +75 -0
- package/package.json +5 -3
- package/src/args.mjs +1 -2
- package/src/cli.mjs +18 -20
- package/src/config.mjs +22 -19
- package/src/constants.mjs +7 -17
- package/src/cron.mjs +18 -9
- package/src/display.mjs +2 -2
- package/src/list.mjs +1 -1
- package/src/logs.mjs +2 -1
- package/src/overview.mjs +7 -7
- package/src/patrol.mjs +199 -162
- package/src/registry.mjs +10 -13
- package/src/service.mjs +189 -49
- package/src/sessions.mjs +61 -20
- package/src/tasks.mjs +45 -84
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# 贡献指南
|
|
2
|
+
|
|
3
|
+
## 开发环境
|
|
4
|
+
|
|
5
|
+
- macOS,便于验证 launchd、`script(1)`、zsh 和进程行为
|
|
6
|
+
- Node.js 22 或更高版本
|
|
7
|
+
- 无第三方运行时依赖,测试直接使用 `node:test`
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone git@github.com:adaex/cron-up.git
|
|
11
|
+
cd cron-up
|
|
12
|
+
node --test
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
CI 在 macOS 上分别使用 Node 22、24 运行全部测试。需要本机联调时可执行 `npm link`,它会把开发版链接成全局 `cron-up` 命令;只做测试时不需要这一步
|
|
16
|
+
|
|
17
|
+
## 代码结构
|
|
18
|
+
|
|
19
|
+
- `bin/cron-up.mjs`:CLI 入口与顶层异常处理
|
|
20
|
+
- `src/cli.mjs`、`src/args.mjs`:参数解析、帮助和命令分发
|
|
21
|
+
- `src/internals.mjs`:可替换的 `deps` 容器及系统级默认实现
|
|
22
|
+
- `src/` 其他模块:任务发现、cron、巡检、会话、登记、展示、日志与 launchd 服务
|
|
23
|
+
- `test/`、`test-support/`:行为测试和可注入依赖的夹具
|
|
24
|
+
|
|
25
|
+
业务模块向 `deps` 注册外部依赖,测试通过替换这些依赖隔离文件、进程、时钟和 launchd。修改行为时同步修改测试,不要只让旧断言适配新输出
|
|
26
|
+
|
|
27
|
+
## 文档同步要求
|
|
28
|
+
|
|
29
|
+
用户可观察行为变化时,至少检查以下位置,避免命令、README 和实现各自描述一套口径:
|
|
30
|
+
|
|
31
|
+
| 变化 | 需要同步的位置 |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| 产品范围、安装、日常使用、限制、卸载或数据保留 | `README.md` |
|
|
34
|
+
| 命令、选项、升级步骤或帮助文案 | `src/cli.mjs`,必要时包括 `src/overview.mjs` |
|
|
35
|
+
| npm 包定位 | `package.json` 的 `description` |
|
|
36
|
+
| 内部机制、生命周期常量、兼容性证据或投递算法 | `docs/internals.md` |
|
|
37
|
+
| 跨版本行为与旧安装收尾 | `docs/migration.md` |
|
|
38
|
+
| 版本号、工作流或发布验收 | `docs/release.md` |
|
|
39
|
+
|
|
40
|
+
维护文档中的常量表只写语义和派生关系,具体数值链接 `src/constants.mjs`,避免同时维护两份当前值;README 中面向用户的数值仍需随行为变化核对
|
|
41
|
+
|
|
42
|
+
文档中的兼容版本必须有实际核对依据。没有把某个版本验证过,就写“未验证”,不要用“以上均支持”替代证据
|
|
43
|
+
|
|
44
|
+
示例输出应与真实命令格式核对;时间、pid 等变化值可以标注为示例,不要伪造验证结果
|
|
45
|
+
|
|
46
|
+
## 提交前检查
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
node --test
|
|
50
|
+
node bin/cron-up.mjs --help
|
|
51
|
+
npm pack --dry-run
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
同时检查:
|
|
55
|
+
|
|
56
|
+
- README 与分拆文档的相对链接有效
|
|
57
|
+
- 新命令或字段有对应测试
|
|
58
|
+
- 文档没有引入“保证一个不少”“绝不可能失败”等超过实现边界的承诺
|
|
59
|
+
- 涉及任务文件写入、权限、联网或卸载时,明确说明影响范围和不会处理的内容
|
|
60
|
+
|
|
61
|
+
发布是独立流程,见[发布流程](docs/release.md)
|
package/README.md
CHANGED
|
@@ -1,191 +1,307 @@
|
|
|
1
1
|
# cron-up
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
为 **Claude Code 的 durable 定时任务**在触发前按需预热交互会话,减少因原会话关闭、电脑上没有在线会话而造成的漏跑。每个需要 cron-up 预热且能唯一识别的任务使用独立后台会话,任务仍由 Claude Code 自己投递和执行
|
|
4
|
+
|
|
5
|
+
目前仅支持 macOS,依赖 launchd、`script(1)` 和 zsh;不是通用命令行 AI agent 的调度器。会话按任务需要创建,满足回收条件后退出,短周期任务也可能持续复用现有会话
|
|
4
6
|
|
|
5
7
|
> [!WARNING]
|
|
6
|
-
> cron-up
|
|
8
|
+
> cron-up 依赖 Claude Code 未公开且无稳定性承诺的内部实现。当前核心行为以 **2.1.302** 的本机核对结果为依据,其他版本不代表已经验证;不同机制的核对版本见[内部机制与兼容性](docs/internals.md)。升级 Claude Code 后,先运行 `cron-up` 查看告警,再确认任务能正常预热和投递
|
|
7
9
|
|
|
8
10
|
## 它解决什么问题
|
|
9
11
|
|
|
10
|
-
在 Claude Code
|
|
12
|
+
在 Claude Code 中用 CronCreate 创建 `durable: true` 的任务后,任务记录在当前工作目录的 `.claude/scheduled_tasks.json` 中。任务到点时,该目录需要有正在运行的交互 `claude` 会话:
|
|
13
|
+
|
|
14
|
+
- 原创建者会话还开着时,任务优先投递给它,cron-up 不为其预热或改写归属
|
|
15
|
+
- 原创建者会话已经关闭时,cron-up 在任务进入预热窗口后尝试启动专属后台会话,并改写该任务的归属字段
|
|
16
|
+
- 电脑休眠期间错过的周期任务不会补执行;一次性任务的补执行见[任务时间与执行](#任务时间与执行)
|
|
17
|
+
|
|
18
|
+
职责边界如下:
|
|
19
|
+
|
|
20
|
+
| 环节 | 负责方 |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| 创建和保存任务 | Claude Code 的 CronCreate 工具 |
|
|
23
|
+
| 判断触发时刻、投递 prompt、执行任务 | Claude Code 交互会话 |
|
|
24
|
+
| 提前准备会话、调整任务归属、回收会话 | cron-up |
|
|
25
|
+
| 定时驱动巡检 | macOS launchd |
|
|
26
|
+
|
|
27
|
+
cron-up 不负责创作任务内容,也不能保证每个任务都执行成功。授权、模型、工具、任务脚本和上游调度仍可能失败
|
|
28
|
+
|
|
29
|
+
## 安装前须知
|
|
30
|
+
|
|
31
|
+
### 环境与兼容性
|
|
32
|
+
|
|
33
|
+
- **macOS**:使用 launchd、`script(1)` 和 zsh,暂不支持其他系统
|
|
34
|
+
- **Node.js 22 或更高版本**:cron-up 没有第三方运行时依赖
|
|
35
|
+
- **Claude Code**:核心行为已在 2.1.302 核对;更低或更高版本均应按未验证版本对待,不要仅凭版本号推断兼容
|
|
36
|
+
- **可用的 `claude` 命令**:已完成登录和所需配置,并且能从交互登录 zsh 中启动;可先用 `zsh -lic 'command -v claude'` 检查
|
|
37
|
+
- **目录信任与工具授权**:先在该工作目录手动启动一次 Claude Code,完成信任确认及无法在无人值守时回答的授权,否则预热可能停在提问界面,随后被判为卡死并进入失败流程
|
|
11
38
|
|
|
12
|
-
|
|
39
|
+
fnm、volta、Homebrew、官网 pkg 等 Node 安装方式均可用于提供 `node`。nvm 等把 Node 放在版本目录中的安装方式,在切换或删除版本后需要重新运行 `cron-up install`;这里说的是 Node 的启动链,不代表所有 Claude Code 安装和 shell 配置都经过验证
|
|
13
40
|
|
|
14
|
-
|
|
41
|
+
### 安全与自动修改
|
|
15
42
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
3. 本进程是任务记录的创建者进程(resume 形态)→ 投;
|
|
19
|
-
4. 其余情况 → 必须持锁、任务属于本项目、且创建者进程已死,才接管。
|
|
43
|
+
> [!CAUTION]
|
|
44
|
+
> `roots` 是**任务扫描与信任范围,不是沙箱**。后台会话通过 `claude --permission-mode bypassPermissions` 跳过工具权限确认,并以当前 macOS 用户身份运行;任务可以访问 roots 之外的位置,也可能通过模型、MCP 或任务工具联网
|
|
20
45
|
|
|
21
|
-
|
|
46
|
+
只扫描你信任其**整个工作区内容**的目录,而不只是检查任务文件。预热会在工作区启动完整的 Claude Code,任务、`CLAUDE.md`、`.claude` 中的项目指令与配置,以及任务可能调用的脚本,都可能影响无人值守会话
|
|
22
47
|
|
|
23
|
-
|
|
24
|
-
|---|---|
|
|
25
|
-
| 任务登记 | Claude Code 原生命令 CronCreate,写入工作目录下 `.claude` |
|
|
26
|
-
| 定时执行 | Claude Code 交互会话自身(包括补执行和抖动) |
|
|
27
|
-
| 保证每个窗口内死主任务有专属会话在场 | cron-up(按任务拉起、盖印、执行完回收) |
|
|
28
|
-
| 安装与升级 | npm:`~/Library/LaunchAgents/local.cron-up.plist` 与 `~/.local/share/cron-up/cron-up-service` |
|
|
48
|
+
目录归你所有并不等于内容可信:clone 到 roots 下的外部仓库、团队共享仓库或他人可写目录,都可能带来由他人编写的任务和项目指令,不能只审核 `.claude/scheduled_tasks.json`
|
|
29
49
|
|
|
30
|
-
|
|
50
|
+
安装后,每轮巡检会按需自动维护扫描到的任务文件:
|
|
31
51
|
|
|
32
|
-
|
|
52
|
+
1. 给所有显式周期任务补 `permanent: true`,使其绕开 Claude Code 的 7 天周期任务回收
|
|
53
|
+
2. 对已经进入预热窗口、原归属会话不在场的任务,改写 `createdBySessionId`、`createdByPid` 和 `createdByProcStart`
|
|
54
|
+
|
|
55
|
+
这些行为没有任务级开关。即使原创建者会话仍在线,自动续期仍会进行;手动删除 `permanent` 后,只要任务仍在扫描范围内,后续巡检还会补回。不希望某个工作区被修改时,应把它移出 roots;已经写入的字段不会自动恢复,详见[卸载与数据保留](#卸载与数据保留)
|
|
56
|
+
|
|
57
|
+
## 快速开始
|
|
58
|
+
|
|
59
|
+
### 1. 安装并选择扫描范围
|
|
33
60
|
|
|
34
61
|
```bash
|
|
35
62
|
npm i -g cron-up
|
|
36
63
|
cron-up install --roots ~/code,~/work
|
|
37
64
|
```
|
|
38
65
|
|
|
39
|
-
|
|
66
|
+
`--roots` 接受逗号分隔的目录列表,每次提供的是完整列表,不是在原列表上追加。第二步会写入配置、启动脚本和 LaunchAgent,并立即尝试首轮巡检
|
|
67
|
+
|
|
68
|
+
不带 `--roots` 时会沿用可读取且 roots 类型有效的现有配置;首次安装使用默认范围 `~/space`、`~/workspace`、`~/tasks`,不存在的目录会跳过。使用默认范围前,同样要确认其中现在及未来出现的任务和项目指令都可信
|
|
69
|
+
|
|
70
|
+
现有配置无法解读时也会回退默认范围;其写入风险和修复方法见[配置无法解读时](#配置无法解读时)
|
|
71
|
+
|
|
72
|
+
扫描范围按下面的路径深度计算:
|
|
73
|
+
|
|
74
|
+
- root 本身,以及 root 下的第 1、2、3 层目录都会检查任务文件
|
|
75
|
+
- 例如 `<root>/<repo>/<subdir>/<nested>` 是 root 下第 3 层,可以发现;再深一层不会发现
|
|
76
|
+
- 不深入符号链接形式的子目录,并跳过依赖目录和常见系统目录,完整名单见 `src/constants.mjs` 中的 `PRUNE_DIRS`
|
|
77
|
+
|
|
78
|
+
工作区指**包含 `.claude/scheduled_tasks.json` 的那个精确目录**,不自动等同于 Git 仓库根目录。若任务是在仓库子目录的 Claude Code 会话里创建,它属于该子目录
|
|
40
79
|
|
|
41
|
-
|
|
80
|
+
### 2. 创建第一个任务
|
|
42
81
|
|
|
43
|
-
|
|
82
|
+
进入 roots 覆盖的工作目录并启动 Claude Code,然后在 Claude Code 中提出请求,例如:
|
|
44
83
|
|
|
45
|
-
|
|
84
|
+
> 在当前工作目录用 CronCreate 创建一个 `durable: true` 的一次性任务,约 5 分钟后在会话里回复“cron-up 验证成功”;完成后执行 `/exit` 退出会话
|
|
46
85
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
-
|
|
86
|
+
CronCreate 是 Claude Code 内部工具,不是在终端里直接执行的 shell 命令。推荐让无人值守任务在完成并保存结果后执行 `/exit`,但是否执行该指令仍取决于任务本身
|
|
87
|
+
|
|
88
|
+
### 3. 验证发现和预热
|
|
89
|
+
|
|
90
|
+
依次用 `cron-up list` 确认任务已被发现并查看时间,用 `cron-up run` 立即巡检一次,再用 `cron-up` 查看服务、会话和告警。需要查看后台界面时,运行 `cron-up logs <目录名或任务id>`;完整命令和日志选项见[日常使用](#日常使用)
|
|
91
|
+
|
|
92
|
+
定时巡检每 5 分钟一轮。周期任务在设定时间前 6 分钟进入预热窗口;所有一次性任务都统一再增加 90 秒前瞻,最早可在设定时间前 7.5 分钟进入窗口。实际启动由窗口内的巡检触发,具体时间取决于巡检相位,并不是固定提前 6 或 7.5 分钟
|
|
93
|
+
|
|
94
|
+
其中,落在 `:00` 或 `:30` 的一次性任务按当前上游规则还可能提前至多 90 秒投递
|
|
95
|
+
|
|
96
|
+
任务还没到预热窗口时,`cron-up run` 不会为它提前常驻一个会话;若已经有一轮巡检在运行,后到的一轮会跳过
|
|
52
97
|
|
|
53
98
|
## 日常使用
|
|
54
99
|
|
|
55
100
|
```bash
|
|
56
|
-
cron-up #
|
|
57
|
-
cron-up list #
|
|
58
|
-
cron-up run #
|
|
59
|
-
cron-up logs #
|
|
60
|
-
cron-up logs team-space #
|
|
101
|
+
cron-up # 总览:服务、配置、最近任务、预热会话与告警
|
|
102
|
+
cron-up list # 逐任务列出时间、类型、摘要与预热状态
|
|
103
|
+
cron-up run # 立即手动巡检一次
|
|
104
|
+
cron-up logs # 查看最近 100 行巡检日志
|
|
105
|
+
cron-up logs team-space # 按文件名片段选择后台会话日志
|
|
61
106
|
cron-up logs team-space--00000002 -f
|
|
62
|
-
cron-up
|
|
107
|
+
cron-up --help # 查看命令帮助
|
|
63
108
|
```
|
|
64
109
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
### 自动维护(每轮无条件进行,没有开关)
|
|
110
|
+
会话日志按文件名片段匹配,可以输入目录名、任务 id 或两者组合;命中多个日志时会列出候选,请补充更长的片段
|
|
68
111
|
|
|
69
|
-
|
|
112
|
+
### 看懂总览和任务列表
|
|
70
113
|
|
|
71
|
-
|
|
72
|
-
- **自动改小任务 id**:把周期任务的随机大 id 改成分文件不冲突的小序号,投递抖动压到 1 分钟以内,观感近乎准点。只改显式周期任务、幂等;一次性任务不碰。
|
|
73
|
-
- **自动任务级盖印**:把窗口内死主任务的三个归属字段盖成各自预热会话的身份。只改 `createdBySessionId/createdByPid/createdByProcStart`,不碰 `prompt`、`cron`、`id`(除上面的改小)与 `createdInProject`。
|
|
114
|
+
总览优先看三处:
|
|
74
115
|
|
|
75
|
-
|
|
116
|
+
- **服务与上次巡检**:launchd 已加载不等于巡检仍在成功运行,心跳可以帮助识别启动链失效
|
|
117
|
+
- **最近**:按预计投递时间排序,而不是只按 cron 的设定时间排序
|
|
118
|
+
- **需要留意**:配置、启动链、会话登记、预热失败和失去扫描范围的会话等告警
|
|
76
119
|
|
|
77
|
-
|
|
120
|
+
下面是 `cron-up list` 的输出格式示例,日期、时间和 pid 以本机实际输出为准:
|
|
78
121
|
|
|
79
|
-
|
|
122
|
+
```text
|
|
123
|
+
共 1 个工作区、2 个定时任务
|
|
80
124
|
|
|
81
|
-
|
|
125
|
+
/Users/you/code/my-project
|
|
126
|
+
10-24 19:52 一次性 52 19 24 10 * 验证 cron-up [预热中 12345]
|
|
127
|
+
10-25 09:00 → 09:30 周期·永久 0 9 * * * 每日汇总
|
|
128
|
+
```
|
|
82
129
|
|
|
83
|
-
|
|
130
|
+
常见状态和标签:
|
|
84
131
|
|
|
85
|
-
|
|
132
|
+
| 显示 | 含义 |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| `设定时间 → 预计时间` | 前者是 cron 设定时间,后者是按已知上游规则估算的投递时间;没有偏差时只显示一个时间 |
|
|
135
|
+
| `启动中 <pid>` | 会话进程已启动,尚未在 Claude Code 会话登记中确认 |
|
|
136
|
+
| `预热中 <pid>` | 已确认后台会话登记且预热进程存活;归属是否已写回可结合巡检日志判断 |
|
|
137
|
+
| `待预热` | 已进入预热窗口,但当前还没有承接会话;结合总览告警判断原因 |
|
|
138
|
+
| `冷却中` / `失败N次` | 预热启动失败,正在退避或已经累计失败 |
|
|
139
|
+
| `终端接管` | 用户通过 `--resume` 接管了该会话,cron-up 不再另拉或结束它 |
|
|
140
|
+
| 标签为空 | 可能还没到预热窗口,也可能原创建者会话仍在线,需要结合时间列和总览判断 |
|
|
141
|
+
| `已错过` | 一次性任务的设定时间已经过去;有效 `createdAt` 是 cron-up 判断错过待补的必要字段 |
|
|
142
|
+
| `一年内无` | cron 可解析,但 366 天展示窗口内没有未来落点;二月三十日这类日历上永远不出现的表达式也归入此状态 |
|
|
143
|
+
| `cron 无效` | 表达式解析失败,或字段值域、区间导致合法值集合为空,巡检会跳过该任务 |
|
|
86
144
|
|
|
87
|
-
|
|
145
|
+
## 任务时间与执行
|
|
88
146
|
|
|
89
|
-
|
|
147
|
+
文档和命令输出中的时间均为 macOS 本机时间,cron-up 也用本地时区计算预热和展示。三个时间概念不要混用:
|
|
90
148
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
- **孤儿认领**:state 记录丢失时,巡检能按会话日志路径从进程表里把仍在运行的预热重新认回来。
|
|
95
|
-
- **升级迁移**:从目录级单预热的旧版本升级时,首轮巡检会结束旧版预热,之后按任务重新拉起。
|
|
149
|
+
1. **设定时间**:cron 表达式对应的分钟
|
|
150
|
+
2. **预计投递时间**:计入已知上游抖动规则后的估算,可能早于或晚于设定时间
|
|
151
|
+
3. **实际执行时间**:Claude Code 会话实际收到并开始处理任务的时间,还受秒级轮询、会话状态和上游实现影响
|
|
96
152
|
|
|
97
|
-
|
|
153
|
+
周期任务通常在设定时间之后投递,每日任务使用原生随机 id 时经常接近固定的 30 分钟上限;一次性任务落在 `:00` 或 `:30` 时可能提前至多 90 秒。约 5 分钟周期且匹配特定表达式的上游任务还可能走提前 15 秒的分支,当前预测没有复刻该分支,完整规则和例外见[投递时间算法](docs/internals.md#投递时间算法)
|
|
98
154
|
|
|
99
|
-
|
|
155
|
+
因此,“设定时间到了但还没执行”不一定是故障,应先看 `cron-up list` 的预计时间和总览告警
|
|
100
156
|
|
|
101
|
-
|
|
102
|
-
claude --resume <sessionId> # 或 claude -r 在列表中选择
|
|
103
|
-
```
|
|
157
|
+
电脑休眠期间,launchd 醒来后只补跑一轮巡检:
|
|
104
158
|
|
|
105
|
-
|
|
159
|
+
- 错过的周期任务不会补执行
|
|
160
|
+
- 错过的一次性任务通常会在后台会话启动时由 Claude Code 补执行;手写任务缺少有效 `createdAt` 时,cron-up 无法据此识别错过待补
|
|
106
161
|
|
|
107
|
-
##
|
|
162
|
+
## 会话生命周期与恢复
|
|
108
163
|
|
|
109
|
-
|
|
164
|
+
预热会话按任务需要创建,周期任务不依靠跨周期累积上下文。复用与回收只在巡检轮次中按启发式条件判断,不等于对任务完成状态做强保证:
|
|
110
165
|
|
|
111
|
-
|
|
166
|
+
- 周期任务的预热窗口从设定时间前 6 分钟开始,一次性任务最早从设定时间前 7.5 分钟开始;具体启动时间取决于窗口内的巡检相位
|
|
167
|
+
- 新会话启动后,当轮每 3 秒重扫一次登记,最多等待 24 秒;这只是加速归属写回,不是启动判死时限
|
|
168
|
+
- 进程超过 3 分钟仍未完成登记时,由后续巡检判定卡死并终止;因为巡检每 5 分钟一轮,实际处置时间不是精确的启动后第 3 分钟
|
|
169
|
+
- 没有成功登记的启动连续失败 3 次后,该任务暂停重试 30 分钟;按任务分别计数,成功登记会清零,已死亡条目的失败记录跨预热窗口保留并在 48 小时后老化
|
|
170
|
+
- 回收除了要求界面静默超过 3 分钟,还要求距该任务下次需要超过 7 分钟;同样只在巡检时判断
|
|
171
|
+
- 距下次需要不超过 7 分钟时会继续复用现有会话,例如每 5 分钟执行的任务通常无法满足回收余量,其会话可能长期在线,不能按“窗口外没有会话”估算内存
|
|
172
|
+
- 同分钟大量任务会同时拉起多个 Claude Code 会话,每个会话可能占用数百 MB,实际以本机为准;正常回收后不再保留这些预热会话的内存开销
|
|
112
173
|
|
|
113
|
-
|
|
174
|
+
后台会话与普通会话一样保存对话记录,可以在终端恢复:
|
|
114
175
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
- **会话日志可能包含任务输出**:后台会话的界面记录写到 `~/Library/Logs/cron-up/sessions/<工作区>--<任务id>.log`,权限 600、目录 700,每个任务独立文件、轮转保留一代;巡检日志在 `~/Library/Logs/cron-up/`。
|
|
119
|
-
- **扫描不深入符号链接的子目录**,默认还排除系统目录与依赖目录。
|
|
120
|
-
- **巡检、预热与你的 claude 会话之间不发起网络请求**,也不依赖 npm 注册表;网络活动全部来自被启动的会话本身。唯一的网络发生在安装/升级时访问 npm registry 拉包。
|
|
176
|
+
```bash
|
|
177
|
+
claude --resume <sessionId> # 或 claude -r 在列表中选择
|
|
178
|
+
```
|
|
121
179
|
|
|
122
|
-
|
|
180
|
+
对话记录位于 `~/.claude/projects/<目录编码>/<会话ID>.jsonl`。如果恢复后原后台进程已经退出、同一 sessionId 仍在终端运行,cron-up 会停止管理它,不结束终端进程;该终端会话关闭后,后续巡检可重新预热
|
|
123
181
|
|
|
124
|
-
|
|
125
|
-
- 每个窗口内的死主任务对应一个后台会话:同一分钟内有大量任务同时到期时,会同时存在相应数量的会话(每个数百 MB 内存)。时间错开的日常任务只在各自的 6 分钟窗口内占用(一次性任务至多 7.5 分钟)。
|
|
126
|
-
- 手工创建的任务若缺少 `createdAt`,错过补执行判定不生效。`cron-up list` 对缺字段的手写任务仍显示「已错过/设定时间」,但预热与补执行依赖 Claude Code 与 cron-up 的字段纪律。
|
|
127
|
-
- 工作区从巡检视野里消失(`.claude/scheduled_tasks.json` 被整个删除或移出 roots)后,仍在运行的预热不会被回收,总览会列出这类会话,不需要时手动结束。
|
|
128
|
-
- 预热依据的逐任务谓词、调度锁与三代进程结构是未公开内部实现:新版若调整归属/锁语义,任务级隔离的前提需要重新核对。cron-up 保留了会话登记格式的烟雾告警,格式漂移时在总览页明示。
|
|
182
|
+
## 自动维护的写入边界
|
|
129
183
|
|
|
130
|
-
|
|
184
|
+
同一轮中,同一批待处理的自动续期和归属改写会合并为带 mtime 乐观检测的原子写入;新会话在后续完成登记、需要补写归属时,可能另做一次原子写入。若检测到 Claude Code 同时更新了 `lastFiredAt`、删除了一次性任务或进行了其他修改,本轮对应写入会暂缓并在后续重试
|
|
131
185
|
|
|
132
|
-
|
|
133
|
-
|---|---|
|
|
134
|
-
| 总览提示「冷却中」 | 运行 `cron-up logs <目录>--<任务id>` 查看启动界面:停在信任确认就先在该目录手动走完信任;shell 初始化缺命令(command not found)按「启动脚本 PATH」段修 |
|
|
135
|
-
| 任务到点没有执行 | `cron-up list` 看任务与预热状态;预热在则看它的会话日志;总览的「需要留意」按任务给出原因 |
|
|
136
|
-
| 总览提示「启动脚本已失效 / PATH 找不到 node」 | 重跑 `cron-up install`(见下) |
|
|
137
|
-
| 总览提示「巡检已沉默」 | launchd 显示加载但心跳超时:先 `cron-up logs`,再重跑 install |
|
|
138
|
-
| `last exit code=2` | 配置损坏或字段类型不对:修好 `~/.local/share/cron-up/config.json`(只有 roots 一个字段)或重跑 install |
|
|
139
|
-
| 手动 `cron-up run` 正常、定时执行不正常 | 手动测试继承终端 PATH,定时由启动脚本驱动;换过 Node 安装方式后重跑 install |
|
|
140
|
-
| 任务表显示「已错过」 | 一次性任务落点已过:后台会话一启动就会补执行;周期任务没有补执行 |
|
|
141
|
-
| 日志里出现「卡死」 | 会话 3 分钟没完成登记:信任确认/工具授权提问没人应答,手动进目录走一遍授权 |
|
|
142
|
-
| 同目录多个任务互相干扰 | 升级后首轮会结束旧版目录级预热;如仍异常,确认所有预热会话都由新版本拉起(日志名含 `--任务id`) |
|
|
186
|
+
这只是降低并发覆盖概率,不是与 Claude Code 共用的互斥锁。最后一次检查与原子替换之间仍存在极小竞态,详见[内部机制](docs/internals.md#任务文件维护与并发)
|
|
143
187
|
|
|
144
|
-
|
|
188
|
+
卸载 cron-up 不会撤销这些已经写入任务文件的修改
|
|
145
189
|
|
|
146
|
-
|
|
147
|
-
- **升级**:`npm i -g cron-up@latest` 后重跑一次 `cron-up install`。下一轮巡检自然跑新版;Node 版本与包文件在 install 后自动跟随。fnm 切换默认版本同理。
|
|
148
|
-
- **版本号**:总览首行、心跳文件都带版本;发版收尾跑一次无参数 `cron-up` 确认首行版本、服务已加载且退出码 0、「需要留意」为空。
|
|
190
|
+
## 配置与产物
|
|
149
191
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
配置只有扫描范围一个字段:`~/.local/share/cron-up/config.json`
|
|
192
|
+
配置文件为 `~/.local/share/cron-up/config.json`,只有扫描范围一个字段:
|
|
153
193
|
|
|
154
194
|
```json
|
|
155
195
|
{ "roots": ["/Users/you/code", "/Users/you/work"] }
|
|
156
196
|
```
|
|
157
197
|
|
|
158
|
-
|
|
198
|
+
可以直接编辑 roots,通常最多等下一轮巡检生效;也可以运行:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
cron-up install --roots "$HOME/code,$HOME/work"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
重跑 install 会重写配置、启动脚本与 LaunchAgent,并移除旧版本遗留的废弃配置字段
|
|
205
|
+
|
|
206
|
+
巡检间隔、提前量、扫描深度、失败退避等均为固定行为,完整常量和生命周期规则见[内部机制](docs/internals.md#巡检与会话生命周期)
|
|
159
207
|
|
|
160
208
|
| 产物 | 位置 |
|
|
161
|
-
|
|
162
|
-
| 命令行与代码 | npm
|
|
209
|
+
| --- | --- |
|
|
210
|
+
| 命令行与代码 | npm 全局目录,具体位置取决于 Node 与 npm 安装方式 |
|
|
163
211
|
| 配置文件 | `~/.local/share/cron-up/config.json` |
|
|
164
|
-
| 运行状态 |
|
|
165
|
-
|
|
|
212
|
+
| 运行状态 | `~/.local/share/cron-up/state.json`,按工作区与任务 id 记录预热 |
|
|
213
|
+
| 巡检心跳 | `~/.local/share/cron-up/heartbeat`,每轮成功完成后写入 |
|
|
166
214
|
| LaunchAgent | `~/Library/LaunchAgents/local.cron-up.plist` |
|
|
167
215
|
| 启动脚本 | `~/.local/share/cron-up/cron-up-service` |
|
|
168
216
|
| 巡检日志 | `~/Library/Logs/cron-up/launchd.{out,err}.log` |
|
|
169
|
-
| 各任务会话日志 | `~/Library/Logs/cron-up/sessions
|
|
217
|
+
| 各任务会话日志 | `~/Library/Logs/cron-up/sessions/<工作区折名>--<任务id>.log`,轮转保留一代 `.1` |
|
|
218
|
+
| Claude Code 对话记录 | `~/.claude/projects/<目录编码>/<会话ID>.jsonl`,由 Claude Code 管理 |
|
|
219
|
+
|
|
220
|
+
会话日志可能包含任务输出,文件权限为 0600、目录权限为 0700。日志文件名中的工作区折名由完整工作区路径生成,日常使用建议通过 `cron-up logs` 查找,不要手工猜文件名
|
|
221
|
+
|
|
222
|
+
cron-up 自身的巡检和会话管理不主动访问 npm registry 或其他网络服务;安装和升级时由 npm 访问 registry。被启动的 Claude Code 会话仍可能联网,cron-up 不对其网络活动作限制
|
|
170
223
|
|
|
171
|
-
|
|
224
|
+
### 配置无法解读时
|
|
225
|
+
|
|
226
|
+
不带 roots 重跑 install 时,只有可读取且 roots 类型有效的现有配置才会被沿用;配置无法解读时会提示并回退到默认范围 `~/space`、`~/workspace`、`~/tasks`,随后立即尝试首轮巡检,可能续期或改写其中的任务文件
|
|
227
|
+
|
|
228
|
+
修复时可以直接改正上述配置文件;需要通过 install 重建且不想使用默认范围时,必须显式传入完整的 `--roots`
|
|
229
|
+
|
|
230
|
+
## 升级与启动链维护
|
|
231
|
+
|
|
232
|
+
升级 cron-up:
|
|
172
233
|
|
|
173
234
|
```bash
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
235
|
+
npm i -g cron-up@latest --prefer-online
|
|
236
|
+
cron-up install
|
|
237
|
+
cron-up
|
|
177
238
|
```
|
|
178
239
|
|
|
179
|
-
|
|
240
|
+
最后一步确认首行版本、launchd 状态、上次退出码和“需要留意”。刚发布新版本时 registry 与本机缓存可能有延迟,维护者发版后的逐机升级使用显式版本号,见[发布后的逐机升级与验收](docs/release.md#每次发版后的逐机升级与验收)
|
|
241
|
+
|
|
242
|
+
launchd 的初始 PATH 很小,因此 install 会把 `node` 与 `cron-up` 所在 bin 目录写入 `~/.local/share/cron-up/cron-up-service`。预热中的 `claude` 则由 `/bin/zsh -lic` 启动,依赖登录交互 shell 的配置,而不是仅靠前述两个目录
|
|
243
|
+
|
|
244
|
+
出现以下情况后应重跑 `cron-up install`:
|
|
245
|
+
|
|
246
|
+
- 切换、删除或更换 Node 安装方式
|
|
247
|
+
- 修改 roots 或需要重建 LaunchAgent
|
|
248
|
+
- 总览提示启动脚本失效、PATH 找不到 node/cron-up,或巡检心跳沉默
|
|
249
|
+
|
|
250
|
+
若 `zsh -lic 'command -v claude'` 失败,先修复交互 zsh 中的 `claude` 命令及其环境,再重跑 install。手动 `cron-up run` 成功而 launchd 失败时,也应优先比较这两种环境的差异
|
|
251
|
+
|
|
252
|
+
## 卸载与数据保留
|
|
253
|
+
|
|
254
|
+
`cron-up uninstall` 管理的是本机服务和 cron-up 自有产物,不等于删除所有相关数据:
|
|
255
|
+
|
|
256
|
+
| 操作或数据 | `cron-up uninstall` | `cron-up uninstall --purge` |
|
|
257
|
+
| --- | --- | --- |
|
|
258
|
+
| 停止仍可安全跟踪的预热会话 | 是 | 是 |
|
|
259
|
+
| 移除 LaunchAgent 和 plist | 是 | 是 |
|
|
260
|
+
| 删除 `~/.local/share/cron-up` 中的配置、状态、心跳和启动脚本 | 否 | 是 |
|
|
261
|
+
| 删除 `~/Library/Logs/cron-up` 中的巡检与会话日志 | 否 | 是 |
|
|
262
|
+
| 卸载 npm 全局 `cron-up` 命令 | 否 | 否 |
|
|
263
|
+
| 删除 Claude Code 任务或 `~/.claude/projects` 对话记录 | 否 | 否 |
|
|
264
|
+
| 恢复任务文件中的 `permanent`、`createdBy*` 字段 | 否 | 否 |
|
|
265
|
+
|
|
266
|
+
完全移除命令行和 cron-up 自有产物:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
cron-up uninstall --purge
|
|
270
|
+
npm uninstall -g cron-up
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Claude Code 任务和对话记录需要在 Claude Code 侧另行处理。卸载后任务文件仍然存在,但 cron-up 不再提供预热、续期或会话回收;不要把这个状态理解为任务字段已经恢复原状
|
|
274
|
+
|
|
275
|
+
## 故障排查
|
|
276
|
+
|
|
277
|
+
| 现象 | 排查方法 |
|
|
278
|
+
| --- | --- |
|
|
279
|
+
| 总览提示“冷却中” | 运行 `cron-up logs <目录名>--<任务id>` 查看启动界面;常见原因是目录信任、工具授权、登录状态或 shell 环境,冷却结束不代表问题已经修复 |
|
|
280
|
+
| 设定时间到了但没有执行 | 先看 `cron-up list` 的预计投递时间,排除上游抖动;再看总览告警和对应会话日志,并确认当时电脑没有休眠 |
|
|
281
|
+
| 总览提示“启动脚本已失效 / PATH 找不到 node” | 按[升级与启动链维护](#升级与启动链维护)检查并重跑 `cron-up install` |
|
|
282
|
+
| 总览提示“巡检已沉默” | 先运行 `cron-up logs` 查看巡检和 stderr 提示,再检查启动链并重跑 install |
|
|
283
|
+
| `last exit code=2` | 通常是配置损坏或 roots 类型不对;直接修复 `~/.local/share/cron-up/config.json`,或按[配置无法解读时](#配置无法解读时)的说明显式传入完整 roots 重跑 install |
|
|
284
|
+
| 手动 `cron-up run` 正常、定时执行不正常 | 比较终端与 launchd/`zsh -lic` 的 PATH、Node 和 `claude` 环境;换过 Node 安装方式后重跑 install |
|
|
285
|
+
| 任务表显示“已错过” | 对一次性任务检查后台会话是否启动及其日志;手写任务缺少有效 `createdAt` 时,cron-up 不会按错过待补处理 |
|
|
286
|
+
| 日志里出现“卡死” | 表示超过登记宽限后由巡检终止,常见于人工作答界面,也可能是会话登记或启动形态变化;先看会话日志和总览的登记格式告警 |
|
|
287
|
+
| 从旧版升级后仍有目录级预热 | 查看[旧版迁移](docs/migration.md),核实是否因缺少进程指纹而需要人工处理 |
|
|
288
|
+
|
|
289
|
+
## 已知限制
|
|
290
|
+
|
|
291
|
+
- 仅支持 macOS,且依赖未公开的 Claude Code 内部实现;上游升级可能改变任务归属、登记格式、进程结构和投递算法
|
|
292
|
+
- 电脑休眠期间错过的周期任务不会补执行;一次性任务的补执行依赖上游机制和必要任务字段
|
|
293
|
+
- 每个进入窗口且原归属不在场的任务对应一个会话,同分钟大量任务可能带来明显内存压力
|
|
294
|
+
- 手写或第三方生成的任务文件不是推荐入口;任务应有唯一 id、显式 `recurring` 和符合上游语义的字段,一次性任务还需要毫秒时间戳 `createdAt` 才能由 cron-up 判断错过待补
|
|
295
|
+
- 工作区的任务文件被整体删除或目录移出 roots 后,仍在运行的预热可能不再被回收;总览会列出能识别的此类会话,需要人工处理
|
|
296
|
+
- mtime 乐观检测不能完全关闭跨进程写入竞态;需要绝对写入保证时,应由上游提供共享锁或公开写接口
|
|
297
|
+
- 即使预热和投递正常,任务仍可能因模型、工具、权限、网络或 prompt 本身而失败
|
|
180
298
|
|
|
181
|
-
##
|
|
299
|
+
## 更多文档
|
|
182
300
|
|
|
183
|
-
-
|
|
184
|
-
-
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
- **npm 传播延迟**:publish 成功约两分钟才完成处理,本机缓存各自滞后,装到旧版且毫无提示——等两分钟后用显式版本号安装,装完以总览首行版本号为准。
|
|
188
|
-
- **trusted publishing**:npm 包设置页里已登记 GitHub 仓库(repository `adaex/cron-up`,workflow `release.yml`),一次性配置,没了见 Trusted Publisher 文档。
|
|
301
|
+
- [内部机制与兼容性](docs/internals.md):任务归属、生命周期常量、投递算法、并发与版本证据
|
|
302
|
+
- [旧版迁移](docs/migration.md):从 26.10.30 及更早版本升级
|
|
303
|
+
- [贡献指南](CONTRIBUTING.md):开发、测试和文档同步要求
|
|
304
|
+
- [发布流程](docs/release.md):版本号、签名 tag、trusted publishing 与逐机升级
|
|
189
305
|
|
|
190
306
|
## License
|
|
191
307
|
|