openclaw-scheduler 0.2.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/AGENTS.md +302 -0
- package/BEST-PRACTICES.md +506 -0
- package/CHANGELOG.md +82 -0
- package/CODE_OF_CONDUCT.md +22 -0
- package/CONTEXT.md +26 -0
- package/CONTRIBUTING.md +73 -0
- package/IMPLEMENTATION_SPEC.md +170 -0
- package/INSTALL-ADDITIONAL-HOST.md +333 -0
- package/INSTALL-LINUX.md +419 -0
- package/INSTALL-WINDOWS.md +305 -0
- package/INSTALL.md +364 -0
- package/JOB-QUICK-REF.md +222 -0
- package/LICENSE +21 -0
- package/QUICK-START.md +256 -0
- package/README.md +2170 -0
- package/SECURITY.md +34 -0
- package/UNINSTALL.md +129 -0
- package/UPGRADING.md +436 -0
- package/agents.js +67 -0
- package/approval.js +107 -0
- package/backup.js +390 -0
- package/bin/openclaw-scheduler.js +138 -0
- package/cli.js +1083 -0
- package/db.js +122 -0
- package/dispatch/529-recovery.mjs +204 -0
- package/dispatch/README.md +372 -0
- package/dispatch/config.example.json +24 -0
- package/dispatch/deliver-watcher.sh +57 -0
- package/dispatch/hooks.mjs +171 -0
- package/dispatch/index.mjs +1836 -0
- package/dispatch/watcher.mjs +1396 -0
- package/dispatch-queue.js +112 -0
- package/dispatcher-approvals.js +96 -0
- package/dispatcher-delivery.js +43 -0
- package/dispatcher-maintenance.js +242 -0
- package/dispatcher-shell.js +29 -0
- package/dispatcher-strategies.js +1280 -0
- package/dispatcher-utils.js +81 -0
- package/dispatcher.js +855 -0
- package/docs/adr-schedule-ownership.md +73 -0
- package/docs/gateway-contract.md +904 -0
- package/docs/plans/2026-03-09-fix-typescript-types.md +91 -0
- package/docs/plans/2026-03-09-test-coverage-gaps.md +83 -0
- package/docs/plans/2026-03-10-dispatcher-refactor.md +801 -0
- package/docs/trust-architecture.md +266 -0
- package/gateway.js +473 -0
- package/idempotency.js +119 -0
- package/index.d.ts +864 -0
- package/index.js +17 -0
- package/jobs.js +1224 -0
- package/messages.js +357 -0
- package/migrate-consolidate.js +694 -0
- package/migrate.js +125 -0
- package/package.json +130 -0
- package/paths.js +79 -0
- package/prompt-context.js +94 -0
- package/retrieval.js +176 -0
- package/runs.js +270 -0
- package/scheduler-schema.js +101 -0
- package/schema.sql +480 -0
- package/scripts/dispatch-cli-utils.mjs +65 -0
- package/scripts/inbox-consumer.mjs +288 -0
- package/scripts/stuck-detector.sh +18 -0
- package/scripts/stuck-run-detector.mjs +333 -0
- package/scripts/telegram-webhook-check.mjs +238 -0
- package/setup.mjs +724 -0
- package/shell-result.js +214 -0
- package/task-tracker.js +300 -0
- package/team-adapter.js +335 -0
- package/v02-runtime.js +599 -0
package/INSTALL-LINUX.md
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# Installing OpenClaw Scheduler on Linux
|
|
2
|
+
|
|
3
|
+
Step-by-step guide to deploy the scheduler on a Linux host running OpenClaw.
|
|
4
|
+
|
|
5
|
+
> **macOS?** See [INSTALL.md](INSTALL.md).
|
|
6
|
+
> **Windows?** See [INSTALL-WINDOWS.md](INSTALL-WINDOWS.md).
|
|
7
|
+
> **Need examples or migration help?** See [Starter Recipes in the README](README.md#starter-recipes) and [Common Migrations](README.md#common-migrations).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
| Requirement | Notes |
|
|
14
|
+
|-------------|-------|
|
|
15
|
+
| Node.js >= 20 | Install via [nvm](https://github.com/nvm-sh/nvm) or [NodeSource](https://github.com/nodesource/distributions) |
|
|
16
|
+
| build-essential | `sudo apt install build-essential python3` — required for `better-sqlite3` native compile |
|
|
17
|
+
| OpenClaw gateway running | With auth token |
|
|
18
|
+
| Git | `sudo apt install git` |
|
|
19
|
+
| systemd (user units) | Ubuntu 18.04+, Debian 10+, Fedora, Arch — standard |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Step 1: Install Scheduler Files
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd ~/.openclaw
|
|
27
|
+
git clone https://github.com/amittell/openclaw-scheduler.git scheduler
|
|
28
|
+
cd scheduler
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or copy from an existing host:
|
|
32
|
+
```bash
|
|
33
|
+
scp -r user@source-host:~/.openclaw/scheduler ~/.openclaw/scheduler
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or npm-first install (no git clone):
|
|
37
|
+
```bash
|
|
38
|
+
mkdir -p ~/.openclaw/scheduler
|
|
39
|
+
npm install --prefix ~/.openclaw/scheduler openclaw-scheduler@latest
|
|
40
|
+
npm exec --prefix ~/.openclaw/scheduler openclaw-scheduler -- help
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Runtime state for npm installs defaults to `~/.openclaw/scheduler/`, not the package directory under `node_modules/`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Step 2: Install Build Dependencies and Node Modules
|
|
48
|
+
|
|
49
|
+
If you used the npm-first install path in Step 1, dependencies are already installed; skip to Step 3.
|
|
50
|
+
|
|
51
|
+
Install system build tools first — `better-sqlite3` compiles a native addon and needs them:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Ubuntu/Debian
|
|
55
|
+
sudo apt install build-essential python3
|
|
56
|
+
|
|
57
|
+
# Fedora/RHEL
|
|
58
|
+
sudo dnf install gcc gcc-c++ make python3
|
|
59
|
+
|
|
60
|
+
# Arch
|
|
61
|
+
sudo pacman -S base-devel python
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Then install Node dependencies:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cd ~/.openclaw/scheduler
|
|
68
|
+
npm install
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If `better-sqlite3` still fails, check that `node-gyp` can find Python:
|
|
72
|
+
```bash
|
|
73
|
+
node -e "require('better-sqlite3')" && echo "OK"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If the host's Node runtime changes later, rebuild the native binding before restarting the scheduler:
|
|
77
|
+
```bash
|
|
78
|
+
cd ~/.openclaw/scheduler
|
|
79
|
+
npm rebuild better-sqlite3
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Step 3: Run Tests
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
SCHEDULER_DB=:memory: node test.js
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**All tests must pass before proceeding.**
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Step 4: Enable Chat Completions on Gateway
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
openclaw config set gateway.http.endpoints.chatCompletions.enabled true
|
|
98
|
+
openclaw gateway restart
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Verify:
|
|
102
|
+
```bash
|
|
103
|
+
curl -s -o /dev/null -w "%{http_code}" \
|
|
104
|
+
-X POST \
|
|
105
|
+
-H "Authorization: Bearer YOUR_GATEWAY_TOKEN" \
|
|
106
|
+
-H "Content-Type: application/json" \
|
|
107
|
+
-d '{"model":"openclaw:main","messages":[{"role":"user","content":"reply OK"}]}' \
|
|
108
|
+
http://127.0.0.1:18789/v1/chat/completions
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Expected: `200`
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Step 5: Migrate Jobs from OC Cron
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
cd ~/.openclaw/scheduler
|
|
119
|
+
node migrate.js
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This imports jobs from `~/.openclaw/cron/jobs.json` → SQLite, converting schedule formats:
|
|
123
|
+
- `cron` → direct expression
|
|
124
|
+
- `every` → approximate cron (e.g., 30min → `*/30 * * * *`)
|
|
125
|
+
- `at` → one-shot with `delete_after_run=true`
|
|
126
|
+
|
|
127
|
+
Verify:
|
|
128
|
+
```bash
|
|
129
|
+
node cli.js jobs list
|
|
130
|
+
node cli.js status
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Step 6: Disable OC Built-in Cron
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
openclaw cron list
|
|
139
|
+
# For each enabled job:
|
|
140
|
+
openclaw cron edit <job-id> --disable
|
|
141
|
+
openclaw config set cron.enabled false
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Also set `OPENCLAW_SKIP_CRON=1` in your OpenClaw gateway service environment (systemd/pm2), then restart the gateway.
|
|
145
|
+
|
|
146
|
+
Verify: `openclaw cron list` shows no enabled jobs (or "No cron jobs").
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Step 7: Disable OC Heartbeat
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
openclaw config set agents.defaults.heartbeat.every "0m"
|
|
154
|
+
# If you have per-agent heartbeat overrides, set/remove those too:
|
|
155
|
+
# agents.list[].heartbeat.every = "0m"
|
|
156
|
+
openclaw gateway restart
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Step 8: Install systemd User Service
|
|
162
|
+
|
|
163
|
+
The scheduler runs as a **systemd user service** — it runs under your user account, starts on login (and optionally on boot via linger), and restarts automatically on crash.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Create user service directory
|
|
167
|
+
mkdir -p ~/.config/systemd/user/
|
|
168
|
+
|
|
169
|
+
# Create the service file
|
|
170
|
+
cat > ~/.config/systemd/user/openclaw-scheduler.service <<'EOF'
|
|
171
|
+
[Unit]
|
|
172
|
+
Description=OpenClaw Scheduler
|
|
173
|
+
Documentation=https://github.com/amittell/openclaw-scheduler
|
|
174
|
+
After=network.target
|
|
175
|
+
|
|
176
|
+
[Service]
|
|
177
|
+
Type=simple
|
|
178
|
+
WorkingDirectory=%h/.openclaw/scheduler
|
|
179
|
+
ExecStart=/usr/bin/node --no-warnings %h/.openclaw/scheduler/dispatcher.js
|
|
180
|
+
Environment=OPENCLAW_GATEWAY_URL=http://127.0.0.1:18789
|
|
181
|
+
Environment=OPENCLAW_GATEWAY_TOKEN=YOUR_GATEWAY_TOKEN
|
|
182
|
+
Environment=SCHEDULER_DB=%h/.openclaw/scheduler/scheduler.db
|
|
183
|
+
Environment=SCHEDULER_TICK_MS=10000
|
|
184
|
+
Environment=SCHEDULER_STALE_THRESHOLD_S=90
|
|
185
|
+
# Uncomment to enable verbose logging:
|
|
186
|
+
# Environment=SCHEDULER_DEBUG=1
|
|
187
|
+
Restart=always
|
|
188
|
+
RestartSec=5
|
|
189
|
+
StandardOutput=append:/tmp/openclaw-scheduler.log
|
|
190
|
+
StandardError=append:/tmp/openclaw-scheduler.log
|
|
191
|
+
|
|
192
|
+
[Install]
|
|
193
|
+
WantedBy=default.target
|
|
194
|
+
EOF
|
|
195
|
+
|
|
196
|
+
# Edit the service file if needed
|
|
197
|
+
nano ~/.config/systemd/user/openclaw-scheduler.service
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Required edits in the service file:**
|
|
201
|
+
|
|
202
|
+
1. Replace `YOUR_GATEWAY_TOKEN` with your actual OpenClaw gateway token
|
|
203
|
+
2. Verify `ExecStart` uses the correct `node` path:
|
|
204
|
+
```bash
|
|
205
|
+
which node # check where node is — might be /usr/local/bin/node or ~/.nvm/versions/node/.../bin/node
|
|
206
|
+
```
|
|
207
|
+
If node is not at `/usr/bin/node`, update the `ExecStart` line:
|
|
208
|
+
```ini
|
|
209
|
+
ExecStart=/home/youruser/.nvm/versions/node/vX.Y.Z/bin/node --no-warnings /home/youruser/.openclaw/scheduler/dispatcher.js
|
|
210
|
+
```
|
|
211
|
+
Replace `vX.Y.Z` with your installed Node.js version (run `node --version` to check).
|
|
212
|
+
Note: `%h` expands to your home directory in systemd user units.
|
|
213
|
+
|
|
214
|
+
**Enable and start:**
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Reload systemd to pick up the new service
|
|
218
|
+
systemctl --user daemon-reload
|
|
219
|
+
|
|
220
|
+
# Enable autostart
|
|
221
|
+
systemctl --user enable openclaw-scheduler
|
|
222
|
+
|
|
223
|
+
# Start now
|
|
224
|
+
systemctl --user start openclaw-scheduler
|
|
225
|
+
|
|
226
|
+
# Verify
|
|
227
|
+
systemctl --user status openclaw-scheduler
|
|
228
|
+
journalctl --user -u openclaw-scheduler -f # live logs (Ctrl-C to exit)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Run without an active login session (important for servers):**
|
|
232
|
+
|
|
233
|
+
By default, systemd user services stop when you log out. To keep the scheduler running 24/7 even without an active session:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
loginctl enable-linger $USER
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
This is the Linux equivalent of macOS LaunchDaemon `RunAtLoad: true` + `KeepAlive: true`.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Step 9: Smoke Tests
|
|
244
|
+
|
|
245
|
+
> **Note:** These smoke test commands use direct file imports and are for the git-clone install path. For npm installs, use `openclaw-scheduler` CLI commands instead.
|
|
246
|
+
|
|
247
|
+
### Isolated dispatch
|
|
248
|
+
|
|
249
|
+
Shell jobs on Linux use `/bin/bash` by default. Override with `SCHEDULER_SHELL` env var in the service file.
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
cd ~/.openclaw/scheduler
|
|
253
|
+
node --input-type=module -e "
|
|
254
|
+
import { initDb, getDb } from './db.js';
|
|
255
|
+
import { createJob } from './jobs.js';
|
|
256
|
+
initDb();
|
|
257
|
+
const job = createJob({
|
|
258
|
+
name: 'Smoke Test',
|
|
259
|
+
schedule_cron: '0 0 31 2 *',
|
|
260
|
+
payload_message: 'Reply with exactly: SCHEDULER_OK',
|
|
261
|
+
delivery_mode: 'none',
|
|
262
|
+
delete_after_run: true,
|
|
263
|
+
origin: 'system',
|
|
264
|
+
run_timeout_ms: 300000,
|
|
265
|
+
});
|
|
266
|
+
getDb().prepare(\"UPDATE jobs SET next_run_at = datetime('now', '-1 second') WHERE id = ?\").run(job.id);
|
|
267
|
+
console.log('Created smoke test:', job.id);
|
|
268
|
+
"
|
|
269
|
+
sleep 20 && tail -10 /tmp/openclaw-scheduler.log
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Look for: `Dispatching: Smoke Test` → `Completed: Smoke Test`
|
|
273
|
+
|
|
274
|
+
### Shell job smoke test
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
node --input-type=module -e "
|
|
278
|
+
import { initDb, getDb } from './db.js';
|
|
279
|
+
import { createJob } from './jobs.js';
|
|
280
|
+
initDb();
|
|
281
|
+
const job = createJob({
|
|
282
|
+
name: 'Shell Smoke Test',
|
|
283
|
+
schedule_cron: '0 0 31 2 *',
|
|
284
|
+
session_target: 'shell',
|
|
285
|
+
payload_message: 'echo scheduler_shell_ok',
|
|
286
|
+
delivery_mode: 'announce-always',
|
|
287
|
+
delete_after_run: true,
|
|
288
|
+
origin: 'system',
|
|
289
|
+
run_timeout_ms: 300000,
|
|
290
|
+
});
|
|
291
|
+
getDb().prepare(\"UPDATE jobs SET next_run_at = datetime('now', '-1 second') WHERE id = ?\").run(job.id);
|
|
292
|
+
console.log('Created shell smoke test:', job.id);
|
|
293
|
+
"
|
|
294
|
+
sleep 15 && node cli.js runs list
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Telegram delivery
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
node --input-type=module -e "
|
|
301
|
+
import { initDb, getDb } from './db.js';
|
|
302
|
+
import { createJob } from './jobs.js';
|
|
303
|
+
initDb();
|
|
304
|
+
const job = createJob({
|
|
305
|
+
name: 'Telegram Test',
|
|
306
|
+
schedule_cron: '0 0 31 2 *',
|
|
307
|
+
payload_message: 'Confirm scheduler is working. Send a brief greeting.',
|
|
308
|
+
delivery_mode: 'announce',
|
|
309
|
+
delivery_channel: 'telegram',
|
|
310
|
+
delivery_to: 'YOUR_CHAT_ID',
|
|
311
|
+
delete_after_run: true,
|
|
312
|
+
origin: 'system',
|
|
313
|
+
run_timeout_ms: 300000,
|
|
314
|
+
});
|
|
315
|
+
getDb().prepare(\"UPDATE jobs SET next_run_at = datetime('now', '-1 second') WHERE id = ?\").run(job.id);
|
|
316
|
+
console.log('Created Telegram test:', job.id);
|
|
317
|
+
"
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
You should receive a Telegram message within 30 seconds.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Step 10: Review Migrated Jobs
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
node cli.js jobs list
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
- Disable expired one-shot jobs: `node cli.js jobs disable <id>`
|
|
331
|
+
- Delete unwanted jobs: `node cli.js jobs delete <id>`
|
|
332
|
+
- Adjust timeouts: `node cli.js jobs update <id> '{"run_timeout_ms":600000}'`
|
|
333
|
+
- Verify cron conversions are correct for `every`-based schedules
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Step 11: Verify First Real Job
|
|
338
|
+
|
|
339
|
+
Wait for the next scheduled job and confirm:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# Watch live
|
|
343
|
+
journalctl --user -u openclaw-scheduler -f
|
|
344
|
+
tail -f /tmp/openclaw-scheduler.log
|
|
345
|
+
|
|
346
|
+
# After it fires:
|
|
347
|
+
node cli.js runs list <job-id>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Service Management
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
# Start / Stop / Restart
|
|
356
|
+
systemctl --user start openclaw-scheduler
|
|
357
|
+
systemctl --user stop openclaw-scheduler
|
|
358
|
+
systemctl --user restart openclaw-scheduler
|
|
359
|
+
|
|
360
|
+
# Status
|
|
361
|
+
systemctl --user status openclaw-scheduler
|
|
362
|
+
|
|
363
|
+
# Logs
|
|
364
|
+
journalctl --user -u openclaw-scheduler -f
|
|
365
|
+
tail -f /tmp/openclaw-scheduler.log
|
|
366
|
+
|
|
367
|
+
# Disable autostart (won't start on next login)
|
|
368
|
+
systemctl --user disable openclaw-scheduler
|
|
369
|
+
|
|
370
|
+
# Quick health
|
|
371
|
+
node cli.js status
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Rollback
|
|
377
|
+
|
|
378
|
+
If anything goes wrong:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# 1. Stop and disable service
|
|
382
|
+
systemctl --user stop openclaw-scheduler
|
|
383
|
+
systemctl --user disable openclaw-scheduler
|
|
384
|
+
rm ~/.config/systemd/user/openclaw-scheduler.service
|
|
385
|
+
systemctl --user daemon-reload
|
|
386
|
+
|
|
387
|
+
# 2. Re-enable OC cron
|
|
388
|
+
openclaw cron edit <job-id> --enable # for each job
|
|
389
|
+
openclaw config set cron.enabled true
|
|
390
|
+
# remove OPENCLAW_SKIP_CRON=1 from gateway service env
|
|
391
|
+
|
|
392
|
+
# 3. Re-enable heartbeat
|
|
393
|
+
openclaw config set agents.defaults.heartbeat.every "5m"
|
|
394
|
+
openclaw gateway restart
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
For a complete removal (deleting all data), see [UNINSTALL.md](UNINSTALL.md).
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Validation Checklist
|
|
402
|
+
|
|
403
|
+
- [ ] `SCHEDULER_DB=:memory: node test.js` -- all passing, 0 failed
|
|
404
|
+
- [ ] `node cli.js status` → shows jobs, 0 stale
|
|
405
|
+
- [ ] `systemctl --user status openclaw-scheduler` → active (running)
|
|
406
|
+
- [ ] Log file has startup lines, no errors
|
|
407
|
+
- [ ] OC cron → all disabled
|
|
408
|
+
- [ ] OC heartbeat → `0m`
|
|
409
|
+
- [ ] Chat completions → 200
|
|
410
|
+
- [ ] Smoke test → dispatched + completed in log
|
|
411
|
+
- [ ] Telegram test → message received
|
|
412
|
+
- [ ] First real job → fires on schedule
|
|
413
|
+
- [ ] `loginctl enable-linger $USER` → confirmed (for always-on servers)
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## Upgrading
|
|
418
|
+
|
|
419
|
+
Already have the scheduler installed and need to update to a newer version? See [UPGRADING.md](UPGRADING.md).
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Installing OpenClaw Scheduler on Windows
|
|
2
|
+
|
|
3
|
+
> **TL;DR:** Use WSL2. It's faster to set up, fully supported, and eliminates every Windows-specific limitation listed in this guide.
|
|
4
|
+
>
|
|
5
|
+
> **Need examples or migration help?** See [Starter Recipes in the README](README.md#starter-recipes) and [Common Migrations](README.md#common-migrations).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Option A: WSL2 (Strongly Recommended)
|
|
10
|
+
|
|
11
|
+
If you're running OpenClaw in WSL2, follow the Linux guide: **[INSTALL-LINUX.md](INSTALL-LINUX.md)**
|
|
12
|
+
|
|
13
|
+
WSL2 gives you a full Linux environment with:
|
|
14
|
+
- **systemd support** — Ubuntu 22.04+ in WSL2 ships with systemd enabled by default
|
|
15
|
+
- Full bash/zsh shell job compatibility
|
|
16
|
+
- No path separator issues, no `.bat` script constraints
|
|
17
|
+
- Identical behavior to a native Linux install
|
|
18
|
+
|
|
19
|
+
To enable systemd in WSL2 (Ubuntu 22.04+), add to `/etc/wsl.conf`:
|
|
20
|
+
```ini
|
|
21
|
+
[boot]
|
|
22
|
+
systemd=true
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then restart WSL: `wsl --shutdown` from PowerShell, reopen your terminal.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Option B: PM2 (Native Windows)
|
|
30
|
+
|
|
31
|
+
Use this path only if you can't use WSL2 — for example, if OpenClaw itself is running natively on Windows (not in WSL2).
|
|
32
|
+
|
|
33
|
+
> ⚠️ **Shell job limitations apply.** See [Shell Jobs on Windows](#shell-jobs-on-windows) below.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
### Prerequisites
|
|
38
|
+
|
|
39
|
+
| Requirement | Install |
|
|
40
|
+
|-------------|---------|
|
|
41
|
+
| Node.js >= 20 | [nodejs.org](https://nodejs.org) -- use the LTS installer |
|
|
42
|
+
| pm2 | `npm install -g pm2` |
|
|
43
|
+
| OpenClaw gateway | Must be running with a valid auth token |
|
|
44
|
+
| Git for Windows | [git-scm.com](https://git-scm.com) or use GitHub Desktop |
|
|
45
|
+
|
|
46
|
+
**Build tools for `better-sqlite3`** (required -- it compiles a native addon):
|
|
47
|
+
|
|
48
|
+
Option 1 -- Visual Studio Build Tools (recommended):
|
|
49
|
+
1. Install [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) -- select the **"Desktop development with C++"** workload
|
|
50
|
+
2. Install [Python 3.x](https://python.org) -- check "Add to PATH" during install
|
|
51
|
+
|
|
52
|
+
Option 2 -- `windows-build-tools` (deprecated, may not work on newer Windows):
|
|
53
|
+
```powershell
|
|
54
|
+
npm install -g windows-build-tools
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Verify:
|
|
58
|
+
```powershell
|
|
59
|
+
node -e "require('better-sqlite3')" && echo "OK"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### Step 1: Install Scheduler Files
|
|
65
|
+
|
|
66
|
+
```powershell
|
|
67
|
+
cd $env:USERPROFILE\.openclaw
|
|
68
|
+
git clone https://github.com/amittell/openclaw-scheduler.git scheduler
|
|
69
|
+
cd scheduler
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or npm-first install (no git clone):
|
|
73
|
+
```powershell
|
|
74
|
+
mkdir $env:USERPROFILE\.openclaw\scheduler -Force
|
|
75
|
+
npm install --prefix $env:USERPROFILE\.openclaw\scheduler openclaw-scheduler@latest
|
|
76
|
+
npm exec --prefix $env:USERPROFILE\.openclaw\scheduler openclaw-scheduler -- help
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Runtime state for npm installs defaults to `$env:USERPROFILE\.openclaw\scheduler`, not the package directory under `node_modules`.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### Step 2: Install Dependencies
|
|
84
|
+
|
|
85
|
+
If you used the npm-first install path in Step 1, dependencies are already installed; skip to Step 3.
|
|
86
|
+
|
|
87
|
+
```powershell
|
|
88
|
+
npm install
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
If `better-sqlite3` fails with a build error, make sure Visual Studio Build Tools and Python are installed (see Prerequisites above), then:
|
|
92
|
+
```powershell
|
|
93
|
+
npm install --build-from-source
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
If Node changes later on this machine, rebuild the native binding before restarting the scheduler:
|
|
97
|
+
```powershell
|
|
98
|
+
cd $env:USERPROFILE\.openclaw\scheduler
|
|
99
|
+
npm rebuild better-sqlite3
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### Step 3: Run Tests
|
|
105
|
+
|
|
106
|
+
```powershell
|
|
107
|
+
$env:SCHEDULER_DB=":memory:"; node test.js
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
All tests must pass before continuing.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### Step 4: Enable Chat Completions on Gateway
|
|
115
|
+
|
|
116
|
+
```powershell
|
|
117
|
+
openclaw config set gateway.http.endpoints.chatCompletions.enabled true
|
|
118
|
+
openclaw gateway restart
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Verify (PowerShell):
|
|
122
|
+
```powershell
|
|
123
|
+
$headers = @{ Authorization = "Bearer YOUR_GATEWAY_TOKEN"; "Content-Type" = "application/json" }
|
|
124
|
+
$body = '{"model":"openclaw:main","messages":[{"role":"user","content":"reply OK"}]}'
|
|
125
|
+
(Invoke-WebRequest -Uri http://127.0.0.1:18789/v1/chat/completions -Method POST -Headers $headers -Body $body).StatusCode
|
|
126
|
+
# Expected: 200
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### Step 5: Migrate Jobs from OC Cron
|
|
132
|
+
|
|
133
|
+
```powershell
|
|
134
|
+
node migrate.js
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Verify:
|
|
138
|
+
```powershell
|
|
139
|
+
node cli.js jobs list
|
|
140
|
+
node cli.js status
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### Step 6: Disable OC Built-in Cron and Heartbeat
|
|
146
|
+
|
|
147
|
+
```powershell
|
|
148
|
+
openclaw cron list
|
|
149
|
+
# For each enabled job:
|
|
150
|
+
openclaw cron edit <job-id> --disable
|
|
151
|
+
openclaw config set cron.enabled false
|
|
152
|
+
|
|
153
|
+
openclaw config set agents.defaults.heartbeat.every "0m"
|
|
154
|
+
# If you have per-agent heartbeat overrides, set/remove those too:
|
|
155
|
+
# agents.list[].heartbeat.every = "0m"
|
|
156
|
+
openclaw gateway restart
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Also set `OPENCLAW_SKIP_CRON=1` in your OpenClaw gateway process environment (service wrapper/PM2 ecosystem), then restart the gateway.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### Step 7: Start with PM2
|
|
164
|
+
|
|
165
|
+
```powershell
|
|
166
|
+
$env:OPENCLAW_GATEWAY_URL = "http://127.0.0.1:18789"
|
|
167
|
+
$env:OPENCLAW_GATEWAY_TOKEN = "YOUR_GATEWAY_TOKEN"
|
|
168
|
+
$env:SCHEDULER_TICK_MS = "10000"
|
|
169
|
+
$env:SCHEDULER_STALE_THRESHOLD_S = "90"
|
|
170
|
+
pm2 start dispatcher.js --name openclaw-scheduler
|
|
171
|
+
|
|
172
|
+
# Optional verbose logging:
|
|
173
|
+
$env:SCHEDULER_DEBUG = "1"
|
|
174
|
+
pm2 restart openclaw-scheduler --update-env
|
|
175
|
+
|
|
176
|
+
# Save PM2 process list (persists across restarts)
|
|
177
|
+
pm2 save
|
|
178
|
+
|
|
179
|
+
# Generate and apply startup hook (run the printed command as Administrator)
|
|
180
|
+
pm2 startup
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Verify:
|
|
184
|
+
```powershell
|
|
185
|
+
pm2 status
|
|
186
|
+
pm2 logs openclaw-scheduler --lines 20
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### Step 8: Smoke Test
|
|
192
|
+
|
|
193
|
+
> **Note:** These smoke test commands use direct file imports and are for the git-clone install path. For npm installs, use `openclaw-scheduler` CLI commands instead.
|
|
194
|
+
|
|
195
|
+
```powershell
|
|
196
|
+
node --input-type=module -e "
|
|
197
|
+
import { initDb, getDb } from './db.js';
|
|
198
|
+
import { createJob } from './jobs.js';
|
|
199
|
+
initDb();
|
|
200
|
+
const job = createJob({
|
|
201
|
+
name: 'Smoke Test',
|
|
202
|
+
schedule_cron: '0 0 31 2 *',
|
|
203
|
+
payload_message: 'Reply with exactly: SCHEDULER_OK',
|
|
204
|
+
delivery_mode: 'none',
|
|
205
|
+
delete_after_run: true,
|
|
206
|
+
origin: 'system',
|
|
207
|
+
run_timeout_ms: 300000,
|
|
208
|
+
});
|
|
209
|
+
getDb().prepare(\"UPDATE jobs SET next_run_at = datetime('now', '-1 second') WHERE id = ?\").run(job.id);
|
|
210
|
+
console.log('Created smoke test:', job.id);
|
|
211
|
+
"
|
|
212
|
+
Start-Sleep 20; pm2 logs openclaw-scheduler --lines 20
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Look for: `Dispatching: Smoke Test` → `Completed: Smoke Test`
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## PM2 Management
|
|
220
|
+
|
|
221
|
+
```powershell
|
|
222
|
+
# Status
|
|
223
|
+
pm2 status
|
|
224
|
+
|
|
225
|
+
# Logs (live)
|
|
226
|
+
pm2 logs openclaw-scheduler
|
|
227
|
+
|
|
228
|
+
# Restart
|
|
229
|
+
pm2 restart openclaw-scheduler
|
|
230
|
+
|
|
231
|
+
# Stop
|
|
232
|
+
pm2 stop openclaw-scheduler
|
|
233
|
+
|
|
234
|
+
# Remove from PM2
|
|
235
|
+
pm2 delete openclaw-scheduler
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Shell Jobs on Windows
|
|
241
|
+
|
|
242
|
+
Shell jobs (`session_target: 'shell'`) use **`cmd.exe`** by default on Windows. Override with:
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
SCHEDULER_SHELL=powershell.exe
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Set this in your PM2 launch command:
|
|
249
|
+
```powershell
|
|
250
|
+
pm2 start dispatcher.js --name openclaw-scheduler `
|
|
251
|
+
--env SCHEDULER_SHELL=powershell.exe `
|
|
252
|
+
...
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Known Limitations (native Windows)
|
|
256
|
+
|
|
257
|
+
| Limitation | Impact | Fix |
|
|
258
|
+
|------------|--------|-----|
|
|
259
|
+
| Shell is `cmd.exe` by default | Bash scripts won't work | Use `.bat`/`.cmd` or set `SCHEDULER_SHELL=powershell.exe` |
|
|
260
|
+
| No `/bin/bash` or `/bin/zsh` | Can't use Unix shell syntax | Rewrite scripts as PowerShell |
|
|
261
|
+
| Path separators | `\` vs `/` in commands | Use `\\` in `payload_message` strings |
|
|
262
|
+
| Line endings | `.sh` scripts may fail | Save scripts with LF line endings |
|
|
263
|
+
|
|
264
|
+
**WSL2 eliminates all of these.** If you hit these issues in practice, switching to WSL2 will be faster than working around them.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Rollback
|
|
269
|
+
|
|
270
|
+
```powershell
|
|
271
|
+
# Stop and remove PM2 process
|
|
272
|
+
pm2 stop openclaw-scheduler
|
|
273
|
+
pm2 delete openclaw-scheduler
|
|
274
|
+
|
|
275
|
+
# Re-enable OC cron
|
|
276
|
+
openclaw cron edit <job-id> --enable # for each job
|
|
277
|
+
openclaw config set cron.enabled true
|
|
278
|
+
# remove OPENCLAW_SKIP_CRON=1 from gateway process env
|
|
279
|
+
|
|
280
|
+
# Re-enable heartbeat
|
|
281
|
+
openclaw config set agents.defaults.heartbeat.every "5m"
|
|
282
|
+
openclaw gateway restart
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
For a complete removal (deleting all data), see [UNINSTALL.md](UNINSTALL.md).
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Upgrading
|
|
290
|
+
|
|
291
|
+
Already have the scheduler installed and need to update to a newer version? See [UPGRADING.md](UPGRADING.md).
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Validation Checklist
|
|
296
|
+
|
|
297
|
+
- [ ] `$env:SCHEDULER_DB=":memory:"; node test.js` -- all passing, 0 failed
|
|
298
|
+
- [ ] `node cli.js status` → shows jobs, 0 stale
|
|
299
|
+
- [ ] `pm2 status` → openclaw-scheduler is `online`
|
|
300
|
+
- [ ] PM2 log has startup lines, no errors
|
|
301
|
+
- [ ] OC cron → all disabled
|
|
302
|
+
- [ ] OC heartbeat → `0m`
|
|
303
|
+
- [ ] Chat completions → 200
|
|
304
|
+
- [ ] Smoke test → dispatched + completed in log
|
|
305
|
+
- [ ] First real job → fires on schedule
|