openclaw-scheduler 0.2.7 → 0.2.9

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.
@@ -23,9 +23,10 @@ import { readFileSync, writeFileSync, renameSync } from 'fs';
23
23
  import { execFileSync } from 'child_process';
24
24
  import { dirname, join } from 'path';
25
25
  import { fileURLToPath } from 'url';
26
+ import { resolveLabelsPath } from './paths.mjs';
26
27
 
27
28
  const __dirname = dirname(fileURLToPath(import.meta.url));
28
- const LABELS_PATH = process.env.DISPATCH_LABELS_PATH || join(__dirname, 'labels.json');
29
+ const LABELS_PATH = resolveLabelsPath({ legacyCandidates: [join(__dirname, 'labels.json')] });
29
30
  const INDEX_PATH = process.env.DISPATCH_INDEX_PATH || join(__dirname, 'index.mjs');
30
31
 
31
32
  const MAX_RETRIES = 3;
@@ -23,7 +23,7 @@ No scheduler DB dependency. No dispatcher tick delay. Sessions start instantly.
23
23
  | `chilisaus.mjs` | Branded wrapper |
24
24
  | `config.example.json` | Example config |
25
25
  | `test-done-postoffice.mjs` | Done handler test |
26
- | `labels.json` | Local label→session ledger (gitignored) |
26
+ | `~/.openclaw/scheduler/dispatch/labels.json` | Durable label→session ledger |
27
27
  | `README.md` | This file |
28
28
 
29
29
  ---
@@ -45,7 +45,7 @@ Orchestrator calls:
45
45
  → Patches session with model/thinking/spawnDepth
46
46
  → Calls gateway `agent` method with the task
47
47
  → Session starts immediately (no scheduler tick delay)
48
- → Tracks label→sessionKey in labels.json
48
+ → Tracks label→sessionKey in the durable labels ledger
49
49
  → Agent auto-announces results on completion
50
50
  → hooks.mjs fires dispatch.started to Loki
51
51
  ```
@@ -117,7 +117,7 @@ node dispatch/index.mjs stuck --threshold-min 15
117
117
  Exit 0 = nothing stuck (silent).
118
118
  Exit 1 = stuck sessions found (triggers announce delivery).
119
119
 
120
- Checks labels.json for sessions marked `running`, cross-references gateway
120
+ Checks the labels ledger for sessions marked `running`, cross-references gateway
121
121
  session store for last activity timestamp.
122
122
 
123
123
  ### `result` — last assistant reply from a session
@@ -206,7 +206,7 @@ Shows all labels in the ledger, sorted by most recent. Filter by status.
206
206
  node dispatch/index.mjs sync
207
207
  ```
208
208
 
209
- Reconciles `labels.json` with the gateway sessions store. Sessions that no
209
+ Reconciles the labels ledger with the gateway sessions store. Sessions that no
210
210
  longer exist on the gateway are marked stale, and sessions present on the
211
211
  gateway but missing from the ledger are imported. Useful after gateway restarts
212
212
  or manual session cleanup.
@@ -41,6 +41,7 @@ import {
41
41
  taskRequiresGitSha,
42
42
  } from './completion.mjs';
43
43
  import { getDispatchLivenessPolicy } from './liveness.mjs';
44
+ import { resolveLabelsPath } from './paths.mjs';
44
45
  import { onStarted, onFinished, onStuck } from './hooks.mjs';
45
46
  import { resolveMessageInput } from './message-input.mjs';
46
47
  import { buildDispatchDeliverySurface } from '../scripts/dispatch-cli-utils.mjs';
