openclaw-scheduler 0.2.9 → 0.2.11
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/CHANGELOG.md +7 -0
- package/INSTALL-ADDITIONAL-HOST.md +1 -1
- package/INSTALL-LINUX.md +1 -1
- package/INSTALL-WINDOWS.md +1 -1
- package/INSTALL.md +1 -1
- package/JOB-QUICK-REF.md +2 -0
- package/README.md +5 -5
- package/cli.js +9 -1
- package/dispatch/529-recovery.mjs +21 -2
- package/dispatch/completion.mjs +50 -0
- package/dispatch/index.mjs +179 -11
- package/dispatch/watcher.mjs +106 -16
- package/dispatcher-strategies.js +121 -72
- package/dispatcher.js +4 -2
- package/docs/gateway-contract.md +21 -0
- package/gateway.js +140 -30
- package/index.d.ts +5 -0
- package/jobs.js +23 -8
- package/migrate-consolidate.js +6 -2
- package/package.json +3 -3
- package/paths.js +43 -1
- package/scheduler-schema.js +2 -0
- package/schema.sql +6 -1
- package/setup.mjs +24 -22
package/setup.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import os from 'os';
|
|
|
19
19
|
import { execSync } from 'child_process';
|
|
20
20
|
|
|
21
21
|
import { fileURLToPath } from 'url';
|
|
22
|
-
import { ensureSchedulerDbParent, resolveSchedulerDbPath } from './paths.js';
|
|
22
|
+
import { ensureSchedulerDbParent, resolveSchedulerDbPath, resolveServiceWorkingDirectory } from './paths.js';
|
|
23
23
|
import { createJob } from './jobs.js';
|
|
24
24
|
import { initDb } from './db.js';
|
|
25
25
|
|
|
@@ -152,7 +152,8 @@ print();
|
|
|
152
152
|
// --- Step 1: Paths ------------------------------------------------------------
|
|
153
153
|
|
|
154
154
|
print('-- Step 1: Paths ---------------------------------------');
|
|
155
|
-
const
|
|
155
|
+
const schedulerInstallRoot = __dirname;
|
|
156
|
+
const serviceWorkingDirectory = resolveServiceWorkingDirectory({ env: process.env, moduleDir: schedulerInstallRoot });
|
|
156
157
|
const defaultWorkspace = path.join(os.homedir(), '.openclaw', 'workspace');
|
|
157
158
|
const workspacePath = await ask('Workspace path', defaultWorkspace);
|
|
158
159
|
const defaultGateway = 'http://127.0.0.1:18789';
|
|
@@ -162,10 +163,11 @@ const schedulerDbPath = resolveSchedulerDbPath({ env: process.env });
|
|
|
162
163
|
if (schedulerDbPath !== ':memory:') ensureSchedulerDbParent(schedulerDbPath);
|
|
163
164
|
|
|
164
165
|
print();
|
|
165
|
-
print(` Scheduler:
|
|
166
|
-
print(`
|
|
167
|
-
print(`
|
|
168
|
-
print(`
|
|
166
|
+
print(` Scheduler install root: ${schedulerInstallRoot}`);
|
|
167
|
+
print(` Service working dir: ${serviceWorkingDirectory}`);
|
|
168
|
+
print(` Workspace: ${workspacePath}`);
|
|
169
|
+
print(` Gateway: ${gatewayUrl}`);
|
|
170
|
+
print(` Deliver to: ${deliverTo || '(none -- skipping job creation)'}`);
|
|
169
171
|
print();
|
|
170
172
|
|
|
171
173
|
// --- Preflight: npm install behavior -----------------------------------------
|
|
@@ -193,9 +195,9 @@ print();
|
|
|
193
195
|
|
|
194
196
|
print('-- Step 2: Database migrations -------------------------');
|
|
195
197
|
try {
|
|
196
|
-
const { setDbPath } = await import(path.join(
|
|
198
|
+
const { setDbPath } = await import(path.join(schedulerInstallRoot, 'db.js'));
|
|
197
199
|
setDbPath(schedulerDbPath);
|
|
198
|
-
const migrate = (await import(path.join(
|
|
200
|
+
const migrate = (await import(path.join(schedulerInstallRoot, 'migrate-consolidate.js'))).default;
|
|
199
201
|
const ran = migrate();
|
|
200
202
|
if (ran) {
|
|
201
203
|
ok(`Migrations applied -> ${schedulerDbPath}`);
|
|
@@ -213,9 +215,9 @@ print();
|
|
|
213
215
|
print('-- Step 3: Agent memory files --------------------------');
|
|
214
216
|
|
|
215
217
|
const memoryMd = path.join(workspacePath, 'MEMORY.md');
|
|
216
|
-
const memoryEntry = `- **Scheduler Queue Pattern:** Use \`node ${
|
|
217
|
-
Inbox Consumer (\`${
|
|
218
|
-
Stuck Run Detector (\`${
|
|
218
|
+
const memoryEntry = `- **Scheduler Queue Pattern:** Use \`node ${schedulerInstallRoot}/cli.js msg send <from> <to> "body"\` for signal-only queue entries.
|
|
219
|
+
Inbox Consumer (\`${schedulerInstallRoot}/scripts/inbox-consumer.mjs\`) drains pending queue messages to Telegram.
|
|
220
|
+
Stuck Run Detector (\`${schedulerInstallRoot}/scripts/stuck-run-detector.mjs\`) alerts on stale \`running\` runs.`;
|
|
219
221
|
|
|
220
222
|
const memResult = appendIfMissing(memoryMd, 'Scheduler Queue Pattern', memoryEntry);
|
|
221
223
|
if (memResult === true) ok('Appended scheduler queue entry -> MEMORY.md');
|
|
@@ -228,10 +230,10 @@ const indexSection = `### Scheduler & Dispatch
|
|
|
228
230
|
|
|
229
231
|
| File | Covers | Load |
|
|
230
232
|
|------|--------|------|
|
|
231
|
-
| \`${
|
|
232
|
-
| \`${
|
|
233
|
-
| \`${
|
|
234
|
-
| \`${
|
|
233
|
+
| \`${schedulerInstallRoot}/\` | Standalone SQLite scheduler. CLI: \`node cli.js\`. launchd service: \`ai.openclaw.scheduler\`. | Any scheduler/cron work |
|
|
234
|
+
| \`${schedulerInstallRoot}/cli.js\` | Queue + run operations: \`msg send\`, \`msg inbox\`, \`runs running\`, \`runs stale\`. | Day-to-day scheduler operations |
|
|
235
|
+
| \`${schedulerInstallRoot}/scripts/inbox-consumer.mjs\` | Drains queue messages for one agent and delivers to Telegram. | Queue/inbox consumption |
|
|
236
|
+
| \`${schedulerInstallRoot}/scripts/stuck-run-detector.mjs\` | Detects stale \`running\` runs and exits non-zero for alerts. | Run health monitoring |`;
|
|
235
237
|
|
|
236
238
|
// Try inserting before a common section header, fall back to append.
|
|
237
239
|
// NOTE: the link emoji anchors must match the actual markdown heading in
|
|
@@ -278,7 +280,7 @@ if (!deliverTo) {
|
|
|
278
280
|
const existingNames = listJobs().map(r => r.name);
|
|
279
281
|
|
|
280
282
|
// Inbox Consumer
|
|
281
|
-
const icScript = path.join(
|
|
283
|
+
const icScript = path.join(schedulerInstallRoot, 'scripts', 'inbox-consumer.mjs');
|
|
282
284
|
const icName = 'Inbox Consumer';
|
|
283
285
|
if (existingNames.includes(icName)) {
|
|
284
286
|
skip(`"${icName}" job already exists`);
|
|
@@ -304,7 +306,7 @@ if (!deliverTo) {
|
|
|
304
306
|
|
|
305
307
|
// Stuck Run Detector
|
|
306
308
|
const srdName = 'Stuck Run Detector';
|
|
307
|
-
const srdScript = path.join(
|
|
309
|
+
const srdScript = path.join(schedulerInstallRoot, 'scripts', 'stuck-run-detector.mjs');
|
|
308
310
|
const srdCmd = `node ${srdScript} --threshold-min 45`; // coding tasks regularly take 30m+
|
|
309
311
|
if (existingNames.includes(srdName)) {
|
|
310
312
|
skip(`"${srdName}" job already exists`);
|
|
@@ -338,7 +340,7 @@ print();
|
|
|
338
340
|
|
|
339
341
|
const platform = process.platform;
|
|
340
342
|
const nodePath = process.execPath;
|
|
341
|
-
const indexPath = path.join(
|
|
343
|
+
const indexPath = path.join(schedulerInstallRoot, 'dispatcher.js');
|
|
342
344
|
const logPath = platform === 'win32'
|
|
343
345
|
? path.join(os.tmpdir(), 'openclaw-scheduler.log')
|
|
344
346
|
: '/tmp/openclaw-scheduler.log';
|
|
@@ -457,7 +459,7 @@ if (platform === 'darwin') {
|
|
|
457
459
|
<string>${xmlEscape(indexPath)}</string>
|
|
458
460
|
</array>
|
|
459
461
|
${userXml} <key>WorkingDirectory</key>
|
|
460
|
-
<string>${xmlEscape(
|
|
462
|
+
<string>${xmlEscape(serviceWorkingDirectory)}</string>
|
|
461
463
|
<key>EnvironmentVariables</key>
|
|
462
464
|
<dict>
|
|
463
465
|
<key>HOME</key>
|
|
@@ -565,7 +567,7 @@ After=network.target
|
|
|
565
567
|
|
|
566
568
|
[Service]
|
|
567
569
|
Type=simple
|
|
568
|
-
WorkingDirectory=${
|
|
570
|
+
WorkingDirectory=${serviceWorkingDirectory}
|
|
569
571
|
ExecStart=${nodePath} --no-warnings ${indexPath}
|
|
570
572
|
Environment=OPENCLAW_GATEWAY_URL=${gatewayUrl}${gatewayToken ? `\nEnvironment="OPENCLAW_GATEWAY_TOKEN=${gatewayToken.replace(/"/g, '\\"')}"` : ''}
|
|
571
573
|
Environment=SCHEDULER_DB=${schedulerDbPath}
|
|
@@ -612,7 +614,7 @@ WantedBy=default.target
|
|
|
612
614
|
if (install) {
|
|
613
615
|
try {
|
|
614
616
|
execSync(
|
|
615
|
-
`pm2 start "${indexPath}" --name "${pm2Name}" --cwd "${
|
|
617
|
+
`pm2 start "${indexPath}" --name "${pm2Name}" --cwd "${serviceWorkingDirectory}" ` +
|
|
616
618
|
`--log "${logPath}"`,
|
|
617
619
|
{
|
|
618
620
|
stdio: 'inherit',
|
|
@@ -652,7 +654,7 @@ WantedBy=default.target
|
|
|
652
654
|
print(' Setup steps:');
|
|
653
655
|
print(' 1. Install WSL2: wsl --install (in PowerShell as Admin)');
|
|
654
656
|
print(' 2. Open your WSL terminal and run this wizard again from there:');
|
|
655
|
-
print(` cd ${
|
|
657
|
+
print(` cd ${schedulerInstallRoot.replace(/\\/g, '/')}`);
|
|
656
658
|
print(' node setup.mjs');
|
|
657
659
|
print();
|
|
658
660
|
print(' WSL2 with systemd enabled gives the best experience (auto-start on login).');
|