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.
Files changed (70) hide show
  1. package/AGENTS.md +302 -0
  2. package/BEST-PRACTICES.md +506 -0
  3. package/CHANGELOG.md +82 -0
  4. package/CODE_OF_CONDUCT.md +22 -0
  5. package/CONTEXT.md +26 -0
  6. package/CONTRIBUTING.md +73 -0
  7. package/IMPLEMENTATION_SPEC.md +170 -0
  8. package/INSTALL-ADDITIONAL-HOST.md +333 -0
  9. package/INSTALL-LINUX.md +419 -0
  10. package/INSTALL-WINDOWS.md +305 -0
  11. package/INSTALL.md +364 -0
  12. package/JOB-QUICK-REF.md +222 -0
  13. package/LICENSE +21 -0
  14. package/QUICK-START.md +256 -0
  15. package/README.md +2170 -0
  16. package/SECURITY.md +34 -0
  17. package/UNINSTALL.md +129 -0
  18. package/UPGRADING.md +436 -0
  19. package/agents.js +67 -0
  20. package/approval.js +107 -0
  21. package/backup.js +390 -0
  22. package/bin/openclaw-scheduler.js +138 -0
  23. package/cli.js +1083 -0
  24. package/db.js +122 -0
  25. package/dispatch/529-recovery.mjs +204 -0
  26. package/dispatch/README.md +372 -0
  27. package/dispatch/config.example.json +24 -0
  28. package/dispatch/deliver-watcher.sh +57 -0
  29. package/dispatch/hooks.mjs +171 -0
  30. package/dispatch/index.mjs +1836 -0
  31. package/dispatch/watcher.mjs +1396 -0
  32. package/dispatch-queue.js +112 -0
  33. package/dispatcher-approvals.js +96 -0
  34. package/dispatcher-delivery.js +43 -0
  35. package/dispatcher-maintenance.js +242 -0
  36. package/dispatcher-shell.js +29 -0
  37. package/dispatcher-strategies.js +1280 -0
  38. package/dispatcher-utils.js +81 -0
  39. package/dispatcher.js +855 -0
  40. package/docs/adr-schedule-ownership.md +73 -0
  41. package/docs/gateway-contract.md +904 -0
  42. package/docs/plans/2026-03-09-fix-typescript-types.md +91 -0
  43. package/docs/plans/2026-03-09-test-coverage-gaps.md +83 -0
  44. package/docs/plans/2026-03-10-dispatcher-refactor.md +801 -0
  45. package/docs/trust-architecture.md +266 -0
  46. package/gateway.js +473 -0
  47. package/idempotency.js +119 -0
  48. package/index.d.ts +864 -0
  49. package/index.js +17 -0
  50. package/jobs.js +1224 -0
  51. package/messages.js +357 -0
  52. package/migrate-consolidate.js +694 -0
  53. package/migrate.js +125 -0
  54. package/package.json +130 -0
  55. package/paths.js +79 -0
  56. package/prompt-context.js +94 -0
  57. package/retrieval.js +176 -0
  58. package/runs.js +270 -0
  59. package/scheduler-schema.js +101 -0
  60. package/schema.sql +480 -0
  61. package/scripts/dispatch-cli-utils.mjs +65 -0
  62. package/scripts/inbox-consumer.mjs +288 -0
  63. package/scripts/stuck-detector.sh +18 -0
  64. package/scripts/stuck-run-detector.mjs +333 -0
  65. package/scripts/telegram-webhook-check.mjs +238 -0
  66. package/setup.mjs +724 -0
  67. package/shell-result.js +214 -0
  68. package/task-tracker.js +300 -0
  69. package/team-adapter.js +335 -0
  70. package/v02-runtime.js +599 -0
@@ -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