@@ -67,7 +68,9 @@ const INVOKE_DIR = (() => {
67
68
 
68
69
  // -- Config ---------------------------------------------------
69
70
 
70
- const LABELS_PATH = process.env.DISPATCH_LABELS_PATH || join(INVOKE_DIR, 'labels.json');
71
+ const LABELS_PATH = resolveLabelsPath({
72
+ legacyCandidates: [join(INVOKE_DIR, 'labels.json'), join(__dirname, 'labels.json')],
73
+ });
71
74
 
72
75
  /** Load dispatch config from config.json.
73
76
  * Resolution order:
@@ -0,0 +1,36 @@
1
+ import { copyFileSync, existsSync, mkdirSync } from 'fs';
2
+ import { dirname, join, resolve as pathResolve } from 'path';
3
+ import { homedir } from 'os';
4
+
5
+ function schedulerHome() {
6
+ return process.env.OPENCLAW_SCHEDULER_HOME ||
7
+ join(process.env.HOME || homedir(), '.openclaw', 'scheduler');
8
+ }
9
+
10
+ export function resolveDispatchStateDir() {
11
+ return process.env.DISPATCH_STATE_DIR ||
12
+ join(schedulerHome(), 'dispatch');
13
+ }
14
+
15
+ export function resolveLabelsPath({ legacyCandidates = [] } = {}) {
16
+ if (process.env.DISPATCH_LABELS_PATH) {
17
+ mkdirSync(dirname(process.env.DISPATCH_LABELS_PATH), { recursive: true });
18
+ return process.env.DISPATCH_LABELS_PATH;
19
+ }
20
+
21
+ const labelsPath = join(resolveDispatchStateDir(), 'labels.json');
22
+ mkdirSync(dirname(labelsPath), { recursive: true });
23
+
24
+ if (!existsSync(labelsPath)) {
25
+ const normalizedTarget = pathResolve(labelsPath);
26
+ const legacyPath = legacyCandidates
27
+ .filter(Boolean)
28
+ .map((candidate) => pathResolve(candidate))
29
+ .find((candidate) => candidate !== normalizedTarget && existsSync(candidate));
30
+ if (legacyPath) {
31
+ copyFileSync(legacyPath, labelsPath);
32
+ }
33
+ }
34
+
35
+ return labelsPath;
36
+ }
@@ -37,11 +37,12 @@ import {
37
37
  resolveCompletionDelivery,
38
38
  } from './completion.mjs';
39
39
  import { getDispatchLivenessPolicy } from './liveness.mjs';
40
+ import { resolveLabelsPath } from './paths.mjs';
40
41
  import { sendMessage } from '../messages.js';
41
42
 
42
43
  const __dirname = dirname(fileURLToPath(import.meta.url));
43
44
  const INDEX_PATH = process.env.DISPATCH_INDEX_PATH || join(__dirname, 'index.mjs');
44
- const LABELS_PATH = process.env.DISPATCH_LABELS_PATH || join(__dirname, 'labels.json');
45
+ const LABELS_PATH = resolveLabelsPath({ legacyCandidates: [join(__dirname, 'labels.json')] });
45
46
  const HOME_DIR = process.env.HOME || homedir();
46
47
  let labelsCache = null;
47
48
  let labelsCacheSignature = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-scheduler",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "SQLite-backed job scheduler and workflow engine for OpenClaw agents",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -44,6 +44,7 @@
44
44
  "dispatch/index.mjs",
45
45
  "dispatch/liveness.mjs",
46
46
  "dispatch/message-input.mjs",
47
+ "dispatch/paths.mjs",
47
48
  "dispatch/README.md",
48
49
  "dispatch/watcher.mjs",
49
50
  "scripts/dispatch-cli-utils.mjs",
@@ -29,12 +29,13 @@ import { join, dirname } from 'path';
29
29
  import { fileURLToPath } from 'url';
30
30
  import { tmpdir } from 'os';
31
31
  import { resolveDispatchCliPath, resolveDispatchLabel } from './dispatch-cli-utils.mjs';
32
+ import { resolveLabelsPath } from '../dispatch/paths.mjs';
32
33
 
33
34
  const __dirname = dirname(fileURLToPath(import.meta.url));
34
35
 
35
36
  // -- Paths ----------------------------------------------------
36
37
 
37
- const LABELS_PATH = process.env.DISPATCH_LABELS_PATH || join(__dirname, '..', 'dispatch', 'labels.json');
38
+ const LABELS_PATH = resolveLabelsPath({ legacyCandidates: [join(__dirname, '..', 'dispatch', 'labels.json')] });
38
39
  const STATE_PATH = process.env.STUCK_STATE_PATH || join(tmpdir(), 'stuck-detector-state.json');
39
40
  const DISPATCH_CLI = resolveDispatchCliPath(process.env);
40
41
  const DISPATCH_IS_BIN = !DISPATCH_CLI.includes('/') && !DISPATCH_CLI.includes('\\');