cron-up 26.9.3 → 26.9.4
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 +4 -4
- package/package.json +1 -1
- package/src/paths.mjs +9 -4
- package/src/patrol.mjs +2 -2
- package/src/service.mjs +29 -2
package/README.md
CHANGED
|
@@ -68,8 +68,8 @@ cron-up install --force --no-auto-renew
|
|
|
68
68
|
|---|---|
|
|
69
69
|
| 命令行与代码 | npm 全局目录(位置因安装方式而异,如 fnm 是 `~/.local/share/fnm/aliases/default/lib/node_modules/cron-up/`,命令在同级 `bin/cron-up`) |
|
|
70
70
|
| launchd 配置 | `~/Library/LaunchAgents/local.cron-up.plist` |
|
|
71
|
-
| 启动脚本 |
|
|
72
|
-
| 配置文件 |
|
|
71
|
+
| 启动脚本 | `~/.local/share/cron-up/cron-up-service`(launchd 拉起的入口,内含 node 与包入口的绝对路径;后台列表显示名即来自它) |
|
|
72
|
+
| 配置文件 | `~/.local/share/cron-up/config.json` |
|
|
73
73
|
| 运行状态 | 同目录下的 `state.json` |
|
|
74
74
|
| 日志 | `~/Library/Logs/cron-up/`(巡检日志;各后台会话的界面记录在 `sessions/` 子目录下,该目录权限 700、日志文件 600) |
|
|
75
75
|
|
|
@@ -127,7 +127,7 @@ cron-up spawn 的预热会话进程环境里带 `CRON_UP_SESSION=1`(值严格
|
|
|
127
127
|
- **修改扫描范围**:直接编辑配置文件的 `roots`、`maxDepth`,最多 5 分钟后的下一轮巡检自动生效,无需重新加载服务。
|
|
128
128
|
- **开关自动续期**:编辑配置文件的 `autoRenew`(`true`/`false`)同样在下一轮巡检生效;或运行 `cron-up install --force --auto-renew`(`--no-auto-renew` 关闭)。想立即处理一次而不改长期开关,用 `cron-up renew`。
|
|
129
129
|
- **修改巡检间隔或提前量**:重新运行 `cron-up install --force --interval 600`,会重新生成 launchd 配置。`--force` 只更新命令行里显式给出的字段,其余配置(如 `roots`)保留;路径参数建议都加引号书写,例如 `--roots "$HOME/code,$HOME/work"`,避免 shell 对逗号后的波浪号不展开。
|
|
130
|
-
- **升级**:`npm i -g cron-up@latest`,下一轮巡检自然跑新版。**例外**:fnm/nvm 下切换默认 Node 版本后,包入口仍指向旧版本目录(见「安装」),巡检会继续跑旧版包,总览页会提示,重跑一次 `cron-up install` 即可。从 26.9.
|
|
130
|
+
- **升级**:`npm i -g cron-up@latest`,下一轮巡检自然跑新版。**例外**:fnm/nvm 下切换默认 Node 版本后,包入口仍指向旧版本目录(见「安装」),巡检会继续跑旧版包,总览页会提示,重跑一次 `cron-up install` 即可。从 26.9.3 及更早升级后建议重跑一次 `cron-up install --force`,一次完成两件事:把 LaunchAgent 切到启动脚本形态(后台列表显示名由 Node.js Foundation 变为 cron-up-service)、把数据目录从 `~/Library/Application Support/cron-up` 自动迁移到 `~/.local/share/cron-up`(配置与状态随行,旧目录搬走);不重跑的话旧形态照常巡检。版本号按「年.月.序号」:两位年份、月份、月内发布序号(序号每月重置),如 2026 年 9 月第一次发布是 `26.9.1`,同月再发是 `26.9.2`,到 10 月是 `26.10.1`。发布方式:先把 package.json 的 version 改成新版本并合入 main,再打同名 `v<version>` tag(如 `v26.9.1`);GitHub Actions 会跑通测试(Node 22/24)、校验 package.json 版本与 tag 一致后 publish,版本不符直接失败,杜绝「代码是 26.9.2、tag 叫 26.9.3」这类漂移。push 到 main 的每次提交也会先跑一遍测试(ci workflow)。
|
|
131
131
|
- **配置校验**:手改配置时字段类型写错(`roots` 不是数组、数值字段写成字符串或布尔、`autoRenew` 不是布尔)不会静默回退默认值——巡检会以退出码 2 响亮失败并在总览页列出具体字段,修好或重跑 `cron-up install --force`(坏字段回默认,其余保留)。软问题只警告不中止:提前量小于间隔(可能来不及在任务前启动会话)、扫描目录不存在。无参数的 `cron-up` 总览随时也会显示这些问题;其中 interval 显示的是 launchd **实际生效**的间隔,手动改过 config 但与实际不一致时会提示需要重新 install。
|
|
132
132
|
- **路径大小写**:macOS 默认文件系统大小写不敏感,而 `realpath` 并不规范化大小写。扫描入口会把 `roots` 认回磁盘上的真实写法,`cron-up logs` 给绝对路径时也一样——大小写敲错不再是「警告一声然后匹配失效」,而是直接按同一路径工作。
|
|
133
133
|
- **提前量与间隔的关系**:请保持 `leadSeconds ≥ intervalSeconds`,否则极端情况下任务会在两轮巡检之间到期而来不及启动。默认 600 ≥ 300 有一倍余量。`--lead 0` 是合法取值(表示不提前,只靠错过补执行兜底),会如实写入配置;`--interval 0` 非法,安装时直接报错中止。
|
|
@@ -164,7 +164,7 @@ cron-up spawn 的预热会话进程环境里带 `CRON_UP_SESSION=1`(值严格
|
|
|
164
164
|
|---|---|
|
|
165
165
|
| 总览提示「冷却中」 | 运行 `cron-up logs <目录名>` 查看启动界面:要么停在信任确认或工具授权提问(日志里能看到提问界面),要么 shell 环境缺少命令(日志里是 `command not found`) |
|
|
166
166
|
| 总览提示「Node 或入口路径已失效」 | Node 版本目录被删或移动后启动脚本里的绝对路径失效,重跑 `cron-up install` 重新解析即可 |
|
|
167
|
-
| 总览提示「启动脚本已失效 / 内容无法解读」 |
|
|
167
|
+
| 总览提示「启动脚本已失效 / 内容无法解读」 | 启动脚本(`~/.local/share/cron-up/cron-up-service`)被删或手改,重跑 `cron-up install` 覆盖重生成 |
|
|
168
168
|
| 总览提示「入口路径属于另一个 node 版本目录」 | fnm/nvm 切换了默认 Node 版本,巡检在跑旧版本目录里的旧包;重跑 `cron-up install` 更新路径 |
|
|
169
169
|
| 日志里出现「卡死」 | 会话进程起来了但没能变成可用会话,绝大多数是停在信任确认或授权提问。先手动 `cd <目录> && claude` 走完确认,再等下一轮巡检 |
|
|
170
170
|
| 日志里出现「任务文件读不出 \<目录\>」 | 该目录的 `.claude/scheduled_tasks.json` 存在却解析不了(格式损坏),巡检跳过这一个目录,**其中的定时任务一个都不会执行**;其余目录照常。检查该文件 |
|
package/package.json
CHANGED
package/src/paths.mjs
CHANGED
|
@@ -16,7 +16,12 @@ export const TASK_REL = path.join('.claude', 'scheduled_tasks.json');
|
|
|
16
16
|
// HOME。getter 让 sessionLogDir/configPath/statePath 跟随替换后的前缀。
|
|
17
17
|
export const paths = {
|
|
18
18
|
home: HOME,
|
|
19
|
-
|
|
19
|
+
// 数据目录用 XDG 风格的 ~/.local/share/cron-up:config、state 与启动脚本
|
|
20
|
+
// 集中一处,purge 一删全清。26.9.3 及更早放在
|
|
21
|
+
// ~/Library/Application Support/cron-up,install 时自动搬家(见
|
|
22
|
+
// service.mjs 的 migrateLegacyDir)。
|
|
23
|
+
dataDir: path.join(HOME, '.local', 'share', 'cron-up'),
|
|
24
|
+
legacyAppSupport: path.join(HOME, 'Library', 'Application Support', 'cron-up'),
|
|
20
25
|
logDir: path.join(HOME, 'Library', 'Logs', 'cron-up'),
|
|
21
26
|
plistPath: path.join(HOME, 'Library', 'LaunchAgents', `${LABEL}.plist`),
|
|
22
27
|
sessionDir: path.join(HOME, '.claude', 'sessions'),
|
|
@@ -25,16 +30,16 @@ export const paths = {
|
|
|
25
30
|
return path.join(this.logDir, 'sessions');
|
|
26
31
|
},
|
|
27
32
|
get configPath() {
|
|
28
|
-
return path.join(this.
|
|
33
|
+
return path.join(this.dataDir, 'config.json');
|
|
29
34
|
},
|
|
30
35
|
get statePath() {
|
|
31
|
-
return path.join(this.
|
|
36
|
+
return path.join(this.dataDir, 'state.json');
|
|
32
37
|
},
|
|
33
38
|
// launchd 拉起的启动脚本。ProgramArguments[0] 直接指向它而非 node:
|
|
34
39
|
// macOS 后台项目列表按可执行文件名显示条目,直连 node 会归到「Node.js
|
|
35
40
|
// Foundation」名下(node 的签名者),自有脚本则显示脚本自己的名字。
|
|
36
41
|
get serviceScriptPath() {
|
|
37
|
-
return path.join(this.
|
|
42
|
+
return path.join(this.dataDir, 'cron-up-service');
|
|
38
43
|
},
|
|
39
44
|
};
|
|
40
45
|
|
package/src/patrol.mjs
CHANGED
|
@@ -58,8 +58,8 @@ function readLockPid(lockPath) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export function acquireRunLock() {
|
|
61
|
-
fs.mkdirSync(deps.paths.
|
|
62
|
-
const lockPath = path.join(deps.paths.
|
|
61
|
+
fs.mkdirSync(deps.paths.dataDir, { recursive: true });
|
|
62
|
+
const lockPath = path.join(deps.paths.dataDir, 'run.lock');
|
|
63
63
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
64
64
|
const fd = tryCreateLock(lockPath);
|
|
65
65
|
if (fd !== null) {
|
package/src/service.mjs
CHANGED
|
@@ -356,8 +356,34 @@ export function launcherHealth() {
|
|
|
356
356
|
|
|
357
357
|
// ---- install / uninstall ----
|
|
358
358
|
|
|
359
|
+
// 26.9.3 及更早把数据放在 ~/Library/Application Support/cron-up;新版统一
|
|
360
|
+
// 到 ~/.local/share/cron-up。整体搬家(config、state 随行),随后 install
|
|
361
|
+
// 照常重写脚本与 plist 并重启服务,用户无感。搬家只在 install 里发生:
|
|
362
|
+
// 只升级 npm 包而不跑 install 的话,旧目录与旧 plist 依旧配套、照常巡检。
|
|
363
|
+
function migrateLegacyDir() {
|
|
364
|
+
const legacy = deps.paths.legacyAppSupport;
|
|
365
|
+
if (!fs.existsSync(legacy) || fs.existsSync(deps.paths.dataDir)) return;
|
|
366
|
+
try {
|
|
367
|
+
fs.mkdirSync(path.dirname(deps.paths.dataDir), { recursive: true });
|
|
368
|
+
try {
|
|
369
|
+
fs.renameSync(legacy, deps.paths.dataDir);
|
|
370
|
+
} catch {
|
|
371
|
+
// rename 搬不动(目标异常等):退化为复制,旧目录留着无害。
|
|
372
|
+
fs.cpSync(legacy, deps.paths.dataDir, { recursive: true });
|
|
373
|
+
}
|
|
374
|
+
deps.print(`已迁移数据目录:${legacy} → ${deps.paths.dataDir}`);
|
|
375
|
+
} catch {
|
|
376
|
+
// 迁移彻底失败必须当面说:不说的话 install 会按「全新安装」落默认
|
|
377
|
+
// 配置,用户收窄过的 roots 等手改就静默丢了。
|
|
378
|
+
deps.print(`提示:旧数据目录迁移失败(${legacy}),本次按新目录继续;`
|
|
379
|
+
+ '如旧目录里还有你改过的配置,请手动复制到 '
|
|
380
|
+
+ `${deps.paths.dataDir} 后重跑 cron-up install`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
359
384
|
export async function cmdInstall(args) {
|
|
360
|
-
|
|
385
|
+
migrateLegacyDir();
|
|
386
|
+
fs.mkdirSync(deps.paths.dataDir, { recursive: true });
|
|
361
387
|
fs.mkdirSync(deps.paths.sessionLogDir, { recursive: true });
|
|
362
388
|
try {
|
|
363
389
|
fs.chmodSync(deps.paths.sessionLogDir, 0o700);
|
|
@@ -497,7 +523,8 @@ export async function cmdUninstall(args) {
|
|
|
497
523
|
}
|
|
498
524
|
|
|
499
525
|
if (args.purge) {
|
|
500
|
-
for (const p of [deps.paths.
|
|
526
|
+
for (const p of [deps.paths.dataDir, deps.paths.legacyAppSupport,
|
|
527
|
+
deps.paths.logDir]) {
|
|
501
528
|
fs.rmSync(p, { recursive: true, force: true });
|
|
502
529
|
deps.print(`已删除 ${p}`);
|
|
503
530
|
}
|