cron-claude 1.0.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/LICENSE +21 -0
- package/README.md +260 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +107 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +69 -0
- package/dist/config.js +96 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon.d.ts +13 -0
- package/dist/daemon.js +95 -0
- package/dist/daemon.js.map +1 -0
- package/dist/mcp-server.d.ts +34 -0
- package/dist/mcp-server.js +181 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/scheduler.d.ts +19 -0
- package/dist/scheduler.js +87 -0
- package/dist/scheduler.js.map +1 -0
- package/install.sh +55 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tygi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# cron-claude
|
|
2
|
+
|
|
3
|
+
Schedule any Claude prompt to run automatically on a cron. Add a job, set a schedule, forget about it.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/tarsbotty/cron-claude/actions/workflows/ci.yml)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## What it does
|
|
11
|
+
|
|
12
|
+
`cron-claude` is a lightweight daemon that runs `claude` CLI commands on a schedule. Define a prompt, set a cron expression, and the daemon fires it automatically — logging output to `~/.cron-claude/logs/`. It runs as a macOS LaunchAgent so it survives reboots, and it reloads jobs live when you edit the config. No server to babysit, no web UI, just prompts on a schedule.
|
|
13
|
+
|
|
14
|
+
You can manage jobs from the CLI, from Claude Desktop or Claude Code via the bundled MCP server, or from Claude chat via the OpenClaw plugin.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why not just use OpenClaw?
|
|
19
|
+
|
|
20
|
+
OpenClaw's scheduled agentic tasks are genuinely powerful — prompts that run on a timer, check things, take actions, and report back. But OpenClaw is a full personal AI platform with broad system access: files, email, calendar, browser, messaging. That's the point of it, but it's also a wide attack surface.
|
|
21
|
+
|
|
22
|
+
`cron-claude` is just that one feature. Each job is a single `claude` invocation with a prompt you wrote — the blast radius is scoped to what that one call can do, on your terms, with no credentials stored in a platform. If you want scheduled agentic tasks without the overhead of a full AI stack, this is the trade-off.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g cron-claude
|
|
30
|
+
cron-claude install-service # sets up macOS LaunchAgent (auto-starts on login)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Requirements:** Node.js 22+, `claude` CLI installed and authenticated (`~/.local/bin/claude`).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Add a job that runs daily at 9 AM
|
|
41
|
+
cron-claude add \
|
|
42
|
+
--id morning-brief \
|
|
43
|
+
--schedule "0 9 * * *" \
|
|
44
|
+
--prompt "Check my email inbox for anything urgent and summarize it."
|
|
45
|
+
|
|
46
|
+
# Confirm it's scheduled
|
|
47
|
+
cron-claude list
|
|
48
|
+
# ID Schedule Model Enabled Prompt
|
|
49
|
+
# --------------------------------------------------------------------------------
|
|
50
|
+
# morning-brief 0 9 * * * sonnet true Check my email inbox for anything urgent...
|
|
51
|
+
|
|
52
|
+
# Check overall status
|
|
53
|
+
cron-claude status
|
|
54
|
+
# Cron-Claude Status
|
|
55
|
+
# Total jobs: 1
|
|
56
|
+
# Enabled: 1
|
|
57
|
+
# Disabled: 0
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## CLI Reference
|
|
63
|
+
|
|
64
|
+
### `add`
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cron-claude add --id <id> --schedule "<cron>" --prompt "<prompt>" [--model <model>] [--disabled]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
| Flag | Required | Default | Description |
|
|
71
|
+
|------|----------|---------|-------------|
|
|
72
|
+
| `--id` | ✅ | — | Unique job identifier |
|
|
73
|
+
| `--schedule` | ✅ | — | Cron expression (5-field) |
|
|
74
|
+
| `--prompt` | ✅ | — | Prompt sent to `claude` |
|
|
75
|
+
| `--model` | | `sonnet` | Claude model (`sonnet`, `opus`, `haiku`) |
|
|
76
|
+
| `--disabled` | | `false` | Create job in disabled state |
|
|
77
|
+
|
|
78
|
+
**Examples:**
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Daily at 2 PM
|
|
82
|
+
cron-claude add --id daily-summary --schedule "0 14 * * *" --prompt "Summarize my day and post to Discord"
|
|
83
|
+
|
|
84
|
+
# Every Monday at 9 AM with Opus
|
|
85
|
+
cron-claude add --id weekly-review --schedule "0 9 * * 1" --prompt "Review open GitHub issues and write a priority list" --model opus
|
|
86
|
+
|
|
87
|
+
# Add but keep disabled until ready
|
|
88
|
+
cron-claude add --id nightly-report --schedule "0 23 * * *" --prompt "Generate nightly stats" --disabled
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### `list`
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cron-claude list
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Prints a table of all configured jobs: ID, schedule, model, enabled state, and prompt preview.
|
|
98
|
+
|
|
99
|
+
### `remove`
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
cron-claude remove <id>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Removes the job from `jobs.json`. The daemon reloads automatically.
|
|
106
|
+
|
|
107
|
+
### `enable` / `disable`
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
cron-claude enable <id>
|
|
111
|
+
cron-claude disable <id>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Toggle a job on or off without removing it.
|
|
115
|
+
|
|
116
|
+
### `status`
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
cron-claude status
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Shows total, enabled, and disabled job counts.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## MCP Server (Claude Desktop / Claude Code)
|
|
127
|
+
|
|
128
|
+
`cron-claude` ships an [MCP](https://modelcontextprotocol.io) server so you can manage jobs directly from Claude Desktop, Claude Code, or any MCP client — no terminal needed.
|
|
129
|
+
|
|
130
|
+
### Claude Desktop
|
|
131
|
+
|
|
132
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"mcpServers": {
|
|
137
|
+
"cron-claude": {
|
|
138
|
+
"command": "cron-claude-mcp"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Restart Claude Desktop. You can then say *"add a cron job that runs every morning and checks the weather"* and it'll use the tools directly.
|
|
145
|
+
|
|
146
|
+
### Claude Code
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
claude mcp add cron-claude -- cron-claude-mcp
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Or add to `.mcp.json` manually:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"mcpServers": {
|
|
157
|
+
"cron-claude": {
|
|
158
|
+
"command": "cron-claude-mcp"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Available MCP Tools
|
|
165
|
+
|
|
166
|
+
| Tool | Description | Parameters |
|
|
167
|
+
|------|-------------|------------|
|
|
168
|
+
| `add_cron` | Add a new scheduled job | `id`, `schedule`, `prompt`, `model?`, `disabled?` |
|
|
169
|
+
| `list_crons` | List all configured jobs | — |
|
|
170
|
+
| `remove_cron` | Remove a job by ID | `id` |
|
|
171
|
+
| `enable_cron` | Enable a disabled job | `id` |
|
|
172
|
+
| `disable_cron` | Disable a job without removing it | `id` |
|
|
173
|
+
| `get_status` | Daemon status + job counts | — |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Claude Code Plugin
|
|
178
|
+
|
|
179
|
+
Install `cron-claude` from the Claude Code plugin marketplace:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
claude plugin marketplace add github:tarsbotty/cron-claude
|
|
183
|
+
claude plugin install cron-claude
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Claude Code will automatically connect to the `cron-claude` MCP server, exposing all management tools without leaving your editor.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## OpenClaw Skill
|
|
191
|
+
|
|
192
|
+
For OpenClaw users, the skill in `src/plugin/` exposes natural-language cron management via `SKILL.md`. Copy `src/plugin/` into your OpenClaw skills directory or reference it in your OpenClaw config.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Config
|
|
197
|
+
|
|
198
|
+
Jobs are stored in `~/.cron-claude/jobs.json`. The daemon watches this file and reloads automatically on any change — no restart required.
|
|
199
|
+
|
|
200
|
+
**Example `jobs.json`:**
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"jobs": [
|
|
205
|
+
{
|
|
206
|
+
"id": "morning-brief",
|
|
207
|
+
"schedule": "0 9 * * *",
|
|
208
|
+
"prompt": "Check my email inbox for anything urgent and summarize it.",
|
|
209
|
+
"model": "sonnet",
|
|
210
|
+
"enabled": true
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"id": "weekly-review",
|
|
214
|
+
"schedule": "0 9 * * 1",
|
|
215
|
+
"prompt": "Review open GitHub issues and write a priority list.",
|
|
216
|
+
"model": "opus",
|
|
217
|
+
"enabled": true
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Cron Syntax
|
|
224
|
+
|
|
225
|
+
Standard 5-field format: `minute hour day-of-month month day-of-week`
|
|
226
|
+
|
|
227
|
+
| Expression | Meaning |
|
|
228
|
+
|------------|---------|
|
|
229
|
+
| `0 9 * * *` | Daily at 9:00 AM |
|
|
230
|
+
| `0 14 * * *` | Daily at 2:00 PM |
|
|
231
|
+
| `*/30 * * * *` | Every 30 minutes |
|
|
232
|
+
| `0 9 * * 1` | Mondays at 9:00 AM |
|
|
233
|
+
| `0 0 1 * *` | First of each month at midnight |
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## How it works
|
|
238
|
+
|
|
239
|
+
A macOS LaunchAgent keeps the `cron-claude` daemon alive across reboots. On startup, the daemon loads `~/.cron-claude/jobs.json`, validates each job with [Zod](https://zod.dev), and registers schedules using [node-cron](https://github.com/node-cron/node-cron). When a job fires, the daemon calls `execFile` on the `claude` CLI with `--print --model <model> <prompt>` and a 5-minute timeout. Output (or any error) is written to a timestamped log file in `~/.cron-claude/logs/`. The config file is watched via `fs.watch`; any edit triggers a debounced reload so you never need to restart the daemon manually.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Development
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
git clone git@github.com:tarsbotty/cron-claude.git
|
|
247
|
+
cd cron-claude
|
|
248
|
+
npm install
|
|
249
|
+
npm run build # compile TypeScript → dist/
|
|
250
|
+
npm test # run vitest with coverage (90% threshold enforced)
|
|
251
|
+
npm run dev # run daemon directly with tsx (no build step)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Test suite:** `tests/` covers the config layer, scheduler, daemon, CLI, and MCP server handlers. Coverage thresholds are enforced in `vitest.config.ts` (statements/functions/lines: 90%, branches: 85%).
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { addJob, removeJob, listJobs, toggleJob, JobSchema } from './config.js';
|
|
4
|
+
const program = new Command();
|
|
5
|
+
program
|
|
6
|
+
.name('cron-claude')
|
|
7
|
+
.description('Manage scheduled Claude CLI jobs')
|
|
8
|
+
.version('1.0.0');
|
|
9
|
+
program
|
|
10
|
+
.command('list')
|
|
11
|
+
.description('List all scheduled jobs')
|
|
12
|
+
.action(() => {
|
|
13
|
+
const jobs = listJobs();
|
|
14
|
+
if (jobs.length === 0) {
|
|
15
|
+
console.log('No jobs configured.');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
console.log(`\n${'ID'.padEnd(20)} ${'Schedule'.padEnd(15)} ${'Model'.padEnd(10)} ${'Enabled'.padEnd(8)} Prompt`);
|
|
19
|
+
console.log('-'.repeat(80));
|
|
20
|
+
for (const job of jobs) {
|
|
21
|
+
console.log(`${job.id.padEnd(20)} ${job.schedule.padEnd(15)} ${job.model.padEnd(10)} ${String(job.enabled).padEnd(8)} ${job.prompt.slice(0, 40)}`);
|
|
22
|
+
}
|
|
23
|
+
console.log('');
|
|
24
|
+
});
|
|
25
|
+
program
|
|
26
|
+
.command('add')
|
|
27
|
+
.description('Add a new scheduled job')
|
|
28
|
+
.requiredOption('--id <id>', 'Unique job identifier')
|
|
29
|
+
.requiredOption('--schedule <cron>', 'Cron schedule expression')
|
|
30
|
+
.requiredOption('--prompt <prompt>', 'Prompt to send to Claude')
|
|
31
|
+
.option('--model <model>', 'Claude model to use', 'sonnet')
|
|
32
|
+
.option('--disabled', 'Create job in disabled state')
|
|
33
|
+
.action((opts) => {
|
|
34
|
+
try {
|
|
35
|
+
const job = JobSchema.parse({
|
|
36
|
+
id: opts.id,
|
|
37
|
+
schedule: opts.schedule,
|
|
38
|
+
prompt: opts.prompt,
|
|
39
|
+
model: opts.model,
|
|
40
|
+
enabled: !opts.disabled,
|
|
41
|
+
});
|
|
42
|
+
addJob(job);
|
|
43
|
+
console.log(`Job "${job.id}" added successfully.`);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
console.error(`Error: ${err.message}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
program
|
|
51
|
+
.command('remove <id>')
|
|
52
|
+
.description('Remove a scheduled job')
|
|
53
|
+
.action((id) => {
|
|
54
|
+
try {
|
|
55
|
+
removeJob(id);
|
|
56
|
+
console.log(`Job "${id}" removed.`);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
console.error(`Error: ${err.message}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
program
|
|
64
|
+
.command('enable <id>')
|
|
65
|
+
.description('Enable a job')
|
|
66
|
+
.action((id) => {
|
|
67
|
+
try {
|
|
68
|
+
toggleJob(id, true);
|
|
69
|
+
console.log(`Job "${id}" enabled.`);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
console.error(`Error: ${err.message}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
program
|
|
77
|
+
.command('disable <id>')
|
|
78
|
+
.description('Disable a job')
|
|
79
|
+
.action((id) => {
|
|
80
|
+
try {
|
|
81
|
+
toggleJob(id, false);
|
|
82
|
+
console.log(`Job "${id}" disabled.`);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
console.error(`Error: ${err.message}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
program
|
|
90
|
+
.command('status')
|
|
91
|
+
.description('Show daemon status')
|
|
92
|
+
.action(() => {
|
|
93
|
+
const jobs = listJobs();
|
|
94
|
+
const enabled = jobs.filter((j) => j.enabled).length;
|
|
95
|
+
const disabled = jobs.filter((j) => !j.enabled).length;
|
|
96
|
+
console.log(`\nCron-Claude Status`);
|
|
97
|
+
console.log(` Total jobs: ${jobs.length}`);
|
|
98
|
+
console.log(` Enabled: ${enabled}`);
|
|
99
|
+
console.log(` Disabled: ${disabled}\n`);
|
|
100
|
+
});
|
|
101
|
+
export { program };
|
|
102
|
+
// Run if executed directly
|
|
103
|
+
const isMain = process.argv[1]?.endsWith('cli.ts') || process.argv[1]?.endsWith('cli.js');
|
|
104
|
+
if (isMain) {
|
|
105
|
+
program.parse();
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const JobSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodString;
|
|
4
|
+
schedule: z.ZodString;
|
|
5
|
+
prompt: z.ZodString;
|
|
6
|
+
model: z.ZodDefault<z.ZodString>;
|
|
7
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
id: string;
|
|
10
|
+
schedule: string;
|
|
11
|
+
prompt: string;
|
|
12
|
+
model: string;
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
}, {
|
|
15
|
+
id: string;
|
|
16
|
+
schedule: string;
|
|
17
|
+
prompt: string;
|
|
18
|
+
model?: string | undefined;
|
|
19
|
+
enabled?: boolean | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
22
|
+
jobs: z.ZodArray<z.ZodObject<{
|
|
23
|
+
id: z.ZodString;
|
|
24
|
+
schedule: z.ZodString;
|
|
25
|
+
prompt: z.ZodString;
|
|
26
|
+
model: z.ZodDefault<z.ZodString>;
|
|
27
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
id: string;
|
|
30
|
+
schedule: string;
|
|
31
|
+
prompt: string;
|
|
32
|
+
model: string;
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
}, {
|
|
35
|
+
id: string;
|
|
36
|
+
schedule: string;
|
|
37
|
+
prompt: string;
|
|
38
|
+
model?: string | undefined;
|
|
39
|
+
enabled?: boolean | undefined;
|
|
40
|
+
}>, "many">;
|
|
41
|
+
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
jobs: {
|
|
43
|
+
id: string;
|
|
44
|
+
schedule: string;
|
|
45
|
+
prompt: string;
|
|
46
|
+
model: string;
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
}[];
|
|
49
|
+
}, {
|
|
50
|
+
jobs: {
|
|
51
|
+
id: string;
|
|
52
|
+
schedule: string;
|
|
53
|
+
prompt: string;
|
|
54
|
+
model?: string | undefined;
|
|
55
|
+
enabled?: boolean | undefined;
|
|
56
|
+
}[];
|
|
57
|
+
}>;
|
|
58
|
+
export type Job = z.infer<typeof JobSchema>;
|
|
59
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
60
|
+
export declare function getConfigDir(): string;
|
|
61
|
+
export declare function getConfigPath(): string;
|
|
62
|
+
export declare function getLogsDir(): string;
|
|
63
|
+
export declare function ensureConfigDir(): void;
|
|
64
|
+
export declare function loadConfig(configPath?: string): Config;
|
|
65
|
+
export declare function saveConfig(config: Config, configPath?: string): void;
|
|
66
|
+
export declare function addJob(job: Job, configPath?: string): Config;
|
|
67
|
+
export declare function removeJob(jobId: string, configPath?: string): Config;
|
|
68
|
+
export declare function listJobs(configPath?: string): Job[];
|
|
69
|
+
export declare function toggleJob(jobId: string, enabled: boolean, configPath?: string): Config;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export const JobSchema = z.object({
|
|
5
|
+
id: z.string().min(1),
|
|
6
|
+
schedule: z.string().min(1),
|
|
7
|
+
prompt: z.string().min(1),
|
|
8
|
+
model: z.string().default('sonnet'),
|
|
9
|
+
enabled: z.boolean().default(true),
|
|
10
|
+
});
|
|
11
|
+
export const ConfigSchema = z.object({
|
|
12
|
+
jobs: z.array(JobSchema),
|
|
13
|
+
});
|
|
14
|
+
const CONFIG_DIR = path.join(process.env.HOME || '~', '.cron-claude');
|
|
15
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'jobs.json');
|
|
16
|
+
const LOGS_DIR = path.join(CONFIG_DIR, 'logs');
|
|
17
|
+
export function getConfigDir() {
|
|
18
|
+
return CONFIG_DIR;
|
|
19
|
+
}
|
|
20
|
+
export function getConfigPath() {
|
|
21
|
+
return CONFIG_FILE;
|
|
22
|
+
}
|
|
23
|
+
export function getLogsDir() {
|
|
24
|
+
return LOGS_DIR;
|
|
25
|
+
}
|
|
26
|
+
const EXAMPLE_CONFIG = {
|
|
27
|
+
jobs: [
|
|
28
|
+
{
|
|
29
|
+
id: 'daily-summary',
|
|
30
|
+
schedule: '0 14 * * *',
|
|
31
|
+
prompt: 'Summarize my day and send to Discord',
|
|
32
|
+
model: 'sonnet',
|
|
33
|
+
enabled: true,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
export function ensureConfigDir() {
|
|
38
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
39
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
if (!fs.existsSync(LOGS_DIR)) {
|
|
42
|
+
fs.mkdirSync(LOGS_DIR, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function loadConfig(configPath) {
|
|
46
|
+
const filePath = configPath ?? CONFIG_FILE;
|
|
47
|
+
ensureConfigDir();
|
|
48
|
+
if (!fs.existsSync(filePath)) {
|
|
49
|
+
const config = EXAMPLE_CONFIG;
|
|
50
|
+
fs.writeFileSync(filePath, JSON.stringify(config, null, 2), 'utf-8');
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
53
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
54
|
+
const parsed = JSON.parse(raw);
|
|
55
|
+
return ConfigSchema.parse(parsed);
|
|
56
|
+
}
|
|
57
|
+
export function saveConfig(config, configPath) {
|
|
58
|
+
const filePath = configPath ?? CONFIG_FILE;
|
|
59
|
+
ensureConfigDir();
|
|
60
|
+
ConfigSchema.parse(config);
|
|
61
|
+
fs.writeFileSync(filePath, JSON.stringify(config, null, 2), 'utf-8');
|
|
62
|
+
}
|
|
63
|
+
export function addJob(job, configPath) {
|
|
64
|
+
const config = loadConfig(configPath);
|
|
65
|
+
if (config.jobs.some((j) => j.id === job.id)) {
|
|
66
|
+
throw new Error(`Job with id "${job.id}" already exists`);
|
|
67
|
+
}
|
|
68
|
+
config.jobs.push(job);
|
|
69
|
+
saveConfig(config, configPath);
|
|
70
|
+
return config;
|
|
71
|
+
}
|
|
72
|
+
export function removeJob(jobId, configPath) {
|
|
73
|
+
const config = loadConfig(configPath);
|
|
74
|
+
const idx = config.jobs.findIndex((j) => j.id === jobId);
|
|
75
|
+
if (idx === -1) {
|
|
76
|
+
throw new Error(`Job with id "${jobId}" not found`);
|
|
77
|
+
}
|
|
78
|
+
config.jobs.splice(idx, 1);
|
|
79
|
+
saveConfig(config, configPath);
|
|
80
|
+
return config;
|
|
81
|
+
}
|
|
82
|
+
export function listJobs(configPath) {
|
|
83
|
+
const config = loadConfig(configPath);
|
|
84
|
+
return config.jobs;
|
|
85
|
+
}
|
|
86
|
+
export function toggleJob(jobId, enabled, configPath) {
|
|
87
|
+
const config = loadConfig(configPath);
|
|
88
|
+
const job = config.jobs.find((j) => j.id === jobId);
|
|
89
|
+
if (!job) {
|
|
90
|
+
throw new Error(`Job with id "${jobId}" not found`);
|
|
91
|
+
}
|
|
92
|
+
job.enabled = enabled;
|
|
93
|
+
saveConfig(config, configPath);
|
|
94
|
+
return config;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Scheduler } from './scheduler.js';
|
|
2
|
+
export declare class Daemon {
|
|
3
|
+
private scheduler;
|
|
4
|
+
private watcher;
|
|
5
|
+
private configPath;
|
|
6
|
+
constructor(configPath?: string);
|
|
7
|
+
start(): void;
|
|
8
|
+
stop(): void;
|
|
9
|
+
getScheduler(): Scheduler;
|
|
10
|
+
private loadAndSchedule;
|
|
11
|
+
private watchConfig;
|
|
12
|
+
}
|
|
13
|
+
export declare function main(): void;
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { loadConfig, getConfigPath, ensureConfigDir } from './config.js';
|
|
3
|
+
import { Scheduler } from './scheduler.js';
|
|
4
|
+
export class Daemon {
|
|
5
|
+
scheduler;
|
|
6
|
+
watcher = null;
|
|
7
|
+
configPath;
|
|
8
|
+
constructor(configPath) {
|
|
9
|
+
this.configPath = configPath ?? getConfigPath();
|
|
10
|
+
this.scheduler = new Scheduler({
|
|
11
|
+
onJobStart: (job) => {
|
|
12
|
+
console.log(`[cron-claude] Running job: ${job.id}`);
|
|
13
|
+
},
|
|
14
|
+
onJobComplete: (job, output) => {
|
|
15
|
+
console.log(`[cron-claude] Job ${job.id} completed. Output: ${output.slice(0, 200)}`);
|
|
16
|
+
},
|
|
17
|
+
onJobError: (job, error) => {
|
|
18
|
+
console.error(`[cron-claude] Job ${job.id} failed: ${error.message}`);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
start() {
|
|
23
|
+
ensureConfigDir();
|
|
24
|
+
console.log('[cron-claude] Daemon starting...');
|
|
25
|
+
this.loadAndSchedule();
|
|
26
|
+
this.watchConfig();
|
|
27
|
+
console.log('[cron-claude] Daemon running. Watching for config changes.');
|
|
28
|
+
}
|
|
29
|
+
stop() {
|
|
30
|
+
console.log('[cron-claude] Daemon stopping...');
|
|
31
|
+
this.scheduler.unscheduleAll();
|
|
32
|
+
if (this.watcher) {
|
|
33
|
+
this.watcher.close();
|
|
34
|
+
this.watcher = null;
|
|
35
|
+
}
|
|
36
|
+
console.log('[cron-claude] Daemon stopped.');
|
|
37
|
+
}
|
|
38
|
+
getScheduler() {
|
|
39
|
+
return this.scheduler;
|
|
40
|
+
}
|
|
41
|
+
loadAndSchedule() {
|
|
42
|
+
try {
|
|
43
|
+
const config = loadConfig(this.configPath);
|
|
44
|
+
this.scheduler.unscheduleAll();
|
|
45
|
+
for (const job of config.jobs) {
|
|
46
|
+
try {
|
|
47
|
+
this.scheduler.schedule(job);
|
|
48
|
+
console.log(`[cron-claude] Scheduled: ${job.id} (${job.schedule})`);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
console.error(`[cron-claude] Failed to schedule ${job.id}: ${err.message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
console.log(`[cron-claude] ${this.scheduler.getScheduledIds().length} jobs scheduled.`);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error(`[cron-claude] Failed to load config: ${err.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
watchConfig() {
|
|
61
|
+
let debounce = null;
|
|
62
|
+
try {
|
|
63
|
+
this.watcher = fs.watch(this.configPath, () => {
|
|
64
|
+
if (debounce)
|
|
65
|
+
clearTimeout(debounce);
|
|
66
|
+
debounce = setTimeout(() => {
|
|
67
|
+
console.log('[cron-claude] Config changed, reloading...');
|
|
68
|
+
this.loadAndSchedule();
|
|
69
|
+
}, 500);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
console.warn('[cron-claude] Could not watch config file. Changes require restart.');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/* c8 ignore start */
|
|
78
|
+
export function main() {
|
|
79
|
+
const daemon = new Daemon();
|
|
80
|
+
daemon.start();
|
|
81
|
+
process.on('SIGINT', () => {
|
|
82
|
+
daemon.stop();
|
|
83
|
+
process.exit(0);
|
|
84
|
+
});
|
|
85
|
+
process.on('SIGTERM', () => {
|
|
86
|
+
daemon.stop();
|
|
87
|
+
process.exit(0);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const isMain = process.argv[1]?.endsWith('daemon.ts') || process.argv[1]?.endsWith('daemon.js');
|
|
91
|
+
if (isMain) {
|
|
92
|
+
main();
|
|
93
|
+
}
|
|
94
|
+
/* c8 ignore stop */
|
|
95
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP (Model Context Protocol) server for cron-claude.
|
|
4
|
+
* Exposes cron job management as structured tool calls for Claude Desktop,
|
|
5
|
+
* Claude Code, and any MCP-compatible client.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
export type ToolResult = {
|
|
9
|
+
content: Array<{
|
|
10
|
+
type: 'text';
|
|
11
|
+
text: string;
|
|
12
|
+
}>;
|
|
13
|
+
isError?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare function handleAddCron(args: {
|
|
16
|
+
id: string;
|
|
17
|
+
schedule: string;
|
|
18
|
+
prompt: string;
|
|
19
|
+
model?: string;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
}): Promise<ToolResult>;
|
|
22
|
+
export declare function handleListCrons(): Promise<ToolResult>;
|
|
23
|
+
export declare function handleRemoveCron(args: {
|
|
24
|
+
id: string;
|
|
25
|
+
}): Promise<ToolResult>;
|
|
26
|
+
export declare function handleEnableCron(args: {
|
|
27
|
+
id: string;
|
|
28
|
+
}): Promise<ToolResult>;
|
|
29
|
+
export declare function handleDisableCron(args: {
|
|
30
|
+
id: string;
|
|
31
|
+
}): Promise<ToolResult>;
|
|
32
|
+
export declare function handleGetStatus(): Promise<ToolResult>;
|
|
33
|
+
export declare const server: McpServer;
|
|
34
|
+
export declare function main(): Promise<void>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP (Model Context Protocol) server for cron-claude.
|
|
4
|
+
* Exposes cron job management as structured tool calls for Claude Desktop,
|
|
5
|
+
* Claude Code, and any MCP-compatible client.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { addJob, removeJob, listJobs, toggleJob, JobSchema } from './config.js';
|
|
11
|
+
import { exec } from 'node:child_process';
|
|
12
|
+
import { promisify } from 'node:util';
|
|
13
|
+
const execAsync = promisify(exec);
|
|
14
|
+
// ─── Named handler functions (exported for testability + V8 coverage) ─────────
|
|
15
|
+
export async function handleAddCron(args) {
|
|
16
|
+
try {
|
|
17
|
+
const job = JobSchema.parse({
|
|
18
|
+
id: args.id,
|
|
19
|
+
schedule: args.schedule,
|
|
20
|
+
prompt: args.prompt,
|
|
21
|
+
model: args.model ?? 'sonnet',
|
|
22
|
+
enabled: !(args.disabled ?? false),
|
|
23
|
+
});
|
|
24
|
+
addJob(job);
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: 'text',
|
|
29
|
+
text: `Job "${job.id}" added successfully.\nSchedule: ${job.schedule}\nModel: ${job.model}\nEnabled: ${job.enabled}`,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: 'text', text: `Error adding job: ${err.message}` }],
|
|
37
|
+
isError: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function handleListCrons() {
|
|
42
|
+
try {
|
|
43
|
+
const jobs = listJobs();
|
|
44
|
+
if (jobs.length === 0) {
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: 'text', text: 'No cron jobs configured.' }],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const lines = jobs.map((job) => `• ${job.id}\n Schedule: ${job.schedule}\n Model: ${job.model}\n Enabled: ${job.enabled}\n Prompt: ${job.prompt}`);
|
|
50
|
+
return {
|
|
51
|
+
content: [
|
|
52
|
+
{
|
|
53
|
+
type: 'text',
|
|
54
|
+
text: `${jobs.length} job(s) configured:\n\n${lines.join('\n\n')}`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: 'text', text: `Error listing jobs: ${err.message}` }],
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function handleRemoveCron(args) {
|
|
67
|
+
try {
|
|
68
|
+
removeJob(args.id);
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: 'text', text: `Job "${args.id}" removed successfully.` }],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: 'text', text: `Error removing job: ${err.message}` }],
|
|
76
|
+
isError: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export async function handleEnableCron(args) {
|
|
81
|
+
try {
|
|
82
|
+
toggleJob(args.id, true);
|
|
83
|
+
return {
|
|
84
|
+
content: [{ type: 'text', text: `Job "${args.id}" enabled successfully.` }],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
return {
|
|
89
|
+
content: [{ type: 'text', text: `Error enabling job: ${err.message}` }],
|
|
90
|
+
isError: true,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export async function handleDisableCron(args) {
|
|
95
|
+
try {
|
|
96
|
+
toggleJob(args.id, false);
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: 'text', text: `Job "${args.id}" disabled successfully.` }],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: 'text', text: `Error disabling job: ${err.message}` }],
|
|
104
|
+
isError: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export async function handleGetStatus() {
|
|
109
|
+
try {
|
|
110
|
+
const jobs = listJobs();
|
|
111
|
+
const enabled = jobs.filter((j) => j.enabled).length;
|
|
112
|
+
const disabled = jobs.filter((j) => !j.enabled).length;
|
|
113
|
+
let daemonStatus = 'unknown';
|
|
114
|
+
try {
|
|
115
|
+
const { stdout } = await execAsync('pgrep -f "cron-claude"');
|
|
116
|
+
daemonStatus = stdout.trim() ? 'running' : 'stopped';
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
daemonStatus = 'stopped';
|
|
120
|
+
}
|
|
121
|
+
const lines = [
|
|
122
|
+
`Daemon status: ${daemonStatus}`,
|
|
123
|
+
`Total jobs: ${jobs.length}`,
|
|
124
|
+
`Enabled: ${enabled}`,
|
|
125
|
+
`Disabled: ${disabled}`,
|
|
126
|
+
];
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
return {
|
|
133
|
+
content: [{ type: 'text', text: `Error getting status: ${err.message}` }],
|
|
134
|
+
isError: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// ─── Server setup ─────────────────────────────────────────────────────────────
|
|
139
|
+
export const server = new McpServer({
|
|
140
|
+
name: 'cron-claude',
|
|
141
|
+
version: '1.0.0',
|
|
142
|
+
});
|
|
143
|
+
server.tool('add_cron', 'Add a new scheduled cron job that runs a Claude prompt on a schedule', {
|
|
144
|
+
id: z.string().min(1).describe('Unique identifier for the job'),
|
|
145
|
+
schedule: z
|
|
146
|
+
.string()
|
|
147
|
+
.min(1)
|
|
148
|
+
.describe('Cron schedule expression (e.g. "0 14 * * *" for daily at 2pm)'),
|
|
149
|
+
prompt: z.string().min(1).describe('Prompt to send to Claude when the job runs'),
|
|
150
|
+
model: z.string().optional().describe('Claude model to use (default: sonnet)'),
|
|
151
|
+
disabled: z
|
|
152
|
+
.boolean()
|
|
153
|
+
.optional()
|
|
154
|
+
.describe('Create the job in a disabled state (default: false)'),
|
|
155
|
+
}, handleAddCron);
|
|
156
|
+
server.tool('list_crons', 'List all configured cron jobs', {}, async () => handleListCrons());
|
|
157
|
+
server.tool('remove_cron', 'Remove a scheduled cron job by ID', {
|
|
158
|
+
id: z.string().min(1).describe('ID of the job to remove'),
|
|
159
|
+
}, handleRemoveCron);
|
|
160
|
+
server.tool('enable_cron', 'Enable a previously disabled cron job', {
|
|
161
|
+
id: z.string().min(1).describe('ID of the job to enable'),
|
|
162
|
+
}, handleEnableCron);
|
|
163
|
+
server.tool('disable_cron', 'Disable a cron job without removing it', {
|
|
164
|
+
id: z.string().min(1).describe('ID of the job to disable'),
|
|
165
|
+
}, handleDisableCron);
|
|
166
|
+
server.tool('get_status', 'Get the current status of the cron-claude daemon and job counts', {}, async () => handleGetStatus());
|
|
167
|
+
// ─── Main entry point ─────────────────────────────────────────────────────────
|
|
168
|
+
export async function main() {
|
|
169
|
+
const transport = new StdioServerTransport();
|
|
170
|
+
await server.connect(transport);
|
|
171
|
+
}
|
|
172
|
+
/* c8 ignore start */
|
|
173
|
+
const isMain = process.argv[1]?.endsWith('mcp-server.ts') || process.argv[1]?.endsWith('mcp-server.js');
|
|
174
|
+
if (isMain) {
|
|
175
|
+
main().catch((err) => {
|
|
176
|
+
console.error('MCP server error:', err);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/* c8 ignore stop */
|
|
181
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Job } from './config.js';
|
|
2
|
+
export interface SchedulerOptions {
|
|
3
|
+
claudePath?: string;
|
|
4
|
+
onJobStart?: (job: Job) => void;
|
|
5
|
+
onJobComplete?: (job: Job, output: string) => void;
|
|
6
|
+
onJobError?: (job: Job, error: Error) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare class Scheduler {
|
|
9
|
+
private tasks;
|
|
10
|
+
private options;
|
|
11
|
+
constructor(options?: SchedulerOptions);
|
|
12
|
+
getClaudePath(): string;
|
|
13
|
+
schedule(job: Job): void;
|
|
14
|
+
unschedule(jobId: string): void;
|
|
15
|
+
unscheduleAll(): void;
|
|
16
|
+
getScheduledIds(): string[];
|
|
17
|
+
executeJob(job: Job): Promise<string>;
|
|
18
|
+
private logJobOutput;
|
|
19
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import cron from 'node-cron';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { getLogsDir } from './config.js';
|
|
6
|
+
const CLAUDE_PATH = path.join(process.env.HOME || '~', '.local', 'bin', 'claude');
|
|
7
|
+
export class Scheduler {
|
|
8
|
+
tasks = new Map();
|
|
9
|
+
options;
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.options = options;
|
|
12
|
+
}
|
|
13
|
+
getClaudePath() {
|
|
14
|
+
return this.options.claudePath ?? CLAUDE_PATH;
|
|
15
|
+
}
|
|
16
|
+
schedule(job) {
|
|
17
|
+
if (!job.enabled)
|
|
18
|
+
return;
|
|
19
|
+
if (!cron.validate(job.schedule)) {
|
|
20
|
+
throw new Error(`Invalid cron schedule for job "${job.id}": ${job.schedule}`);
|
|
21
|
+
}
|
|
22
|
+
// Stop existing task if re-scheduling
|
|
23
|
+
this.unschedule(job.id);
|
|
24
|
+
const task = cron.schedule(job.schedule, () => {
|
|
25
|
+
this.executeJob(job);
|
|
26
|
+
});
|
|
27
|
+
this.tasks.set(job.id, task);
|
|
28
|
+
}
|
|
29
|
+
unschedule(jobId) {
|
|
30
|
+
const existing = this.tasks.get(jobId);
|
|
31
|
+
if (existing) {
|
|
32
|
+
existing.stop();
|
|
33
|
+
this.tasks.delete(jobId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
unscheduleAll() {
|
|
37
|
+
for (const [id] of this.tasks) {
|
|
38
|
+
this.unschedule(id);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
getScheduledIds() {
|
|
42
|
+
return Array.from(this.tasks.keys());
|
|
43
|
+
}
|
|
44
|
+
executeJob(job) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
this.options.onJobStart?.(job);
|
|
47
|
+
const args = ['--print', '--model', job.model, job.prompt];
|
|
48
|
+
const claudePath = this.getClaudePath();
|
|
49
|
+
execFile(claudePath, args, { timeout: 300000 }, (error, stdout, stderr) => {
|
|
50
|
+
const output = stdout || stderr || '';
|
|
51
|
+
this.logJobOutput(job, output, error ?? undefined);
|
|
52
|
+
if (error) {
|
|
53
|
+
this.options.onJobError?.(job, error);
|
|
54
|
+
reject(error);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this.options.onJobComplete?.(job, output);
|
|
58
|
+
resolve(output);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
logJobOutput(job, output, error) {
|
|
64
|
+
const logsDir = getLogsDir();
|
|
65
|
+
try {
|
|
66
|
+
if (!fs.existsSync(logsDir)) {
|
|
67
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
70
|
+
const logFile = path.join(logsDir, `${job.id}_${timestamp}.log`);
|
|
71
|
+
const content = [
|
|
72
|
+
`Job: ${job.id}`,
|
|
73
|
+
`Time: ${new Date().toISOString()}`,
|
|
74
|
+
`Schedule: ${job.schedule}`,
|
|
75
|
+
`Model: ${job.model}`,
|
|
76
|
+
error ? `Error: ${error.message}` : 'Status: success',
|
|
77
|
+
`---`,
|
|
78
|
+
output,
|
|
79
|
+
].join('\n');
|
|
80
|
+
fs.writeFileSync(logFile, content, 'utf-8');
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Logging failure shouldn't crash the daemon
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
|
package/install.sh
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
PLIST_NAME="com.cron-claude.daemon"
|
|
5
|
+
PLIST_PATH="$HOME/Library/LaunchAgents/${PLIST_NAME}.plist"
|
|
6
|
+
INSTALL_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
LOG_DIR="$HOME/.cron-claude/logs"
|
|
8
|
+
|
|
9
|
+
mkdir -p "$HOME/Library/LaunchAgents"
|
|
10
|
+
mkdir -p "$LOG_DIR"
|
|
11
|
+
|
|
12
|
+
cat > "$PLIST_PATH" << EOF
|
|
13
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
14
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
15
|
+
<plist version="1.0">
|
|
16
|
+
<dict>
|
|
17
|
+
<key>Label</key>
|
|
18
|
+
<string>${PLIST_NAME}</string>
|
|
19
|
+
<key>ProgramArguments</key>
|
|
20
|
+
<array>
|
|
21
|
+
<string>$(which node)</string>
|
|
22
|
+
<string>${INSTALL_DIR}/dist/daemon.js</string>
|
|
23
|
+
</array>
|
|
24
|
+
<key>RunAtLoad</key>
|
|
25
|
+
<true/>
|
|
26
|
+
<key>KeepAlive</key>
|
|
27
|
+
<true/>
|
|
28
|
+
<key>StandardOutPath</key>
|
|
29
|
+
<string>${LOG_DIR}/daemon-stdout.log</string>
|
|
30
|
+
<key>StandardErrorPath</key>
|
|
31
|
+
<string>${LOG_DIR}/daemon-stderr.log</string>
|
|
32
|
+
<key>WorkingDirectory</key>
|
|
33
|
+
<string>${INSTALL_DIR}</string>
|
|
34
|
+
<key>EnvironmentVariables</key>
|
|
35
|
+
<dict>
|
|
36
|
+
<key>PATH</key>
|
|
37
|
+
<string>/usr/local/bin:/usr/bin:/bin:$HOME/.local/bin</string>
|
|
38
|
+
<key>HOME</key>
|
|
39
|
+
<string>${HOME}</string>
|
|
40
|
+
</dict>
|
|
41
|
+
</dict>
|
|
42
|
+
</plist>
|
|
43
|
+
EOF
|
|
44
|
+
|
|
45
|
+
echo "LaunchAgent plist written to: $PLIST_PATH"
|
|
46
|
+
|
|
47
|
+
# Unload if already loaded
|
|
48
|
+
launchctl unload "$PLIST_PATH" 2>/dev/null || true
|
|
49
|
+
|
|
50
|
+
# Load the agent
|
|
51
|
+
launchctl load "$PLIST_PATH"
|
|
52
|
+
|
|
53
|
+
echo "cron-claude daemon installed and started."
|
|
54
|
+
echo "To stop: launchctl unload $PLIST_PATH"
|
|
55
|
+
echo "To restart: launchctl unload $PLIST_PATH && launchctl load $PLIST_PATH"
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cron-claude",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Schedule any Claude prompt to run automatically on a cron. Lightweight alternative to a full AI platform for recurring agentic tasks.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/daemon.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"cron-claude": "dist/cli.js",
|
|
9
|
+
"cron-claude-mcp": "dist/mcp-server.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsx src/daemon.ts",
|
|
14
|
+
"start": "node dist/daemon.js",
|
|
15
|
+
"cli": "tsx src/cli.ts",
|
|
16
|
+
"test": "vitest run --coverage",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"install-service": "bash install.sh",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/",
|
|
23
|
+
"install.sh",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"claude",
|
|
29
|
+
"cron",
|
|
30
|
+
"mcp",
|
|
31
|
+
"ai",
|
|
32
|
+
"automation",
|
|
33
|
+
"scheduler",
|
|
34
|
+
"anthropic",
|
|
35
|
+
"claude-code"
|
|
36
|
+
],
|
|
37
|
+
"author": "tygi",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/tygiacalone/cron-claude.git"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/tygiacalone/cron-claude#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/tygiacalone/cron-claude/issues"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
52
|
+
"commander": "^12.1.0",
|
|
53
|
+
"node-cron": "^3.0.3",
|
|
54
|
+
"zod": "^3.23.8"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^22.0.0",
|
|
58
|
+
"@types/node-cron": "^3.0.11",
|
|
59
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
60
|
+
"tsx": "^4.19.0",
|
|
61
|
+
"typescript": "^5.6.0",
|
|
62
|
+
"vitest": "^2.1.0"
|
|
63
|
+
}
|
|
64
|
+
}
|