Files
nanoclaw/.claude/skills/migrate-from-openclaw/MIGRATE_CRONS.md
meeech 211d2b5877 docs: convert all skill instructions from npm to pnpm
Batch update 62 files across .claude/skills/ — SKILL.md, REMOVE.md,
and script files. Conversions: npm run -> pnpm run, npm install ->
pnpm install, npx -> pnpm exec/dlx, npm uninstall -> pnpm uninstall,
package-lock.json -> pnpm-lock.yaml, shebangs updated.
2026-04-17 09:22:45 +03:00

101 lines
5.1 KiB
Markdown

# Migrating OpenClaw Cron Jobs to NanoClaw Scheduled Tasks
This file is referenced by SKILL.md Phase 5 when cron jobs are detected.
**Before inserting tasks:** Read `src/db.ts` and search for `scheduled_tasks` to verify the current table schema. The schema below is a reference — if columns have been added, removed, or renamed, use the current schema from the source code.
Also verify the `createTask` function signature in `src/db.ts` — it may be simpler to call it via a script than raw SQL.
## OpenClaw Cron Job Format
Source: `<STATE_DIR>/cron/jobs.json` (from `src/cron/types.ts`). If the file format doesn't match what's described below, read the actual file and adapt — OpenClaw may have changed the schema.
The jobs file is `{ version: 1, jobs: CronJob[] }`. Each job has:
- `id`, `name`, `description`, `enabled`, `deleteAfterRun`
- `schedule`: `{ kind: "cron", expr: string, tz?: string }` | `{ kind: "every", everyMs: number }` | `{ kind: "at", at: string }`
- `payload`: `{ kind: "agentTurn", message: string, model?, thinking?, timeoutSeconds? }` | `{ kind: "systemEvent", text: string }`
- `sessionTarget`: `"main"` | `"isolated"` | `"current"` | `"session:<id>"`
- `wakeMode`: `"next-heartbeat"` | `"now"`
- `delivery`: `{ mode: "none" | "announce" | "webhook", channel?, to?, threadId?, bestEffort? }`
- `failureAlert`: `{ after?: number, channel?, to?, cooldownMs? }` | `false`
- `state`: runtime state (nextRunAtMs, lastRunStatus, consecutiveErrors, etc.)
## NanoClaw `scheduled_tasks` Table
Source: `src/db.ts`
| Column | Type | Notes |
|--------|------|-------|
| `id` | TEXT PK | Unique task ID |
| `group_folder` | TEXT | Target group directory (e.g. `"main"`) |
| `chat_jid` | TEXT | Target chat JID |
| `prompt` | TEXT | Task instructions |
| `script` | TEXT | Optional bash pre-check script |
| `schedule_type` | TEXT | `"cron"`, `"interval"`, or `"once"` |
| `schedule_value` | TEXT | Cron expr, ms interval, or ISO timestamp |
| `context_mode` | TEXT | `"group"` or `"isolated"` (default) |
| `next_run` | TEXT | ISO timestamp — must be computed at insert time |
| `last_run` | TEXT | null initially |
| `last_result` | TEXT | null initially |
| `status` | TEXT | `"active"`, `"paused"`, or `"completed"` |
| `created_at` | TEXT | ISO timestamp |
## Field Mapping
- `schedule.kind:"cron"` + `schedule.expr``schedule_type:"cron"`, `schedule_value:<expr>`
- `schedule.kind:"every"` + `schedule.everyMs``schedule_type:"interval"`, `schedule_value:<ms as string>`
- `schedule.kind:"at"` + `schedule.at``schedule_type:"once"`, `schedule_value:<ISO timestamp>`
- `payload.message` or `payload.text``prompt`
- `sessionTarget:"isolated"``context_mode:"isolated"`, `sessionTarget:"main"` or `"current"``context_mode:"group"`
## What Doesn't Map
- `delivery.mode:"webhook"` — NanoClaw has no webhook delivery. Discuss with the user: this could be implemented as a task `script` that runs `curl` to hit the webhook endpoint.
- `failureAlert` — NanoClaw has no failure alert system. Note this to the user.
- `wakeMode` — NanoClaw tasks always wake the agent immediately.
- `payload.model`, `payload.thinking`, `payload.timeoutSeconds` — NanoClaw doesn't support per-task model/thinking config. These are handled by the SDK.
- `deleteAfterRun` — NanoClaw `"once"` tasks are marked `"completed"` after running, not deleted.
## For Each Enabled Job
1. Show what it does: name, schedule, prompt, delivery mode
2. Explain any differences (no retry config, no webhook delivery, no failure alerts)
3. If `delivery.mode:"webhook"`: discuss with the user — a task `script` with `curl` often suffices
4. Ask if they want to keep this task
## Inserting Tasks
Insert directly into the SQLite database. This requires groups to be registered first (Phase 1). Use the registered group's `folder` and `chat_jid`:
```bash
pnpm exec tsx -e "
const Database = require('better-sqlite3');
const { CronExpressionParser } = require('cron-parser');
const db = new Database('store/messages.db');
// Compute next_run for cron tasks:
// const interval = CronExpressionParser.parse('<expr>', { tz: process.env.TZ || 'UTC' });
// const nextRun = interval.next().toISOString();
db.prepare(\`INSERT INTO scheduled_tasks (id, group_folder, chat_jid, prompt, script, schedule_type, schedule_value, context_mode, next_run, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\`).run(
'migrated-<original-id>',
'<group_folder>',
'<chat_jid>',
'<mapped prompt>',
null,
'<mapped schedule_type>',
'<mapped schedule_value>',
'<mapped context_mode>',
'<computed next_run ISO>',
'active',
new Date().toISOString()
);
db.close();
"
```
**Computing `next_run`:**
- `cron` tasks: use `CronExpressionParser.parse(expr, { tz }).next().toISOString()`
- `interval` tasks: `new Date(Date.now() + ms).toISOString()`
- `once` tasks: `next_run` equals `schedule_value`
If groups haven't been registered yet (database doesn't exist), save the task details to `groups/main/openclaw-migration-tasks.md` with the exact SQL payloads, and tell the user: "These tasks will be created after `/setup` registers your groups."