@cjvana/claude-auto 0.1.0
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/.claude-plugin/plugin.json +10 -0
- package/LICENSE +21 -0
- package/README.md +435 -0
- package/dist/check-repo-6C4QI2M2.js +33 -0
- package/dist/check-repo-6C4QI2M2.js.map +1 -0
- package/dist/check-repo-SXWFIVO5.js +8 -0
- package/dist/check-repo-SXWFIVO5.js.map +1 -0
- package/dist/chunk-24PS2XSV.js +203 -0
- package/dist/chunk-24PS2XSV.js.map +1 -0
- package/dist/chunk-2D5E23XA.js +129 -0
- package/dist/chunk-2D5E23XA.js.map +1 -0
- package/dist/chunk-3NEANSUS.js +26 -0
- package/dist/chunk-3NEANSUS.js.map +1 -0
- package/dist/chunk-4I5UIASZ.js +71 -0
- package/dist/chunk-4I5UIASZ.js.map +1 -0
- package/dist/chunk-5LGOK52J.js +38 -0
- package/dist/chunk-5LGOK52J.js.map +1 -0
- package/dist/chunk-6RYMWH5M.js +35 -0
- package/dist/chunk-6RYMWH5M.js.map +1 -0
- package/dist/chunk-A6XWZPLY.js +56 -0
- package/dist/chunk-A6XWZPLY.js.map +1 -0
- package/dist/chunk-AWLSYOVF.js +61 -0
- package/dist/chunk-AWLSYOVF.js.map +1 -0
- package/dist/chunk-BY5YEOVG.js +75 -0
- package/dist/chunk-BY5YEOVG.js.map +1 -0
- package/dist/chunk-D4MBOIYQ.js +46 -0
- package/dist/chunk-D4MBOIYQ.js.map +1 -0
- package/dist/chunk-DVZC42TL.js +33 -0
- package/dist/chunk-DVZC42TL.js.map +1 -0
- package/dist/chunk-E3XVLTT4.js +13 -0
- package/dist/chunk-E3XVLTT4.js.map +1 -0
- package/dist/chunk-GLW7T4QE.js +116 -0
- package/dist/chunk-GLW7T4QE.js.map +1 -0
- package/dist/chunk-H2MUDYMW.js +23 -0
- package/dist/chunk-H2MUDYMW.js.map +1 -0
- package/dist/chunk-HF7PGQI3.js +69 -0
- package/dist/chunk-HF7PGQI3.js.map +1 -0
- package/dist/chunk-LBH6SLHH.js +543 -0
- package/dist/chunk-LBH6SLHH.js.map +1 -0
- package/dist/chunk-M53MPY3U.js +115 -0
- package/dist/chunk-M53MPY3U.js.map +1 -0
- package/dist/chunk-MI7OZ5XD.js +146 -0
- package/dist/chunk-MI7OZ5XD.js.map +1 -0
- package/dist/chunk-NB46PEG2.js +177 -0
- package/dist/chunk-NB46PEG2.js.map +1 -0
- package/dist/chunk-ORBF5IW3.js +60 -0
- package/dist/chunk-ORBF5IW3.js.map +1 -0
- package/dist/chunk-PFU5YLRH.js +131 -0
- package/dist/chunk-PFU5YLRH.js.map +1 -0
- package/dist/chunk-QLRCFKLU.js +34 -0
- package/dist/chunk-QLRCFKLU.js.map +1 -0
- package/dist/chunk-QQTIJN3S.js +167 -0
- package/dist/chunk-QQTIJN3S.js.map +1 -0
- package/dist/chunk-QRYCNVLT.js +72 -0
- package/dist/chunk-QRYCNVLT.js.map +1 -0
- package/dist/chunk-S6E67XMR.js +52 -0
- package/dist/chunk-S6E67XMR.js.map +1 -0
- package/dist/chunk-S6W4SURF.js +33 -0
- package/dist/chunk-S6W4SURF.js.map +1 -0
- package/dist/chunk-SMZYA6CY.js +121 -0
- package/dist/chunk-SMZYA6CY.js.map +1 -0
- package/dist/chunk-SNOA575X.js +12 -0
- package/dist/chunk-SNOA575X.js.map +1 -0
- package/dist/chunk-SZRIZBWI.js +44 -0
- package/dist/chunk-SZRIZBWI.js.map +1 -0
- package/dist/chunk-TAGHPCFT.js +47 -0
- package/dist/chunk-TAGHPCFT.js.map +1 -0
- package/dist/chunk-TGKCHHXT.js +34 -0
- package/dist/chunk-TGKCHHXT.js.map +1 -0
- package/dist/chunk-TORYFKPK.js +39 -0
- package/dist/chunk-TORYFKPK.js.map +1 -0
- package/dist/chunk-U35GRLBD.js +143 -0
- package/dist/chunk-U35GRLBD.js.map +1 -0
- package/dist/chunk-W2HBRERV.js +57 -0
- package/dist/chunk-W2HBRERV.js.map +1 -0
- package/dist/chunk-WYU476R2.js +119 -0
- package/dist/chunk-WYU476R2.js.map +1 -0
- package/dist/chunk-YMO45Z6G.js +69 -0
- package/dist/chunk-YMO45Z6G.js.map +1 -0
- package/dist/claude-auto-run.js +1717 -0
- package/dist/claude-auto-run.js.map +1 -0
- package/dist/claude-auto.js +186 -0
- package/dist/claude-auto.js.map +1 -0
- package/dist/cost-QGM3D4QW.js +72 -0
- package/dist/cost-QGM3D4QW.js.map +1 -0
- package/dist/cost-QKN3U7AG.js +11 -0
- package/dist/cost-QKN3U7AG.js.map +1 -0
- package/dist/create-T3BDDS6G.js +14 -0
- package/dist/create-T3BDDS6G.js.map +1 -0
- package/dist/create-U5WYKTD4.js +118 -0
- package/dist/create-U5WYKTD4.js.map +1 -0
- package/dist/crontab-CDMC2FDT.js +118 -0
- package/dist/crontab-CDMC2FDT.js.map +1 -0
- package/dist/crontab-MAJ52FOK.js +118 -0
- package/dist/crontab-MAJ52FOK.js.map +1 -0
- package/dist/crontab-PNEWANLW.js +12 -0
- package/dist/crontab-PNEWANLW.js.map +1 -0
- package/dist/edit-77E3ZQHM.js +134 -0
- package/dist/edit-77E3ZQHM.js.map +1 -0
- package/dist/edit-RVPRAAQ2.js +13 -0
- package/dist/edit-RVPRAAQ2.js.map +1 -0
- package/dist/index.d.ts +1137 -0
- package/dist/index.js +2049 -0
- package/dist/index.js.map +1 -0
- package/dist/launchd-7F27BIZB.js +166 -0
- package/dist/launchd-7F27BIZB.js.map +1 -0
- package/dist/launchd-HNZIWLNC.js +166 -0
- package/dist/launchd-HNZIWLNC.js.map +1 -0
- package/dist/launchd-LZGDP7BM.js +12 -0
- package/dist/launchd-LZGDP7BM.js.map +1 -0
- package/dist/list-OIGERGYJ.js +15 -0
- package/dist/list-OIGERGYJ.js.map +1 -0
- package/dist/list-T35RSQVU.js +73 -0
- package/dist/list-T35RSQVU.js.map +1 -0
- package/dist/logs-D5FNSCXE.js +12 -0
- package/dist/logs-D5FNSCXE.js.map +1 -0
- package/dist/logs-YVSFXBSB.js +40 -0
- package/dist/logs-YVSFXBSB.js.map +1 -0
- package/dist/pause-2YOLFMAR.js +12 -0
- package/dist/pause-2YOLFMAR.js.map +1 -0
- package/dist/pause-JB42JGTB.js +45 -0
- package/dist/pause-JB42JGTB.js.map +1 -0
- package/dist/pause-OJNUYBCJ.js +47 -0
- package/dist/pause-OJNUYBCJ.js.map +1 -0
- package/dist/remove-RXYKFYBI.js +12 -0
- package/dist/remove-RXYKFYBI.js.map +1 -0
- package/dist/remove-UASXZCOR.js +59 -0
- package/dist/remove-UASXZCOR.js.map +1 -0
- package/dist/report-CHAJH2SA.js +150 -0
- package/dist/report-CHAJH2SA.js.map +1 -0
- package/dist/report-IYGK5HTC.js +14 -0
- package/dist/report-IYGK5HTC.js.map +1 -0
- package/dist/resume-3ATNZP6D.js +13 -0
- package/dist/resume-3ATNZP6D.js.map +1 -0
- package/dist/resume-6WVGU6XW.js +48 -0
- package/dist/resume-6WVGU6XW.js.map +1 -0
- package/dist/resume-JVTR7OEX.js +50 -0
- package/dist/resume-JVTR7OEX.js.map +1 -0
- package/dist/schtasks-2EQAD3ES.js +11 -0
- package/dist/schtasks-2EQAD3ES.js.map +1 -0
- package/dist/schtasks-4V2IFD3A.js +142 -0
- package/dist/schtasks-4V2IFD3A.js.map +1 -0
- package/dist/schtasks-JGEPEKQS.js +142 -0
- package/dist/schtasks-JGEPEKQS.js.map +1 -0
- package/dist/tui-2DUPCX3Q.js +15 -0
- package/dist/tui-2DUPCX3Q.js.map +1 -0
- package/dist/tui-6LOGPILA.js +547 -0
- package/dist/tui-6LOGPILA.js.map +1 -0
- package/package.json +81 -0
- package/scripts/postinstall.mjs +65 -0
- package/scripts/preuninstall.mjs +33 -0
- package/skills/edit/SKILL.md +25 -0
- package/skills/list/SKILL.md +26 -0
- package/skills/logs/SKILL.md +33 -0
- package/skills/pause/SKILL.md +21 -0
- package/skills/remove/SKILL.md +22 -0
- package/skills/resume/SKILL.md +21 -0
- package/skills/setup/SKILL.md +195 -0
- package/skills/status/SKILL.md +27 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatDuration
|
|
3
|
+
} from "./chunk-A6XWZPLY.js";
|
|
4
|
+
import {
|
|
5
|
+
listRunLogs
|
|
6
|
+
} from "./chunk-GLW7T4QE.js";
|
|
7
|
+
import "./chunk-4I5UIASZ.js";
|
|
8
|
+
import {
|
|
9
|
+
listJobs,
|
|
10
|
+
readJob
|
|
11
|
+
} from "./chunk-BY5YEOVG.js";
|
|
12
|
+
import "./chunk-2D5E23XA.js";
|
|
13
|
+
import "./chunk-E3XVLTT4.js";
|
|
14
|
+
import "./chunk-H2MUDYMW.js";
|
|
15
|
+
import "./chunk-AWLSYOVF.js";
|
|
16
|
+
|
|
17
|
+
// src/cli/commands/report.ts
|
|
18
|
+
function aggregateLogs(logs) {
|
|
19
|
+
let successCount = 0;
|
|
20
|
+
let noChangesCount = 0;
|
|
21
|
+
let errorCount = 0;
|
|
22
|
+
let gitErrorCount = 0;
|
|
23
|
+
let lockedCount = 0;
|
|
24
|
+
let totalCost = 0;
|
|
25
|
+
let totalDuration = 0;
|
|
26
|
+
let prsCreated = 0;
|
|
27
|
+
let lastPrUrl;
|
|
28
|
+
let earliest;
|
|
29
|
+
let latest;
|
|
30
|
+
for (const log of logs) {
|
|
31
|
+
switch (log.status) {
|
|
32
|
+
case "success":
|
|
33
|
+
successCount++;
|
|
34
|
+
break;
|
|
35
|
+
case "no-changes":
|
|
36
|
+
noChangesCount++;
|
|
37
|
+
break;
|
|
38
|
+
case "error":
|
|
39
|
+
errorCount++;
|
|
40
|
+
break;
|
|
41
|
+
case "git-error":
|
|
42
|
+
gitErrorCount++;
|
|
43
|
+
break;
|
|
44
|
+
case "locked":
|
|
45
|
+
lockedCount++;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
totalCost += log.costUsd ?? 0;
|
|
49
|
+
totalDuration += log.durationMs;
|
|
50
|
+
if (log.prUrl) {
|
|
51
|
+
prsCreated++;
|
|
52
|
+
if (!lastPrUrl) {
|
|
53
|
+
lastPrUrl = log.prUrl;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!earliest || log.startedAt < earliest) {
|
|
57
|
+
earliest = log.startedAt;
|
|
58
|
+
}
|
|
59
|
+
if (!latest || log.startedAt > latest) {
|
|
60
|
+
latest = log.startedAt;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
totalRuns: logs.length,
|
|
65
|
+
successCount,
|
|
66
|
+
noChangesCount,
|
|
67
|
+
errorCount,
|
|
68
|
+
gitErrorCount,
|
|
69
|
+
lockedCount,
|
|
70
|
+
totalCost,
|
|
71
|
+
avgDurationMs: logs.length > 0 ? Math.round(totalDuration / logs.length) : 0,
|
|
72
|
+
prsCreated,
|
|
73
|
+
lastPrUrl,
|
|
74
|
+
earliest,
|
|
75
|
+
latest
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function formatReport(report) {
|
|
79
|
+
const lines = [];
|
|
80
|
+
lines.push(`Run Report: ${report.title}`);
|
|
81
|
+
if (report.repoPath) {
|
|
82
|
+
lines.push(`Repository: ${report.repoPath}`);
|
|
83
|
+
}
|
|
84
|
+
if (report.earliest && report.latest) {
|
|
85
|
+
const fmtDate = (iso) => new Date(iso).toLocaleDateString("en-US", {
|
|
86
|
+
year: "numeric",
|
|
87
|
+
month: "short",
|
|
88
|
+
day: "numeric"
|
|
89
|
+
});
|
|
90
|
+
lines.push(`Period: ${fmtDate(report.earliest)} to ${fmtDate(report.latest)}`);
|
|
91
|
+
}
|
|
92
|
+
lines.push("");
|
|
93
|
+
lines.push(`Runs: ${report.totalRuns}`);
|
|
94
|
+
const pct = (n) => report.totalRuns > 0 ? Math.round(n / report.totalRuns * 100) : 0;
|
|
95
|
+
lines.push(` Success: ${report.successCount} (${pct(report.successCount)}%)`);
|
|
96
|
+
lines.push(` No changes: ${report.noChangesCount} (${pct(report.noChangesCount)}%)`);
|
|
97
|
+
lines.push(
|
|
98
|
+
` Errors: ${report.errorCount + report.gitErrorCount} (${pct(report.errorCount + report.gitErrorCount)}%)`
|
|
99
|
+
);
|
|
100
|
+
lines.push(` Locked: ${report.lockedCount} (${pct(report.lockedCount)}%)`);
|
|
101
|
+
lines.push("");
|
|
102
|
+
lines.push(`PRs created: ${report.prsCreated}`);
|
|
103
|
+
lines.push(`Total cost: $${report.totalCost.toFixed(2)}`);
|
|
104
|
+
lines.push(`Avg duration: ${formatDuration(report.avgDurationMs)}`);
|
|
105
|
+
lines.push(`Last PR: ${report.lastPrUrl || "none"}`);
|
|
106
|
+
return lines.join("\n");
|
|
107
|
+
}
|
|
108
|
+
async function reportCommand(args) {
|
|
109
|
+
const jobId = args.jobId;
|
|
110
|
+
if (jobId) {
|
|
111
|
+
const job = await readJob(jobId);
|
|
112
|
+
const logs = await listRunLogs(jobId);
|
|
113
|
+
if (logs.length === 0) {
|
|
114
|
+
console.log(`No runs found for job ${jobId}.`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const agg = aggregateLogs(logs);
|
|
118
|
+
const report = {
|
|
119
|
+
...agg,
|
|
120
|
+
title: `${job.name} (${job.id})`,
|
|
121
|
+
repoPath: job.repo.path
|
|
122
|
+
};
|
|
123
|
+
console.log(formatReport(report));
|
|
124
|
+
} else {
|
|
125
|
+
const jobs = await listJobs();
|
|
126
|
+
if (jobs.length === 0) {
|
|
127
|
+
console.log("No jobs configured.");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const allLogs = [];
|
|
131
|
+
for (const job of jobs) {
|
|
132
|
+
const logs = await listRunLogs(job.id);
|
|
133
|
+
allLogs.push(...logs);
|
|
134
|
+
}
|
|
135
|
+
if (allLogs.length === 0) {
|
|
136
|
+
console.log("No runs found across any jobs.");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const agg = aggregateLogs(allLogs);
|
|
140
|
+
const report = {
|
|
141
|
+
...agg,
|
|
142
|
+
title: `All Jobs (${jobs.length} jobs)`
|
|
143
|
+
};
|
|
144
|
+
console.log(formatReport(report));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export {
|
|
148
|
+
reportCommand
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=report-CHAJH2SA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/report.ts"],"sourcesContent":["import { listJobs, readJob } from \"../../core/job-manager.js\";\nimport { listRunLogs } from \"../../runner/logger.js\";\nimport type { RunLogEntry } from \"../../runner/types.js\";\nimport { formatDuration } from \"../format.js\";\nimport type { ParsedCommand } from \"../types.js\";\n\ninterface AggregateReport {\n\ttitle: string;\n\trepoPath?: string;\n\ttotalRuns: number;\n\tsuccessCount: number;\n\tnoChangesCount: number;\n\terrorCount: number;\n\tgitErrorCount: number;\n\tlockedCount: number;\n\ttotalCost: number;\n\tavgDurationMs: number;\n\tprsCreated: number;\n\tlastPrUrl: string | undefined;\n\tearliest: string | undefined;\n\tlatest: string | undefined;\n}\n\nfunction aggregateLogs(logs: RunLogEntry[]): Omit<AggregateReport, \"title\" | \"repoPath\"> {\n\tlet successCount = 0;\n\tlet noChangesCount = 0;\n\tlet errorCount = 0;\n\tlet gitErrorCount = 0;\n\tlet lockedCount = 0;\n\tlet totalCost = 0;\n\tlet totalDuration = 0;\n\tlet prsCreated = 0;\n\tlet lastPrUrl: string | undefined;\n\tlet earliest: string | undefined;\n\tlet latest: string | undefined;\n\n\tfor (const log of logs) {\n\t\tswitch (log.status) {\n\t\t\tcase \"success\":\n\t\t\t\tsuccessCount++;\n\t\t\t\tbreak;\n\t\t\tcase \"no-changes\":\n\t\t\t\tnoChangesCount++;\n\t\t\t\tbreak;\n\t\t\tcase \"error\":\n\t\t\t\terrorCount++;\n\t\t\t\tbreak;\n\t\t\tcase \"git-error\":\n\t\t\t\tgitErrorCount++;\n\t\t\t\tbreak;\n\t\t\tcase \"locked\":\n\t\t\t\tlockedCount++;\n\t\t\t\tbreak;\n\t\t}\n\n\t\ttotalCost += log.costUsd ?? 0;\n\t\ttotalDuration += log.durationMs;\n\n\t\tif (log.prUrl) {\n\t\t\tprsCreated++;\n\t\t\tif (!lastPrUrl) {\n\t\t\t\tlastPrUrl = log.prUrl; // logs are sorted newest-first\n\t\t\t}\n\t\t}\n\n\t\tif (!earliest || log.startedAt < earliest) {\n\t\t\tearliest = log.startedAt;\n\t\t}\n\t\tif (!latest || log.startedAt > latest) {\n\t\t\tlatest = log.startedAt;\n\t\t}\n\t}\n\n\treturn {\n\t\ttotalRuns: logs.length,\n\t\tsuccessCount,\n\t\tnoChangesCount,\n\t\terrorCount,\n\t\tgitErrorCount,\n\t\tlockedCount,\n\t\ttotalCost,\n\t\tavgDurationMs: logs.length > 0 ? Math.round(totalDuration / logs.length) : 0,\n\t\tprsCreated,\n\t\tlastPrUrl,\n\t\tearliest,\n\t\tlatest,\n\t};\n}\n\nfunction formatReport(report: AggregateReport): string {\n\tconst lines: string[] = [];\n\tlines.push(`Run Report: ${report.title}`);\n\n\tif (report.repoPath) {\n\t\tlines.push(`Repository: ${report.repoPath}`);\n\t}\n\n\tif (report.earliest && report.latest) {\n\t\tconst fmtDate = (iso: string) =>\n\t\t\tnew Date(iso).toLocaleDateString(\"en-US\", {\n\t\t\t\tyear: \"numeric\",\n\t\t\t\tmonth: \"short\",\n\t\t\t\tday: \"numeric\",\n\t\t\t});\n\t\tlines.push(`Period: ${fmtDate(report.earliest)} to ${fmtDate(report.latest)}`);\n\t}\n\n\tlines.push(\"\");\n\tlines.push(`Runs: ${report.totalRuns}`);\n\n\tconst pct = (n: number) => (report.totalRuns > 0 ? Math.round((n / report.totalRuns) * 100) : 0);\n\tlines.push(` Success: ${report.successCount} (${pct(report.successCount)}%)`);\n\tlines.push(` No changes: ${report.noChangesCount} (${pct(report.noChangesCount)}%)`);\n\tlines.push(\n\t\t` Errors: ${report.errorCount + report.gitErrorCount} (${pct(report.errorCount + report.gitErrorCount)}%)`,\n\t);\n\tlines.push(` Locked: ${report.lockedCount} (${pct(report.lockedCount)}%)`);\n\n\tlines.push(\"\");\n\tlines.push(`PRs created: ${report.prsCreated}`);\n\tlines.push(`Total cost: $${report.totalCost.toFixed(2)}`);\n\tlines.push(`Avg duration: ${formatDuration(report.avgDurationMs)}`);\n\tlines.push(`Last PR: ${report.lastPrUrl || \"none\"}`);\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Show aggregate run summary for a single job or across all jobs.\n * Satisfies REPT-02 (aggregate run summary reports).\n */\nexport async function reportCommand(args: ParsedCommand[\"args\"]): Promise<void> {\n\tconst jobId = args.jobId as string | undefined;\n\n\tif (jobId) {\n\t\t// Single job report\n\t\tconst job = await readJob(jobId);\n\t\tconst logs = await listRunLogs(jobId);\n\n\t\tif (logs.length === 0) {\n\t\t\tconsole.log(`No runs found for job ${jobId}.`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst agg = aggregateLogs(logs);\n\t\tconst report: AggregateReport = {\n\t\t\t...agg,\n\t\t\ttitle: `${job.name} (${job.id})`,\n\t\t\trepoPath: job.repo.path,\n\t\t};\n\n\t\tconsole.log(formatReport(report));\n\t} else {\n\t\t// All-jobs report\n\t\tconst jobs = await listJobs();\n\n\t\tif (jobs.length === 0) {\n\t\t\tconsole.log(\"No jobs configured.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst allLogs: RunLogEntry[] = [];\n\t\tfor (const job of jobs) {\n\t\t\tconst logs = await listRunLogs(job.id);\n\t\t\tallLogs.push(...logs);\n\t\t}\n\n\t\tif (allLogs.length === 0) {\n\t\t\tconsole.log(\"No runs found across any jobs.\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst agg = aggregateLogs(allLogs);\n\t\tconst report: AggregateReport = {\n\t\t\t...agg,\n\t\t\ttitle: `All Jobs (${jobs.length} jobs)`,\n\t\t};\n\n\t\tconsole.log(formatReport(report));\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
reportCommand
|
|
3
|
+
} from "./chunk-MI7OZ5XD.js";
|
|
4
|
+
import "./chunk-SMZYA6CY.js";
|
|
5
|
+
import "./chunk-ORBF5IW3.js";
|
|
6
|
+
import "./chunk-4I5UIASZ.js";
|
|
7
|
+
import "./chunk-24PS2XSV.js";
|
|
8
|
+
import "./chunk-E3XVLTT4.js";
|
|
9
|
+
import "./chunk-H2MUDYMW.js";
|
|
10
|
+
import "./chunk-YMO45Z6G.js";
|
|
11
|
+
export {
|
|
12
|
+
reportCommand
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=report-IYGK5HTC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resumeCommand
|
|
3
|
+
} from "./chunk-TAGHPCFT.js";
|
|
4
|
+
import "./chunk-QLRCFKLU.js";
|
|
5
|
+
import "./chunk-D4MBOIYQ.js";
|
|
6
|
+
import "./chunk-24PS2XSV.js";
|
|
7
|
+
import "./chunk-E3XVLTT4.js";
|
|
8
|
+
import "./chunk-H2MUDYMW.js";
|
|
9
|
+
import "./chunk-YMO45Z6G.js";
|
|
10
|
+
export {
|
|
11
|
+
resumeCommand
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=resume-3ATNZP6D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getNextRuns
|
|
3
|
+
} from "./chunk-SNOA575X.js";
|
|
4
|
+
import {
|
|
5
|
+
createScheduler
|
|
6
|
+
} from "./chunk-S6W4SURF.js";
|
|
7
|
+
import {
|
|
8
|
+
readJob,
|
|
9
|
+
updateJob
|
|
10
|
+
} from "./chunk-NB46PEG2.js";
|
|
11
|
+
import "./chunk-H2MUDYMW.js";
|
|
12
|
+
import "./chunk-6RYMWH5M.js";
|
|
13
|
+
|
|
14
|
+
// src/cli/commands/resume.ts
|
|
15
|
+
async function resumeCommand(args) {
|
|
16
|
+
const jobId = args.jobId;
|
|
17
|
+
if (!jobId) {
|
|
18
|
+
console.error("Usage: claude-auto resume <job-id>");
|
|
19
|
+
throw new Error("Missing required argument: job-id");
|
|
20
|
+
}
|
|
21
|
+
let config;
|
|
22
|
+
try {
|
|
23
|
+
config = await readJob(jobId);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
26
|
+
console.error(`Job ${jobId} not found.`);
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
if (config.enabled) {
|
|
32
|
+
console.log(`Job ${jobId} is already active.`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const updatedConfig = await updateJob(jobId, { enabled: true });
|
|
36
|
+
const scheduler = await createScheduler();
|
|
37
|
+
if (await scheduler.isRegistered(jobId)) {
|
|
38
|
+
await scheduler.unregister(jobId);
|
|
39
|
+
}
|
|
40
|
+
await scheduler.register(updatedConfig);
|
|
41
|
+
const nextRuns = getNextRuns(config.schedule.cron, config.schedule.timezone, 1);
|
|
42
|
+
const nextRunStr = nextRuns.length > 0 ? nextRuns[0].toLocaleString() : "unknown";
|
|
43
|
+
console.log(`Job ${jobId} resumed. Next run: ${nextRunStr}`);
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
resumeCommand
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=resume-6WVGU6XW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/resume.ts"],"sourcesContent":["import { readJob, updateJob } from \"../../core/job-manager.js\";\nimport { getNextRuns } from \"../../core/schedule.js\";\nimport type { JobConfig } from \"../../core/types.js\";\nimport { createScheduler } from \"../../platform/scheduler.js\";\nimport type { ParsedCommand } from \"../types.js\";\n\n/**\n * Resume a paused job: sets enabled=true and re-registers with scheduler.\n * Idempotent: resuming an already-active job prints a message and exits cleanly.\n */\nexport async function resumeCommand(args: ParsedCommand[\"args\"]): Promise<void> {\n\tconst jobId = args.jobId as string | undefined;\n\tif (!jobId) {\n\t\tconsole.error(\"Usage: claude-auto resume <job-id>\");\n\t\tthrow new Error(\"Missing required argument: job-id\");\n\t}\n\n\tlet config: JobConfig;\n\ttry {\n\t\tconfig = await readJob(jobId);\n\t} catch (err: unknown) {\n\t\tif (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\tconsole.error(`Job ${jobId} not found.`);\n\t\t\tthrow err;\n\t\t}\n\t\tthrow err;\n\t}\n\n\tif (config.enabled) {\n\t\tconsole.log(`Job ${jobId} is already active.`);\n\t\treturn;\n\t}\n\n\tconst updatedConfig = await updateJob(jobId, { enabled: true });\n\n\tconst scheduler = await createScheduler();\n\n\t// Defensive: unregister first if already registered (shouldn't happen, but safe)\n\tif (await scheduler.isRegistered(jobId)) {\n\t\tawait scheduler.unregister(jobId);\n\t}\n\n\tawait scheduler.register(updatedConfig);\n\n\tconst nextRuns = getNextRuns(config.schedule.cron, config.schedule.timezone, 1);\n\tconst nextRunStr = nextRuns.length > 0 ? nextRuns[0].toLocaleString() : \"unknown\";\n\n\tconsole.log(`Job ${jobId} resumed. Next run: ${nextRunStr}`);\n}\n"],"mappings":";;;;;;;;;;;;;;AAUA,eAAsB,cAAc,MAA4C;AAC/E,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,OAAO;AACX,YAAQ,MAAM,oCAAoC;AAClD,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACpD;AAEA,MAAI;AACJ,MAAI;AACH,aAAS,MAAM,QAAQ,KAAK;AAAA,EAC7B,SAAS,KAAc;AACtB,QAAI,eAAe,SAAS,UAAU,OAAQ,IAA8B,SAAS,UAAU;AAC9F,cAAQ,MAAM,OAAO,KAAK,aAAa;AACvC,YAAM;AAAA,IACP;AACA,UAAM;AAAA,EACP;AAEA,MAAI,OAAO,SAAS;AACnB,YAAQ,IAAI,OAAO,KAAK,qBAAqB;AAC7C;AAAA,EACD;AAEA,QAAM,gBAAgB,MAAM,UAAU,OAAO,EAAE,SAAS,KAAK,CAAC;AAE9D,QAAM,YAAY,MAAM,gBAAgB;AAGxC,MAAI,MAAM,UAAU,aAAa,KAAK,GAAG;AACxC,UAAM,UAAU,WAAW,KAAK;AAAA,EACjC;AAEA,QAAM,UAAU,SAAS,aAAa;AAEtC,QAAM,WAAW,YAAY,OAAO,SAAS,MAAM,OAAO,SAAS,UAAU,CAAC;AAC9E,QAAM,aAAa,SAAS,SAAS,IAAI,SAAS,CAAC,EAAE,eAAe,IAAI;AAExE,UAAQ,IAAI,OAAO,KAAK,uBAAuB,UAAU,EAAE;AAC5D;","names":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createScheduler
|
|
3
|
+
} from "./chunk-DVZC42TL.js";
|
|
4
|
+
import {
|
|
5
|
+
getNextRuns
|
|
6
|
+
} from "./chunk-TORYFKPK.js";
|
|
7
|
+
import {
|
|
8
|
+
readJob,
|
|
9
|
+
updateJob
|
|
10
|
+
} from "./chunk-BY5YEOVG.js";
|
|
11
|
+
import "./chunk-2D5E23XA.js";
|
|
12
|
+
import "./chunk-E3XVLTT4.js";
|
|
13
|
+
import "./chunk-H2MUDYMW.js";
|
|
14
|
+
import "./chunk-AWLSYOVF.js";
|
|
15
|
+
|
|
16
|
+
// src/cli/commands/resume.ts
|
|
17
|
+
async function resumeCommand(args) {
|
|
18
|
+
const jobId = args.jobId;
|
|
19
|
+
if (!jobId) {
|
|
20
|
+
console.error("Usage: claude-auto resume <job-id>");
|
|
21
|
+
throw new Error("Missing required argument: job-id");
|
|
22
|
+
}
|
|
23
|
+
let config;
|
|
24
|
+
try {
|
|
25
|
+
config = await readJob(jobId);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
28
|
+
console.error(`Job ${jobId} not found.`);
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
if (config.enabled) {
|
|
34
|
+
console.log(`Job ${jobId} is already active.`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const updatedConfig = await updateJob(jobId, { enabled: true });
|
|
38
|
+
const scheduler = await createScheduler();
|
|
39
|
+
if (await scheduler.isRegistered(jobId)) {
|
|
40
|
+
await scheduler.unregister(jobId);
|
|
41
|
+
}
|
|
42
|
+
await scheduler.register(updatedConfig);
|
|
43
|
+
const nextRuns = getNextRuns(config.schedule.cron, config.schedule.timezone, 1);
|
|
44
|
+
const nextRunStr = nextRuns.length > 0 ? nextRuns[0].toLocaleString() : "unknown";
|
|
45
|
+
console.log(`Job ${jobId} resumed. Next run: ${nextRunStr}`);
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
resumeCommand
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=resume-JVTR7OEX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/resume.ts"],"sourcesContent":["import { readJob, updateJob } from \"../../core/job-manager.js\";\nimport { getNextRuns } from \"../../core/schedule.js\";\nimport type { JobConfig } from \"../../core/types.js\";\nimport { createScheduler } from \"../../platform/scheduler.js\";\nimport type { ParsedCommand } from \"../types.js\";\n\n/**\n * Resume a paused job: sets enabled=true and re-registers with scheduler.\n * Idempotent: resuming an already-active job prints a message and exits cleanly.\n */\nexport async function resumeCommand(args: ParsedCommand[\"args\"]): Promise<void> {\n\tconst jobId = args.jobId as string | undefined;\n\tif (!jobId) {\n\t\tconsole.error(\"Usage: claude-auto resume <job-id>\");\n\t\tthrow new Error(\"Missing required argument: job-id\");\n\t}\n\n\tlet config: JobConfig;\n\ttry {\n\t\tconfig = await readJob(jobId);\n\t} catch (err: unknown) {\n\t\tif (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\tconsole.error(`Job ${jobId} not found.`);\n\t\t\tthrow err;\n\t\t}\n\t\tthrow err;\n\t}\n\n\tif (config.enabled) {\n\t\tconsole.log(`Job ${jobId} is already active.`);\n\t\treturn;\n\t}\n\n\tconst updatedConfig = await updateJob(jobId, { enabled: true });\n\n\tconst scheduler = await createScheduler();\n\n\t// Defensive: unregister first if already registered (shouldn't happen, but safe)\n\tif (await scheduler.isRegistered(jobId)) {\n\t\tawait scheduler.unregister(jobId);\n\t}\n\n\tawait scheduler.register(updatedConfig);\n\n\tconst nextRuns = getNextRuns(config.schedule.cron, config.schedule.timezone, 1);\n\tconst nextRunStr = nextRuns.length > 0 ? nextRuns[0].toLocaleString() : \"unknown\";\n\n\tconsole.log(`Job ${jobId} resumed. Next run: ${nextRunStr}`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAUA,eAAsB,cAAc,MAA4C;AAC/E,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,OAAO;AACX,YAAQ,MAAM,oCAAoC;AAClD,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACpD;AAEA,MAAI;AACJ,MAAI;AACH,aAAS,MAAM,QAAQ,KAAK;AAAA,EAC7B,SAAS,KAAc;AACtB,QAAI,eAAe,SAAS,UAAU,OAAQ,IAA8B,SAAS,UAAU;AAC9F,cAAQ,MAAM,OAAO,KAAK,aAAa;AACvC,YAAM;AAAA,IACP;AACA,UAAM;AAAA,EACP;AAEA,MAAI,OAAO,SAAS;AACnB,YAAQ,IAAI,OAAO,KAAK,qBAAqB;AAC7C;AAAA,EACD;AAEA,QAAM,gBAAgB,MAAM,UAAU,OAAO,EAAE,SAAS,KAAK,CAAC;AAE9D,QAAM,YAAY,MAAM,gBAAgB;AAGxC,MAAI,MAAM,UAAU,aAAa,KAAK,GAAG;AACxC,UAAM,UAAU,WAAW,KAAK;AAAA,EACjC;AAEA,QAAM,UAAU,SAAS,aAAa;AAEtC,QAAM,WAAW,YAAY,OAAO,SAAS,MAAM,OAAO,SAAS,UAAU,CAAC;AAC9E,QAAM,aAAa,SAAS,SAAS,IAAI,SAAS,CAAC,EAAE,eAAe,IAAI;AAExE,UAAQ,IAAI,OAAO,KAAK,uBAAuB,UAAU,EAAE;AAC5D;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
execCommand
|
|
3
|
+
} from "./chunk-3NEANSUS.js";
|
|
4
|
+
import {
|
|
5
|
+
SchedulerError
|
|
6
|
+
} from "./chunk-6RYMWH5M.js";
|
|
7
|
+
|
|
8
|
+
// src/platform/schtasks.ts
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import { CronExpressionParser } from "cron-parser";
|
|
12
|
+
var TASK_PREFIX = "claude-auto-";
|
|
13
|
+
var dayMap = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
14
|
+
function cronToSchtasks(cronExpr) {
|
|
15
|
+
const interval = CronExpressionParser.parse(cronExpr);
|
|
16
|
+
const fields = interval.fields;
|
|
17
|
+
const minutes = [...fields.minute.values].map(Number);
|
|
18
|
+
const hours = [...fields.hour.values].map(Number);
|
|
19
|
+
const daysOfMonth = [...fields.dayOfMonth.values].map(Number);
|
|
20
|
+
const months = [...fields.month.values].map(Number);
|
|
21
|
+
const daysOfWeek = [...fields.dayOfWeek.values].map(Number);
|
|
22
|
+
const isAllHours = hours.length === 24;
|
|
23
|
+
const isAllDays = daysOfMonth.length === 31;
|
|
24
|
+
const isAllMonths = months.length === 12;
|
|
25
|
+
const isAllDow = daysOfWeek.length === 8;
|
|
26
|
+
if (isAllHours && isAllDays && isAllMonths && isAllDow && minutes.length > 1) {
|
|
27
|
+
const step = minutes[1] - minutes[0];
|
|
28
|
+
const isEvenStep = minutes.every((m, i) => i === 0 || m - minutes[i - 1] === step);
|
|
29
|
+
if (isEvenStep) {
|
|
30
|
+
return {
|
|
31
|
+
args: ["/sc", "MINUTE", "/mo", String(step)],
|
|
32
|
+
description: `every ${step} minutes`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (isAllDays && isAllMonths && isAllDow && minutes.length === 1 && hours.length > 1) {
|
|
37
|
+
const step = hours[1] - hours[0];
|
|
38
|
+
const isEvenStep = hours.every((h, i) => i === 0 || h - hours[i - 1] === step);
|
|
39
|
+
if (isEvenStep) {
|
|
40
|
+
const st = `${String(hours[0]).padStart(2, "0")}:${String(minutes[0]).padStart(2, "0")}`;
|
|
41
|
+
return {
|
|
42
|
+
args: ["/sc", "HOURLY", "/mo", String(step), "/st", st],
|
|
43
|
+
description: `every ${step} hours starting at ${st}`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (isAllDays && isAllMonths && isAllDow && minutes.length === 1 && hours.length === 1) {
|
|
48
|
+
const st = `${String(hours[0]).padStart(2, "0")}:${String(minutes[0]).padStart(2, "0")}`;
|
|
49
|
+
return {
|
|
50
|
+
args: ["/sc", "DAILY", "/st", st],
|
|
51
|
+
description: `daily at ${st}`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (isAllDays && isAllMonths && !isAllDow && minutes.length === 1 && hours.length === 1) {
|
|
55
|
+
const days = daysOfWeek.filter((d) => d <= 6).map((d) => dayMap[d]);
|
|
56
|
+
const st = `${String(hours[0]).padStart(2, "0")}:${String(minutes[0]).padStart(2, "0")}`;
|
|
57
|
+
return {
|
|
58
|
+
args: ["/sc", "WEEKLY", "/d", days.join(","), "/st", st],
|
|
59
|
+
description: `weekly on ${days.join(",")} at ${st}`
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (!isAllDays && isAllMonths && isAllDow && minutes.length === 1 && hours.length === 1 && daysOfMonth.length === 1) {
|
|
63
|
+
const st = `${String(hours[0]).padStart(2, "0")}:${String(minutes[0]).padStart(2, "0")}`;
|
|
64
|
+
return {
|
|
65
|
+
args: ["/sc", "MONTHLY", "/d", String(daysOfMonth[0]), "/st", st],
|
|
66
|
+
description: `monthly on day ${daysOfMonth[0]} at ${st}`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
throw new SchedulerError(
|
|
70
|
+
"win32",
|
|
71
|
+
`Cron expression "${cronExpr}" cannot be represented as a single Windows Task Scheduler entry. Simplify the schedule (e.g., daily at a specific time, every N minutes, or specific weekdays).`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
function getRunnerPath() {
|
|
75
|
+
try {
|
|
76
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
77
|
+
return join(currentDir, "..", "..", "dist", "claude-auto-run.js");
|
|
78
|
+
} catch {
|
|
79
|
+
return join(process.cwd(), "dist", "claude-auto-run.js");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
var SchtasksScheduler = class {
|
|
83
|
+
async register(job, _env) {
|
|
84
|
+
const registered = await this.isRegistered(job.id);
|
|
85
|
+
if (registered) {
|
|
86
|
+
throw new SchedulerError("win32", `Job "${job.id}" is already registered`);
|
|
87
|
+
}
|
|
88
|
+
const schedule = cronToSchtasks(job.schedule.cron);
|
|
89
|
+
const runnerPath = getRunnerPath();
|
|
90
|
+
const command = `"${process.execPath}" "${runnerPath}" --job-id ${job.id}`;
|
|
91
|
+
const taskName = `${TASK_PREFIX}${job.id}`;
|
|
92
|
+
const args = ["/create", "/tn", taskName, "/tr", command, ...schedule.args, "/f"];
|
|
93
|
+
await execCommand("schtasks", args);
|
|
94
|
+
}
|
|
95
|
+
async unregister(jobId) {
|
|
96
|
+
const taskName = `${TASK_PREFIX}${jobId}`;
|
|
97
|
+
try {
|
|
98
|
+
await execCommand("schtasks", ["/delete", "/tn", taskName, "/f"]);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async isRegistered(jobId) {
|
|
103
|
+
const taskName = `${TASK_PREFIX}${jobId}`;
|
|
104
|
+
try {
|
|
105
|
+
await execCommand("schtasks", ["/query", "/tn", taskName, "/fo", "CSV", "/nh"]);
|
|
106
|
+
return true;
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async list() {
|
|
112
|
+
const jobs = [];
|
|
113
|
+
try {
|
|
114
|
+
const { stdout } = await execCommand("schtasks", ["/query", "/fo", "CSV", "/nh"]);
|
|
115
|
+
const lines = stdout.split("\n").filter((line) => line.includes(TASK_PREFIX));
|
|
116
|
+
for (const line of lines) {
|
|
117
|
+
try {
|
|
118
|
+
const match = line.match(/claude-auto-([^"]+)/);
|
|
119
|
+
if (match) {
|
|
120
|
+
const jobId = match[1];
|
|
121
|
+
const fields = line.split('","');
|
|
122
|
+
const schedule = fields.length > 1 ? fields[1] : "";
|
|
123
|
+
const command = fields.length > 2 ? fields[2]?.replace(/"/g, "") : "";
|
|
124
|
+
jobs.push({
|
|
125
|
+
jobId,
|
|
126
|
+
schedule,
|
|
127
|
+
command
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
return jobs;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
export {
|
|
139
|
+
SchtasksScheduler,
|
|
140
|
+
cronToSchtasks
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=schtasks-4V2IFD3A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/platform/schtasks.ts"],"sourcesContent":["import { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { CronExpressionParser } from \"cron-parser\";\nimport type { JobConfig } from \"../core/types.js\";\nimport { SchedulerError } from \"../util/errors.js\";\nimport { execCommand } from \"../util/exec.js\";\nimport type { RegisteredJob, Scheduler } from \"./scheduler.js\";\n\nconst TASK_PREFIX = \"claude-auto-\";\n\nexport interface SchtasksSchedule {\n\targs: string[];\n\tdescription: string;\n}\n\nconst dayMap = [\"SUN\", \"MON\", \"TUE\", \"WED\", \"THU\", \"FRI\", \"SAT\"];\n\n/**\n * Translate a 5-field cron expression into schtasks /SC schedule parameters.\n *\n * Supports:\n * - Every N minutes (e.g., *\\/30 * * * *)\n * - Every N hours at minute M (e.g., 0 *\\/6 * * *)\n * - Daily at specific time (e.g., 0 9 * * *)\n * - Specific days of week (e.g., 0 9 * * 1-5)\n * - Monthly on a single day (e.g., 0 9 15 * *)\n *\n * Throws SchedulerError for patterns that cannot be represented as a single\n * Windows Task Scheduler entry.\n */\nexport function cronToSchtasks(cronExpr: string): SchtasksSchedule {\n\tconst interval = CronExpressionParser.parse(cronExpr);\n\tconst fields = interval.fields;\n\n\tconst minutes = [...fields.minute.values].map(Number);\n\tconst hours = [...fields.hour.values].map(Number);\n\tconst daysOfMonth = [...fields.dayOfMonth.values].map(Number);\n\tconst months = [...fields.month.values].map(Number);\n\tconst daysOfWeek = [...fields.dayOfWeek.values].map(Number);\n\n\tconst isAllHours = hours.length === 24;\n\tconst isAllDays = daysOfMonth.length === 31;\n\tconst isAllMonths = months.length === 12;\n\t// cron-parser returns 0-7 for dayOfWeek (0 and 7 both mean Sunday)\n\tconst isAllDow = daysOfWeek.length === 8;\n\n\t// Pattern: every N minutes (e.g., */30 * * * *)\n\tif (isAllHours && isAllDays && isAllMonths && isAllDow && minutes.length > 1) {\n\t\tconst step = minutes[1] - minutes[0];\n\t\tconst isEvenStep = minutes.every((m, i) => i === 0 || m - minutes[i - 1] === step);\n\t\tif (isEvenStep) {\n\t\t\treturn {\n\t\t\t\targs: [\"/sc\", \"MINUTE\", \"/mo\", String(step)],\n\t\t\t\tdescription: `every ${step} minutes`,\n\t\t\t};\n\t\t}\n\t}\n\n\t// Pattern: every N hours at minute M (e.g., 0 */6 * * *)\n\tif (isAllDays && isAllMonths && isAllDow && minutes.length === 1 && hours.length > 1) {\n\t\tconst step = hours[1] - hours[0];\n\t\tconst isEvenStep = hours.every((h, i) => i === 0 || h - hours[i - 1] === step);\n\t\tif (isEvenStep) {\n\t\t\tconst st = `${String(hours[0]).padStart(2, \"0\")}:${String(minutes[0]).padStart(2, \"0\")}`;\n\t\t\treturn {\n\t\t\t\targs: [\"/sc\", \"HOURLY\", \"/mo\", String(step), \"/st\", st],\n\t\t\t\tdescription: `every ${step} hours starting at ${st}`,\n\t\t\t};\n\t\t}\n\t}\n\n\t// Pattern: daily at specific time (e.g., 0 9 * * *)\n\tif (isAllDays && isAllMonths && isAllDow && minutes.length === 1 && hours.length === 1) {\n\t\tconst st = `${String(hours[0]).padStart(2, \"0\")}:${String(minutes[0]).padStart(2, \"0\")}`;\n\t\treturn {\n\t\t\targs: [\"/sc\", \"DAILY\", \"/st\", st],\n\t\t\tdescription: `daily at ${st}`,\n\t\t};\n\t}\n\n\t// Pattern: specific days of week (e.g., 0 9 * * 1-5)\n\tif (isAllDays && isAllMonths && !isAllDow && minutes.length === 1 && hours.length === 1) {\n\t\tconst days = daysOfWeek.filter((d) => d <= 6).map((d) => dayMap[d]);\n\t\tconst st = `${String(hours[0]).padStart(2, \"0\")}:${String(minutes[0]).padStart(2, \"0\")}`;\n\t\treturn {\n\t\t\targs: [\"/sc\", \"WEEKLY\", \"/d\", days.join(\",\"), \"/st\", st],\n\t\t\tdescription: `weekly on ${days.join(\",\")} at ${st}`,\n\t\t};\n\t}\n\n\t// Pattern: specific day of month (e.g., 0 9 15 * *)\n\tif (\n\t\t!isAllDays &&\n\t\tisAllMonths &&\n\t\tisAllDow &&\n\t\tminutes.length === 1 &&\n\t\thours.length === 1 &&\n\t\tdaysOfMonth.length === 1\n\t) {\n\t\tconst st = `${String(hours[0]).padStart(2, \"0\")}:${String(minutes[0]).padStart(2, \"0\")}`;\n\t\treturn {\n\t\t\targs: [\"/sc\", \"MONTHLY\", \"/d\", String(daysOfMonth[0]), \"/st\", st],\n\t\t\tdescription: `monthly on day ${daysOfMonth[0]} at ${st}`,\n\t\t};\n\t}\n\n\t// Unsupported pattern\n\tthrow new SchedulerError(\n\t\t\"win32\",\n\t\t`Cron expression \"${cronExpr}\" cannot be represented as a single Windows Task Scheduler entry. ` +\n\t\t\t\"Simplify the schedule (e.g., daily at a specific time, every N minutes, or specific weekdays).\",\n\t);\n}\n\n/**\n * Resolve the runner script path relative to this module.\n */\nfunction getRunnerPath(): string {\n\ttry {\n\t\tconst currentDir = dirname(fileURLToPath(import.meta.url));\n\t\treturn join(currentDir, \"..\", \"..\", \"dist\", \"claude-auto-run.js\");\n\t} catch {\n\t\t// Fallback for test/bundle environments\n\t\treturn join(process.cwd(), \"dist\", \"claude-auto-run.js\");\n\t}\n}\n\n/**\n * SchtasksScheduler implements the Scheduler interface for Windows systems.\n * Uses `schtasks.exe` to create, query, and delete tasks in Windows Task Scheduler.\n *\n * Task name format: `claude-auto-{jobId}`\n */\nexport class SchtasksScheduler implements Scheduler {\n\tasync register(job: JobConfig, _env?: Record<string, string>): Promise<void> {\n\t\tconst registered = await this.isRegistered(job.id);\n\t\tif (registered) {\n\t\t\tthrow new SchedulerError(\"win32\", `Job \"${job.id}\" is already registered`);\n\t\t}\n\n\t\tconst schedule = cronToSchtasks(job.schedule.cron);\n\t\tconst runnerPath = getRunnerPath();\n\t\tconst command = `\"${process.execPath}\" \"${runnerPath}\" --job-id ${job.id}`;\n\t\tconst taskName = `${TASK_PREFIX}${job.id}`;\n\n\t\tconst args = [\"/create\", \"/tn\", taskName, \"/tr\", command, ...schedule.args, \"/f\"];\n\n\t\tawait execCommand(\"schtasks\", args);\n\t}\n\n\tasync unregister(jobId: string): Promise<void> {\n\t\tconst taskName = `${TASK_PREFIX}${jobId}`;\n\t\ttry {\n\t\t\tawait execCommand(\"schtasks\", [\"/delete\", \"/tn\", taskName, \"/f\"]);\n\t\t} catch {\n\t\t\t// Task may not exist -- continue gracefully (same pattern as launchd bootout)\n\t\t}\n\t}\n\n\tasync isRegistered(jobId: string): Promise<boolean> {\n\t\tconst taskName = `${TASK_PREFIX}${jobId}`;\n\t\ttry {\n\t\t\tawait execCommand(\"schtasks\", [\"/query\", \"/tn\", taskName, \"/fo\", \"CSV\", \"/nh\"]);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync list(): Promise<RegisteredJob[]> {\n\t\tconst jobs: RegisteredJob[] = [];\n\t\ttry {\n\t\t\tconst { stdout } = await execCommand(\"schtasks\", [\"/query\", \"/fo\", \"CSV\", \"/nh\"]);\n\t\t\tconst lines = stdout.split(\"\\n\").filter((line) => line.includes(TASK_PREFIX));\n\n\t\t\tfor (const line of lines) {\n\t\t\t\ttry {\n\t\t\t\t\t// CSV format: \"TaskName\",\"Next Run Time\",\"Status\"\n\t\t\t\t\t// Task names may include path prefix like \\claude-auto-jobId\n\t\t\t\t\tconst match = line.match(/claude-auto-([^\"]+)/);\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tconst jobId = match[1];\n\t\t\t\t\t\t// Extract schedule and status from CSV fields\n\t\t\t\t\t\tconst fields = line.split('\",\"');\n\t\t\t\t\t\tconst schedule = fields.length > 1 ? fields[1] : \"\";\n\t\t\t\t\t\tconst command = fields.length > 2 ? fields[2]?.replace(/\"/g, \"\") : \"\";\n\n\t\t\t\t\t\tjobs.push({\n\t\t\t\t\t\t\tjobId,\n\t\t\t\t\t\t\tschedule,\n\t\t\t\t\t\t\tcommand,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed lines\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// No tasks found or schtasks not available\n\t\t}\n\t\treturn jobs;\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
execCommand
|
|
3
|
+
} from "./chunk-3NEANSUS.js";
|
|
4
|
+
import {
|
|
5
|
+
SchedulerError
|
|
6
|
+
} from "./chunk-AWLSYOVF.js";
|
|
7
|
+
|
|
8
|
+
// src/platform/schtasks.ts
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import { CronExpressionParser } from "cron-parser";
|
|
12
|
+
var TASK_PREFIX = "claude-auto-";
|
|
13
|
+
var dayMap = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
14
|
+
function cronToSchtasks(cronExpr) {
|
|
15
|
+
const interval = CronExpressionParser.parse(cronExpr);
|
|
16
|
+
const fields = interval.fields;
|
|
17
|
+
const minutes = [...fields.minute.values].map(Number);
|
|
18
|
+
const hours = [...fields.hour.values].map(Number);
|
|
19
|
+
const daysOfMonth = [...fields.dayOfMonth.values].map(Number);
|
|
20
|
+
const months = [...fields.month.values].map(Number);
|
|
21
|
+
const daysOfWeek = [...fields.dayOfWeek.values].map(Number);
|
|
22
|
+
const isAllHours = hours.length === 24;
|
|
23
|
+
const isAllDays = daysOfMonth.length === 31;
|
|
24
|
+
const isAllMonths = months.length === 12;
|
|
25
|
+
const isAllDow = daysOfWeek.length === 8;
|
|
26
|
+
if (isAllHours && isAllDays && isAllMonths && isAllDow && minutes.length > 1) {
|
|
27
|
+
const step = minutes[1] - minutes[0];
|
|
28
|
+
const isEvenStep = minutes.every((m, i) => i === 0 || m - minutes[i - 1] === step);
|
|
29
|
+
if (isEvenStep) {
|
|
30
|
+
return {
|
|
31
|
+
args: ["/sc", "MINUTE", "/mo", String(step)],
|
|
32
|
+
description: `every ${step} minutes`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (isAllDays && isAllMonths && isAllDow && minutes.length === 1 && hours.length > 1) {
|
|
37
|
+
const step = hours[1] - hours[0];
|
|
38
|
+
const isEvenStep = hours.every((h, i) => i === 0 || h - hours[i - 1] === step);
|
|
39
|
+
if (isEvenStep) {
|
|
40
|
+
const st = `${String(hours[0]).padStart(2, "0")}:${String(minutes[0]).padStart(2, "0")}`;
|
|
41
|
+
return {
|
|
42
|
+
args: ["/sc", "HOURLY", "/mo", String(step), "/st", st],
|
|
43
|
+
description: `every ${step} hours starting at ${st}`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (isAllDays && isAllMonths && isAllDow && minutes.length === 1 && hours.length === 1) {
|
|
48
|
+
const st = `${String(hours[0]).padStart(2, "0")}:${String(minutes[0]).padStart(2, "0")}`;
|
|
49
|
+
return {
|
|
50
|
+
args: ["/sc", "DAILY", "/st", st],
|
|
51
|
+
description: `daily at ${st}`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (isAllDays && isAllMonths && !isAllDow && minutes.length === 1 && hours.length === 1) {
|
|
55
|
+
const days = daysOfWeek.filter((d) => d <= 6).map((d) => dayMap[d]);
|
|
56
|
+
const st = `${String(hours[0]).padStart(2, "0")}:${String(minutes[0]).padStart(2, "0")}`;
|
|
57
|
+
return {
|
|
58
|
+
args: ["/sc", "WEEKLY", "/d", days.join(","), "/st", st],
|
|
59
|
+
description: `weekly on ${days.join(",")} at ${st}`
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (!isAllDays && isAllMonths && isAllDow && minutes.length === 1 && hours.length === 1 && daysOfMonth.length === 1) {
|
|
63
|
+
const st = `${String(hours[0]).padStart(2, "0")}:${String(minutes[0]).padStart(2, "0")}`;
|
|
64
|
+
return {
|
|
65
|
+
args: ["/sc", "MONTHLY", "/d", String(daysOfMonth[0]), "/st", st],
|
|
66
|
+
description: `monthly on day ${daysOfMonth[0]} at ${st}`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
throw new SchedulerError(
|
|
70
|
+
"win32",
|
|
71
|
+
`Cron expression "${cronExpr}" cannot be represented as a single Windows Task Scheduler entry. Simplify the schedule (e.g., daily at a specific time, every N minutes, or specific weekdays).`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
function getRunnerPath() {
|
|
75
|
+
try {
|
|
76
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
77
|
+
return join(currentDir, "..", "..", "dist", "claude-auto-run.js");
|
|
78
|
+
} catch {
|
|
79
|
+
return join(process.cwd(), "dist", "claude-auto-run.js");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
var SchtasksScheduler = class {
|
|
83
|
+
async register(job, _env) {
|
|
84
|
+
const registered = await this.isRegistered(job.id);
|
|
85
|
+
if (registered) {
|
|
86
|
+
throw new SchedulerError("win32", `Job "${job.id}" is already registered`);
|
|
87
|
+
}
|
|
88
|
+
const schedule = cronToSchtasks(job.schedule.cron);
|
|
89
|
+
const runnerPath = getRunnerPath();
|
|
90
|
+
const command = `"${process.execPath}" "${runnerPath}" --job-id ${job.id}`;
|
|
91
|
+
const taskName = `${TASK_PREFIX}${job.id}`;
|
|
92
|
+
const args = ["/create", "/tn", taskName, "/tr", command, ...schedule.args, "/f"];
|
|
93
|
+
await execCommand("schtasks", args);
|
|
94
|
+
}
|
|
95
|
+
async unregister(jobId) {
|
|
96
|
+
const taskName = `${TASK_PREFIX}${jobId}`;
|
|
97
|
+
try {
|
|
98
|
+
await execCommand("schtasks", ["/delete", "/tn", taskName, "/f"]);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async isRegistered(jobId) {
|
|
103
|
+
const taskName = `${TASK_PREFIX}${jobId}`;
|
|
104
|
+
try {
|
|
105
|
+
await execCommand("schtasks", ["/query", "/tn", taskName, "/fo", "CSV", "/nh"]);
|
|
106
|
+
return true;
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async list() {
|
|
112
|
+
const jobs = [];
|
|
113
|
+
try {
|
|
114
|
+
const { stdout } = await execCommand("schtasks", ["/query", "/fo", "CSV", "/nh"]);
|
|
115
|
+
const lines = stdout.split("\n").filter((line) => line.includes(TASK_PREFIX));
|
|
116
|
+
for (const line of lines) {
|
|
117
|
+
try {
|
|
118
|
+
const match = line.match(/claude-auto-([^"]+)/);
|
|
119
|
+
if (match) {
|
|
120
|
+
const jobId = match[1];
|
|
121
|
+
const fields = line.split('","');
|
|
122
|
+
const schedule = fields.length > 1 ? fields[1] : "";
|
|
123
|
+
const command = fields.length > 2 ? fields[2]?.replace(/"/g, "") : "";
|
|
124
|
+
jobs.push({
|
|
125
|
+
jobId,
|
|
126
|
+
schedule,
|
|
127
|
+
command
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
return jobs;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
export {
|
|
139
|
+
SchtasksScheduler,
|
|
140
|
+
cronToSchtasks
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=schtasks-JGEPEKQS.js.map
|