opencode-scheduled-tasks 0.1.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/README.md +287 -0
- package/dist/cli.js +1225 -0
- package/dist/cli.js.map +1 -0
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.js +882 -0
- package/dist/plugin.js.map +1 -0
- package/examples/daily-cleanup.md +16 -0
- package/examples/weekly-report.md +20 -0
- package/package.json +69 -0
- package/skill/SKILL.md +119 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/lib/db.ts","../src/lib/sqlite.ts","../src/lib/tasks.ts","../src/lib/cron.ts","../src/lib/runner.ts","../src/lib/installer.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport { copyFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { TaskDatabase, getDefaultDbPath } from \"./lib/db.js\";\nimport { readAllTasks } from \"./lib/tasks.js\";\nimport { isDue, getNextRunTime } from \"./lib/cron.js\";\nimport {\n spawnWorker,\n isProcessAlive,\n execTaskAndUpdateDb,\n} from \"./lib/runner.js\";\nimport {\n install,\n uninstall,\n getInstallInfo,\n} from \"./lib/installer.js\";\nimport type { TaskExecConfig } from \"./lib/types.js\";\n\n/** Absolute path to this script (used to spawn worker subprocesses) */\nconst SCHEDULER_PATH = fileURLToPath(import.meta.url);\n\n/**\n * Reap completed worker processes.\n *\n * For each task with status='running' and a PID set, check if the\n * process is still alive. If it's dead, the worker either completed\n * normally (and already updated the DB) or crashed. If the DB still\n * shows 'running', the worker crashed -- mark it as failed.\n */\nfunction reapWorkers(db: TaskDatabase): void {\n // Reap recurring task runs\n for (const run of db.getRunningTaskRuns()) {\n if (!run.pid) continue;\n if (!isProcessAlive(run.pid)) {\n // Worker exited but didn't update DB -> it crashed\n db.completeTaskRun(run.id, \"failed\", {\n error: `Worker process (PID ${run.pid}) exited unexpectedly`,\n });\n log(\n `Reaped crashed worker for \"${run.taskName}\" (PID ${run.pid})`,\n \"error\"\n );\n }\n }\n\n // Reap one-off tasks\n for (const task of db.getRunningOneoffTasks()) {\n if (!task.pid) continue;\n if (!isProcessAlive(task.pid)) {\n db.updateOneoffTaskStatus(task.id, \"failed\", {\n error: `Worker process (PID ${task.pid}) exited unexpectedly`,\n });\n log(\n `Reaped crashed worker for one-off \"${task.description}\" (PID ${task.pid})`,\n \"error\"\n );\n }\n }\n}\n\n/**\n * Run one scheduler tick: reap dead workers, check all tasks,\n * and spawn workers for any that are due.\n */\nfunction runTick(): void {\n const db = new TaskDatabase(getDefaultDbPath());\n\n try {\n // Phase 1: Clean up stale records (no PID, very old)\n const staleCount = db.cleanupStaleRuns();\n if (staleCount > 0) {\n log(`Cleaned up ${staleCount} stale running record(s)`);\n }\n\n // Phase 2: Reap completed/crashed workers\n reapWorkers(db);\n\n // Phase 3: Check recurring tasks and spawn workers for due ones\n const { tasks, errors } = readAllTasks();\n for (const { file, error } of errors) {\n log(`Error parsing task file \"${file}\": ${error}`, \"error\");\n }\n\n for (const task of tasks) {\n if (!task.enabled) continue;\n\n if (db.hasRunningTask(task.name)) {\n continue; // Already running, skip silently\n }\n\n const lastRun = db.getLastSuccessfulTaskRun(task.name);\n if (!isDue(task.schedule, lastRun?.startedAt)) {\n continue;\n }\n\n log(`Spawning worker for recurring task: ${task.name}`);\n const run = db.createTaskRun(task.name);\n\n try {\n const pid = spawnWorker(SCHEDULER_PATH, run.id, false);\n db.setTaskRunPid(run.id, pid);\n log(` Worker spawned (PID ${pid}, run ${run.id})`);\n } catch (err: any) {\n db.completeTaskRun(run.id, \"failed\", {\n error: `Failed to spawn worker: ${err.message}`,\n });\n log(` Failed to spawn worker: ${err.message}`, \"error\");\n }\n }\n\n // Phase 4: Check one-off tasks and spawn workers for due ones\n const dueTasks = db.getDueOneoffTasks();\n for (const task of dueTasks) {\n if (db.hasRunningOneoffTask(task.id)) {\n continue;\n }\n\n log(`Spawning worker for one-off task: ${task.description} (${task.id})`);\n db.updateOneoffTaskStatus(task.id, \"running\");\n\n try {\n const pid = spawnWorker(SCHEDULER_PATH, task.id, true);\n db.updateOneoffTaskStatus(task.id, \"running\", { pid });\n log(` Worker spawned (PID ${pid})`);\n } catch (err: any) {\n db.updateOneoffTaskStatus(task.id, \"failed\", {\n error: `Failed to spawn worker: ${err.message}`,\n });\n log(` Failed to spawn worker: ${err.message}`, \"error\");\n }\n }\n } finally {\n db.close();\n }\n}\n\n/**\n * Execute a single task synchronously (worker subprocess mode).\n *\n * Called via: scheduler --exec-task <runId> [--oneoff]\n * Runs opencode run, parses output, updates DB, then exits.\n */\nasync function execTask(runId: string, isOneoff: boolean): Promise<void> {\n const db = new TaskDatabase(getDefaultDbPath());\n\n try {\n let config: TaskExecConfig;\n\n if (isOneoff) {\n const task = db.getOneoffTask(runId);\n if (!task) {\n throw new Error(`One-off task not found: ${runId}`);\n }\n config = {\n name: `oneoff-${task.id.slice(0, 8)}`,\n prompt: task.prompt,\n cwd: task.cwd,\n sessionName: task.sessionName,\n model: task.model,\n agent: task.agent,\n permission: task.permission,\n };\n } else {\n // For recurring tasks, we need to look up the task file by name\n // The run ID maps to a task_runs record which has the task_name\n const runs = db.getTaskRunHistory(runId, 1);\n // runId here is actually the task_runs.id, look it up directly\n const allRuns = db.getRunningTaskRuns();\n const run = allRuns.find((r) => r.id === runId);\n if (!run) {\n throw new Error(`Task run not found: ${runId}`);\n }\n\n const { tasks } = readAllTasks();\n const task = tasks.find((t) => t.name === run.taskName);\n if (!task) {\n throw new Error(`Task file not found for: ${run.taskName}`);\n }\n\n config = {\n name: task.name,\n prompt: task.prompt,\n cwd: task.cwd,\n sessionName: task.sessionName,\n model: task.model,\n agent: task.agent,\n permission: task.permission,\n };\n }\n\n await execTaskAndUpdateDb(config, runId, isOneoff, db);\n } finally {\n db.close();\n }\n}\n\n// --- CLI display commands ---\n\nfunction listTasks(): void {\n const db = new TaskDatabase(getDefaultDbPath());\n\n try {\n const { tasks, errors } = readAllTasks();\n if (errors.length > 0) {\n for (const { file, error } of errors) {\n console.error(`Error in \"${file}\": ${error}`);\n }\n }\n\n if (tasks.length > 0) {\n console.log(\"Recurring tasks:\");\n console.log(\"\");\n for (const task of tasks) {\n const lastRun = db.getLastTaskRun(task.name);\n const statusStr = task.enabled ? \"enabled\" : \"disabled\";\n let nextStr = \"\";\n let lastStr = \"never\";\n\n if (task.enabled) {\n try {\n nextStr = `next: ${getNextRunTime(task.schedule)}`;\n } catch {\n nextStr = \"next: invalid cron\";\n }\n }\n\n if (lastRun) {\n lastStr = `${lastRun.status} ${lastRun.startedAt}`;\n }\n\n console.log(\n ` ${task.name.padEnd(24)} ${statusStr.padEnd(10)} ${nextStr.padEnd(40)} last: ${lastStr}`\n );\n }\n } else {\n console.log(\"No recurring tasks found.\");\n }\n\n const oneoffs = db.listOneoffTasks({ status: \"pending\" });\n if (oneoffs.length > 0) {\n console.log(\"\");\n console.log(\"Pending one-off tasks:\");\n console.log(\"\");\n for (const task of oneoffs) {\n console.log(\n ` ${task.id.slice(0, 12)}... \"${task.description}\" scheduled: ${task.scheduledAt}`\n );\n }\n }\n } finally {\n db.close();\n }\n}\n\nfunction showStatus(): void {\n const info = getInstallInfo();\n const platform = info.platform === \"unsupported\" ? \"unknown\" : info.platform;\n\n if (info.installed) {\n console.log(`Scheduler: installed (${platform})`);\n if (info.details) {\n console.log(` ${info.details}`);\n }\n } else {\n console.log(`Scheduler: not installed (detected platform: ${platform})`);\n console.log(\" Run: npx opencode-scheduler --install\");\n }\n\n console.log(\"\");\n\n const db = new TaskDatabase(getDefaultDbPath());\n try {\n const { tasks, errors } = readAllTasks();\n const enabled = tasks.filter((t) => t.enabled);\n const disabled = tasks.filter((t) => !t.enabled);\n\n console.log(\n `Recurring tasks: ${tasks.length} (${enabled.length} enabled, ${disabled.length} disabled)`\n );\n\n for (const task of tasks) {\n const lastRun = db.getLastTaskRun(task.name);\n if (!task.enabled) {\n console.log(` ${task.name.padEnd(24)} disabled`);\n continue;\n }\n\n let nextStr = \"\";\n try {\n nextStr = `next: ${getNextRunTime(task.schedule)}`;\n } catch {\n nextStr = \"next: invalid cron\";\n }\n\n let lastStr = \"never run\";\n if (lastRun) {\n lastStr = `${lastRun.status} ${lastRun.startedAt}`;\n }\n\n console.log(\n ` ${task.name.padEnd(24)} ${nextStr.padEnd(44)} last: ${lastStr}`\n );\n }\n\n if (errors.length > 0) {\n console.log(\"\");\n console.log(`Task file errors: ${errors.length}`);\n for (const { file, error } of errors) {\n console.log(` ${file}: ${error}`);\n }\n }\n\n const pendingOneoffs = db.listOneoffTasks({ status: \"pending\" });\n if (pendingOneoffs.length > 0) {\n console.log(\"\");\n console.log(`One-off tasks: ${pendingOneoffs.length} pending`);\n for (const task of pendingOneoffs) {\n console.log(\n ` ${task.id.slice(0, 12)}... \"${task.description}\" scheduled: ${task.scheduledAt}`\n );\n }\n }\n } finally {\n db.close();\n }\n}\n\nfunction log(message: string, level: \"info\" | \"error\" = \"info\"): void {\n const timestamp = new Date().toISOString();\n const prefix = `[${timestamp}]`;\n if (level === \"error\") {\n console.error(`${prefix} ERROR: ${message}`);\n } else {\n console.log(`${prefix} ${message}`);\n }\n}\n\n/**\n * Install the scheduled-tasks skill to ~/.config/opencode/skills/\n */\nfunction installSkill(): void {\n const cliPath = fileURLToPath(import.meta.url);\n const packageRoot = dirname(dirname(cliPath)); // dist/cli.js -> package root\n const skillSrc = join(packageRoot, \"skill\", \"SKILL.md\");\n\n if (!existsSync(skillSrc)) {\n // When running from source (src/cli.ts), try repo root\n const altSrc = join(dirname(dirname(cliPath)), \"skill\", \"SKILL.md\");\n if (!existsSync(altSrc)) {\n console.error(\"Could not find SKILL.md in the package.\");\n console.error(\"Looked at:\", skillSrc, \"and\", altSrc);\n process.exit(1);\n }\n doInstallSkill(altSrc);\n return;\n }\n\n doInstallSkill(skillSrc);\n}\n\nfunction doInstallSkill(srcPath: string): void {\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"\";\n const destDir = join(home, \".config\", \"opencode\", \"skills\", \"scheduled-tasks\");\n const destPath = join(destDir, \"SKILL.md\");\n\n mkdirSync(destDir, { recursive: true });\n copyFileSync(srcPath, destPath);\n\n console.log(\"Skill installed successfully!\");\n console.log(` Source: ${srcPath}`);\n console.log(` Installed to: ${destPath}`);\n console.log(\"\");\n console.log(\"The 'scheduled-tasks' skill is now available to OpenCode agents.\");\n console.log(\"Agents will automatically discover it and can load it when relevant.\");\n}\n\nfunction printUsage(): void {\n console.log(`opencode-scheduler - CLI for OpenCode scheduled tasks\n\nUsage:\n opencode-scheduler --run-once Run one scheduler tick\n opencode-scheduler --install Install the system scheduler (launchd/systemd)\n opencode-scheduler --uninstall Remove the system scheduler\n opencode-scheduler --install-skill Install the scheduled-tasks agent skill\n opencode-scheduler --status Show scheduler and task status\n opencode-scheduler --list List all tasks with next run times\n opencode-scheduler --help Show this help message\n\nInternal (used by spawned workers):\n opencode-scheduler --exec-task <runId> [--oneoff]\n`);\n}\n\n// --- Main ---\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const command = args[0];\n\n switch (command) {\n case \"--install\":\n await install();\n break;\n case \"--uninstall\":\n await uninstall();\n break;\n case \"--install-skill\":\n installSkill();\n break;\n case \"--status\":\n showStatus();\n break;\n case \"--list\":\n listTasks();\n break;\n case \"--help\":\n case \"-h\":\n printUsage();\n break;\n case \"--exec-task\": {\n const runId = args[1];\n if (!runId) {\n console.error(\"--exec-task requires a run ID\");\n process.exit(1);\n }\n const isOneoff = args.includes(\"--oneoff\");\n await execTask(runId, isOneoff);\n break;\n }\n case \"--run-once\":\n runTick();\n break;\n case undefined:\n printUsage();\n break;\n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n}\n\nmain().catch((err) => {\n console.error(\"Fatal error:\", err.message ?? err);\n process.exit(1);\n});\n","import { randomUUID } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { openDatabase, type Database } from \"./sqlite.js\";\nimport type {\n OneoffTask,\n OneoffTaskStatus,\n PermissionConfig,\n SessionMapping,\n TaskRun,\n TaskRunStatus,\n} from \"./types.js\";\n\nconst SCHEMA_VERSION = 2;\n\nconst SCHEMA_V1 = `\nCREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER PRIMARY KEY\n);\n\nCREATE TABLE IF NOT EXISTS oneoff_tasks (\n id TEXT PRIMARY KEY,\n description TEXT NOT NULL,\n prompt TEXT NOT NULL,\n cwd TEXT NOT NULL,\n scheduled_at TEXT NOT NULL,\n session_mode TEXT NOT NULL DEFAULT 'new',\n session_name TEXT,\n model TEXT,\n agent TEXT,\n permission TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n executed_at TEXT,\n session_id TEXT,\n error TEXT,\n created_by_session TEXT\n);\n\nCREATE TABLE IF NOT EXISTS task_runs (\n id TEXT PRIMARY KEY,\n task_name TEXT NOT NULL,\n started_at TEXT NOT NULL,\n completed_at TEXT,\n status TEXT NOT NULL DEFAULT 'running',\n session_id TEXT,\n error TEXT\n);\n\nCREATE TABLE IF NOT EXISTS session_map (\n session_name TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n task_name TEXT,\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n`;\n\nconst MIGRATION_V2 = `\nALTER TABLE task_runs ADD COLUMN pid INTEGER;\nALTER TABLE oneoff_tasks ADD COLUMN pid INTEGER;\n`;\n\nexport class TaskDatabase {\n private db: Database;\n\n constructor(dbPath: string) {\n mkdirSync(dirname(dbPath), { recursive: true });\n this.db = openDatabase(dbPath);\n this.db.pragma(\"journal_mode = WAL\");\n this.db.pragma(\"foreign_keys = ON\");\n this.initialize();\n }\n\n private initialize(): void {\n const versionExists = this.db\n .prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'\"\n )\n .get();\n\n if (!versionExists) {\n // Fresh database: create all tables, then run migrations\n this.db.exec(SCHEMA_V1);\n this.db\n .prepare(\"INSERT INTO schema_version (version) VALUES (?)\")\n .run(1);\n }\n\n const row = this.db\n .prepare(\"SELECT MAX(version) as version FROM schema_version\")\n .get() as { version: number } | undefined;\n const currentVersion = row?.version ?? 0;\n\n if (currentVersion < SCHEMA_VERSION) {\n this.migrate(currentVersion);\n }\n }\n\n private migrate(fromVersion: number): void {\n if (fromVersion < 2) {\n // V2: Add pid columns for async worker tracking\n // Run each ALTER separately since SQLite doesn't support multi-ALTER\n const statements = MIGRATION_V2.trim().split(\";\").filter(Boolean);\n for (const stmt of statements) {\n try {\n this.db.exec(stmt.trim() + \";\");\n } catch {\n // Column may already exist if migration was partially applied\n }\n }\n this.db\n .prepare(\n \"INSERT OR REPLACE INTO schema_version (version) VALUES (?)\"\n )\n .run(2);\n }\n }\n\n // --- One-off tasks ---\n\n createOneoffTask(task: {\n description: string;\n prompt: string;\n cwd: string;\n scheduledAt: string;\n sessionName?: string;\n model?: string;\n agent?: string;\n permission?: PermissionConfig;\n createdBySession?: string;\n }): OneoffTask {\n const id = randomUUID();\n this.db\n .prepare(\n `INSERT INTO oneoff_tasks (id, description, prompt, cwd, scheduled_at, session_name, model, agent, permission, created_by_session)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n id,\n task.description,\n task.prompt,\n task.cwd,\n task.scheduledAt,\n task.sessionName ?? null,\n task.model ?? null,\n task.agent ?? null,\n task.permission ? JSON.stringify(task.permission) : null,\n task.createdBySession ?? null\n );\n return this.getOneoffTask(id)!;\n }\n\n getOneoffTask(id: string): OneoffTask | undefined {\n const row = this.db\n .prepare(\"SELECT * FROM oneoff_tasks WHERE id = ?\")\n .get(id) as any;\n return row ? this.mapOneoffRow(row) : undefined;\n }\n\n listOneoffTasks(options?: {\n status?: OneoffTaskStatus | \"all\";\n }): OneoffTask[] {\n const status = options?.status ?? \"all\";\n let rows: any[];\n if (status === \"all\") {\n rows = this.db\n .prepare(\"SELECT * FROM oneoff_tasks ORDER BY scheduled_at ASC\")\n .all();\n } else {\n rows = this.db\n .prepare(\n \"SELECT * FROM oneoff_tasks WHERE status = ? ORDER BY scheduled_at ASC\"\n )\n .all(status);\n }\n return rows.map((r) => this.mapOneoffRow(r));\n }\n\n getDueOneoffTasks(): OneoffTask[] {\n return this.db\n .prepare(\n \"SELECT * FROM oneoff_tasks WHERE status = 'pending' AND scheduled_at <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now') ORDER BY scheduled_at ASC\"\n )\n .all()\n .map((r: any) => this.mapOneoffRow(r));\n }\n\n updateOneoffTaskStatus(\n id: string,\n status: OneoffTaskStatus,\n extra?: { sessionId?: string; error?: string; pid?: number }\n ): void {\n const executedAt =\n status === \"running\" ? new Date().toISOString() : undefined;\n this.db\n .prepare(\n `UPDATE oneoff_tasks SET status = ?, executed_at = COALESCE(?, executed_at), session_id = COALESCE(?, session_id), error = ?, pid = COALESCE(?, pid) WHERE id = ?`\n )\n .run(\n status,\n executedAt ?? null,\n extra?.sessionId ?? null,\n extra?.error ?? null,\n extra?.pid ?? null,\n id\n );\n }\n\n setTaskRunPid(id: string, pid: number): void {\n this.db\n .prepare(\"UPDATE task_runs SET pid = ? WHERE id = ?\")\n .run(pid, id);\n }\n\n cancelOneoffTask(id: string): boolean {\n const result = this.db\n .prepare(\n \"UPDATE oneoff_tasks SET status = 'cancelled' WHERE id = ? AND status = 'pending'\"\n )\n .run(id);\n return result.changes > 0;\n }\n\n // --- Task runs (recurring) ---\n\n createTaskRun(taskName: string, pid?: number): TaskRun {\n const id = randomUUID();\n const startedAt = new Date().toISOString();\n this.db\n .prepare(\n \"INSERT INTO task_runs (id, task_name, started_at, pid) VALUES (?, ?, ?, ?)\"\n )\n .run(id, taskName, startedAt, pid ?? null);\n return { id, taskName, startedAt, status: \"running\", pid };\n }\n\n completeTaskRun(\n id: string,\n status: \"completed\" | \"failed\",\n extra?: { sessionId?: string; error?: string }\n ): void {\n const completedAt = new Date().toISOString();\n this.db\n .prepare(\n `UPDATE task_runs SET status = ?, completed_at = ?, session_id = COALESCE(?, session_id), error = ? WHERE id = ?`\n )\n .run(\n status,\n completedAt,\n extra?.sessionId ?? null,\n extra?.error ?? null,\n id\n );\n }\n\n getLastTaskRun(taskName: string): TaskRun | undefined {\n const row = this.db\n .prepare(\n \"SELECT * FROM task_runs WHERE task_name = ? ORDER BY started_at DESC LIMIT 1\"\n )\n .get(taskName) as any;\n return row ? this.mapTaskRunRow(row) : undefined;\n }\n\n getLastSuccessfulTaskRun(taskName: string): TaskRun | undefined {\n const row = this.db\n .prepare(\n \"SELECT * FROM task_runs WHERE task_name = ? AND status = 'completed' ORDER BY started_at DESC LIMIT 1\"\n )\n .get(taskName) as any;\n return row ? this.mapTaskRunRow(row) : undefined;\n }\n\n getTaskRunHistory(taskName: string, limit: number = 10): TaskRun[] {\n return this.db\n .prepare(\n \"SELECT * FROM task_runs WHERE task_name = ? ORDER BY started_at DESC LIMIT ?\"\n )\n .all(taskName, limit)\n .map((r: any) => this.mapTaskRunRow(r));\n }\n\n hasRunningTask(taskName: string): boolean {\n const row = this.db\n .prepare(\n \"SELECT id FROM task_runs WHERE task_name = ? AND status = 'running' LIMIT 1\"\n )\n .get(taskName);\n return !!row;\n }\n\n hasRunningOneoffTask(id: string): boolean {\n const row = this.db\n .prepare(\n \"SELECT id FROM oneoff_tasks WHERE id = ? AND status = 'running' LIMIT 1\"\n )\n .get(id);\n return !!row;\n }\n\n /**\n * Get all running task runs (for PID-based reaping).\n */\n getRunningTaskRuns(): TaskRun[] {\n return this.db\n .prepare(\"SELECT * FROM task_runs WHERE status = 'running'\")\n .all()\n .map((r: any) => this.mapTaskRunRow(r));\n }\n\n /**\n * Get all running one-off tasks (for PID-based reaping).\n */\n getRunningOneoffTasks(): OneoffTask[] {\n return this.db\n .prepare(\"SELECT * FROM oneoff_tasks WHERE status = 'running'\")\n .all()\n .map((r: any) => this.mapOneoffRow(r));\n }\n\n /**\n * Mark stale running records as failed.\n * A record is stale if it has a PID set and that process is no longer alive,\n * or if it has no PID and is older than maxAgeMs (fallback for records\n * created before async execution was added).\n */\n cleanupStaleRuns(maxAgeMs: number = 2 * 60 * 60 * 1000): number {\n const cutoff = new Date(Date.now() - maxAgeMs).toISOString();\n\n // Clean up old records without PIDs (legacy / fallback)\n const taskRunResult = this.db\n .prepare(\n \"UPDATE task_runs SET status = 'failed', completed_at = datetime('now'), error = 'Timed out (stale running record)' WHERE status = 'running' AND pid IS NULL AND started_at < ?\"\n )\n .run(cutoff);\n\n const oneoffResult = this.db\n .prepare(\n \"UPDATE oneoff_tasks SET status = 'failed', error = 'Timed out (stale running record)' WHERE status = 'running' AND pid IS NULL AND executed_at < ?\"\n )\n .run(cutoff);\n\n return taskRunResult.changes + oneoffResult.changes;\n }\n\n // --- Session map ---\n\n getSessionMapping(sessionName: string): SessionMapping | undefined {\n const row = this.db\n .prepare(\"SELECT * FROM session_map WHERE session_name = ?\")\n .get(sessionName) as any;\n return row ? this.mapSessionMapRow(row) : undefined;\n }\n\n upsertSessionMapping(\n sessionName: string,\n sessionId: string,\n taskName?: string\n ): void {\n this.db\n .prepare(\n `INSERT INTO session_map (session_name, session_id, task_name, updated_at)\n VALUES (?, ?, ?, datetime('now'))\n ON CONFLICT(session_name) DO UPDATE SET\n session_id = excluded.session_id,\n task_name = COALESCE(excluded.task_name, session_map.task_name),\n updated_at = datetime('now')`\n )\n .run(sessionName, sessionId, taskName ?? null);\n }\n\n // --- Row mappers ---\n\n private mapOneoffRow(row: any): OneoffTask {\n return {\n id: row.id,\n description: row.description,\n prompt: row.prompt,\n cwd: row.cwd,\n scheduledAt: row.scheduled_at,\n sessionName: row.session_name ?? undefined,\n model: row.model ?? undefined,\n agent: row.agent ?? undefined,\n permission: row.permission ? JSON.parse(row.permission) : undefined,\n status: row.status as OneoffTaskStatus,\n createdAt: row.created_at,\n executedAt: row.executed_at ?? undefined,\n sessionId: row.session_id ?? undefined,\n error: row.error ?? undefined,\n createdBySession: row.created_by_session ?? undefined,\n pid: row.pid ?? undefined,\n };\n }\n\n private mapTaskRunRow(row: any): TaskRun {\n return {\n id: row.id,\n taskName: row.task_name,\n startedAt: row.started_at,\n completedAt: row.completed_at ?? undefined,\n status: row.status as TaskRunStatus,\n sessionId: row.session_id ?? undefined,\n error: row.error ?? undefined,\n pid: row.pid ?? undefined,\n };\n }\n\n private mapSessionMapRow(row: any): SessionMapping {\n return {\n sessionName: row.session_name,\n sessionId: row.session_id,\n taskName: row.task_name ?? undefined,\n updatedAt: row.updated_at,\n };\n }\n\n close(): void {\n this.db.close();\n }\n}\n\n/**\n * Get the default database path\n */\nexport function getDefaultDbPath(): string {\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"\";\n return `${home}/.config/opencode/.tasks.db`;\n}\n","/**\n * Runtime-agnostic SQLite abstraction.\n *\n * Uses bun:sqlite when running in Bun (OpenCode plugin runtime),\n * falls back to better-sqlite3 when running in Node.js (scheduler CLI).\n */\n\nimport { createRequire } from \"node:module\";\n\nexport interface Statement {\n run(...params: any[]): { changes: number };\n get(...params: any[]): any;\n all(...params: any[]): any[];\n}\n\nexport interface Database {\n exec(sql: string): void;\n prepare(sql: string): Statement;\n pragma(pragma: string): any;\n close(): void;\n}\n\n// Detect runtime\nconst isBun = typeof (globalThis as any).Bun !== \"undefined\";\n\n// createRequire gives us a CJS-style require() that works in ESM context\nconst require = createRequire(import.meta.url);\n\n/**\n * Open a SQLite database using the appropriate runtime driver.\n */\nexport function openDatabase(path: string): Database {\n if (isBun) {\n return openBunDatabase(path);\n }\n return openNodeDatabase(path);\n}\n\nfunction openBunDatabase(path: string): Database {\n // bun:sqlite is available in Bun's runtime\n const { Database: BunDatabase } = require(\"bun:sqlite\");\n const db = new BunDatabase(path);\n\n return {\n exec(sql: string) {\n db.exec(sql);\n },\n prepare(sql: string): Statement {\n const stmt = db.prepare(sql);\n return {\n run(...params: any[]) {\n const result = stmt.run(...params);\n return { changes: result.changes ?? 0 };\n },\n get(...params: any[]) {\n return stmt.get(...params);\n },\n all(...params: any[]) {\n return stmt.all(...params);\n },\n };\n },\n pragma(pragma: string) {\n return db.exec(`PRAGMA ${pragma}`);\n },\n close() {\n db.close();\n },\n };\n}\n\nfunction openNodeDatabase(path: string): Database {\n const BetterSqlite3 = require(\"better-sqlite3\");\n const db = new BetterSqlite3(path);\n\n return {\n exec(sql: string) {\n db.exec(sql);\n },\n prepare(sql: string): Statement {\n const stmt = db.prepare(sql);\n return {\n run(...params: any[]) {\n const result = stmt.run(...params);\n return { changes: result.changes ?? 0 };\n },\n get(...params: any[]) {\n return stmt.get(...params);\n },\n all(...params: any[]) {\n return stmt.all(...params);\n },\n };\n },\n pragma(pragma: string) {\n return db.pragma(pragma);\n },\n close() {\n db.close();\n },\n };\n}\n","import matter from \"gray-matter\";\nimport { readdirSync, readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport type { RecurringTask, TaskFrontmatter } from \"./types.js\";\n\n/**\n * Get the default tasks directory path\n */\nexport function getTasksDir(): string {\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"\";\n return join(home, \".config\", \"opencode\", \"tasks\");\n}\n\n/**\n * Expand ~ to home directory in a path\n */\nexport function expandPath(p: string): string {\n if (p.startsWith(\"~/\") || p === \"~\") {\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"\";\n return join(home, p.slice(2));\n }\n return p;\n}\n\n/**\n * Validate task frontmatter and return errors\n */\nfunction validateFrontmatter(\n data: Record<string, any>,\n fileName: string\n): string[] {\n const errors: string[] = [];\n\n if (data.description !== undefined && typeof data.description !== \"string\") {\n errors.push(\"Invalid 'description' field (must be a string)\");\n }\n\n if (!data.schedule || typeof data.schedule !== \"string\") {\n errors.push(\"Missing or invalid 'schedule' field\");\n }\n\n if (!data.cwd || typeof data.cwd !== \"string\") {\n errors.push(\"Missing or invalid 'cwd' field\");\n }\n\n if (data.session_name !== undefined && typeof data.session_name !== \"string\") {\n errors.push(\"Invalid 'session_name' field (must be a string)\");\n }\n\n if (data.model !== undefined && typeof data.model !== \"string\") {\n errors.push(\"Invalid 'model' field (must be a string)\");\n }\n\n if (data.agent !== undefined && typeof data.agent !== \"string\") {\n errors.push(\"Invalid 'agent' field (must be a string)\");\n }\n\n if (data.enabled !== undefined && typeof data.enabled !== \"boolean\") {\n errors.push(\"Invalid 'enabled' field (must be a boolean)\");\n }\n\n return errors;\n}\n\n/**\n * Parse a single task markdown file into a RecurringTask\n */\nexport function parseTaskFile(filePath: string): RecurringTask {\n const content = readFileSync(filePath, \"utf-8\");\n const fileName = basename(filePath);\n const { data, content: body } = matter(content);\n const fm = data as TaskFrontmatter;\n\n const errors = validateFrontmatter(data, fileName);\n if (errors.length > 0) {\n throw new Error(\n `Invalid task file \"${fileName}\":\\n - ${errors.join(\"\\n - \")}`\n );\n }\n\n const name = fileName.replace(/\\.md$/, \"\");\n\n return {\n name,\n description: fm.description,\n schedule: fm.schedule,\n cwd: fm.cwd,\n sessionName: fm.session_name,\n model: fm.model,\n agent: fm.agent,\n permission: fm.permission,\n enabled: fm.enabled ?? true,\n prompt: body.trim(),\n filePath,\n };\n}\n\n/**\n * Read all task files from the tasks directory.\n * Returns successfully parsed tasks and logs errors for invalid ones.\n */\nexport function readAllTasks(\n tasksDir?: string\n): { tasks: RecurringTask[]; errors: Array<{ file: string; error: string }> } {\n const dir = tasksDir ?? getTasksDir();\n const tasks: RecurringTask[] = [];\n const errors: Array<{ file: string; error: string }> = [];\n\n if (!existsSync(dir)) {\n return { tasks, errors };\n }\n\n const files = readdirSync(dir).filter((f) => f.endsWith(\".md\"));\n\n for (const file of files) {\n const filePath = join(dir, file);\n try {\n const task = parseTaskFile(filePath);\n tasks.push(task);\n } catch (err: any) {\n errors.push({ file, error: err.message });\n }\n }\n\n return { tasks, errors };\n}\n\n/**\n * Update the enabled field in a task's frontmatter.\n * Preserves the rest of the file content.\n */\nexport function setTaskEnabled(filePath: string, enabled: boolean): void {\n const content = readFileSync(filePath, \"utf-8\");\n const { data, content: body } = matter(content);\n data.enabled = enabled;\n const updated = matter.stringify(body, data);\n writeFileSync(filePath, updated);\n}\n","import cronParser from \"cron-parser\";\n\n// Handle CJS/ESM interop: in Node ESM, the default import is the module\n// namespace object; the actual class is at .CronExpressionParser or .default\nconst CronExpressionParser =\n (cronParser as any).CronExpressionParser ?? cronParser;\n\n/**\n * Check if a cron expression is valid\n */\nexport function isValidCron(expression: string): boolean {\n try {\n CronExpressionParser.parse(expression);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get the next run time for a cron expression after the given date.\n * Returns an ISO 8601 string.\n */\nexport function getNextRunTime(expression: string, after?: Date): string {\n const expr = CronExpressionParser.parse(expression, {\n currentDate: after ?? new Date(),\n });\n const next = expr.next().toISOString();\n if (!next) throw new Error(`No next run time for expression \"${expression}\"`);\n return next;\n}\n\n/**\n * Get the previous run time for a cron expression before the given date.\n * Returns an ISO 8601 string.\n */\nexport function getPreviousRunTime(\n expression: string,\n before?: Date\n): string {\n const expr = CronExpressionParser.parse(expression, {\n currentDate: before ?? new Date(),\n });\n const prev = expr.prev().toISOString();\n if (!prev) throw new Error(`No previous run time for expression \"${expression}\"`);\n return prev;\n}\n\n/**\n * Determine if a recurring task is due for execution.\n *\n * A task is due if:\n * - It has never run before, OR\n * - The last run was before the most recent cron trigger time\n *\n * For tasks that have never run, we only consider them due if the previous\n * trigger is within the last 24 hours (to avoid running very old tasks on\n * first install).\n *\n * @param expression - Cron expression\n * @param lastRunTime - ISO 8601 timestamp of last successful run, or undefined if never run\n * @returns true if the task should be executed now\n */\nexport function isDue(expression: string, lastRunTime?: string): boolean {\n if (!lastRunTime) {\n // Never run before - check if the cron has a trigger time in the past\n // within the last 24 hours\n const prevTrigger = new Date(getPreviousRunTime(expression));\n const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);\n return prevTrigger >= twentyFourHoursAgo;\n }\n\n // Find the most recent cron trigger time\n const prevTrigger = new Date(getPreviousRunTime(expression));\n const lastRun = new Date(lastRunTime);\n\n // Task is due if the most recent trigger is after the last run\n return prevTrigger > lastRun;\n}\n","import { spawn } from \"node:child_process\";\nimport type { TaskDatabase } from \"./db.js\";\nimport type { TaskExecConfig } from \"./types.js\";\nimport { expandPath } from \"./tasks.js\";\n\n/**\n * Parse a session ID from opencode's JSON-formatted output.\n *\n * When `opencode run --format json` is used, events are emitted as\n * newline-delimited JSON. We look for session-related events that\n * contain a session ID.\n */\nexport function parseSessionIdFromJsonOutput(\n output: string\n): string | undefined {\n for (const line of output.split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n const event = JSON.parse(line);\n if (event.properties?.info?.id?.startsWith(\"ses_\")) {\n return event.properties.info.id;\n }\n if (event.sessionID?.startsWith(\"ses_\")) {\n return event.sessionID;\n }\n if (event.properties?.sessionID?.startsWith(\"ses_\")) {\n return event.properties.sessionID;\n }\n } catch {\n // Not JSON, skip\n }\n }\n return undefined;\n}\n\n/**\n * Check if a process with the given PID is still alive.\n */\nexport function isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Build the opencode run command args and environment for a task.\n */\nexport function buildTaskCommand(\n task: TaskExecConfig,\n db: TaskDatabase\n): { args: string[]; env: Record<string, string>; cwd: string } {\n const sessionArgs: string[] = [];\n if (task.sessionName) {\n const mapping = db.getSessionMapping(task.sessionName);\n if (mapping) {\n sessionArgs.push(\"--session\", mapping.sessionId);\n } else {\n sessionArgs.push(\"--title\", task.sessionName);\n }\n } else {\n sessionArgs.push(\n \"--title\",\n `${task.name} - ${new Date().toISOString()}`\n );\n }\n\n const args = [\"run\", ...sessionArgs, \"--format\", \"json\"];\n if (task.model) args.push(\"--model\", task.model);\n if (task.agent) args.push(\"--agent\", task.agent);\n args.push(task.prompt);\n\n const env: Record<string, string> = {};\n for (const [k, v] of Object.entries(process.env)) {\n if (v !== undefined) env[k] = v;\n }\n if (task.permission) {\n env.OPENCODE_PERMISSION = JSON.stringify(task.permission);\n }\n\n return { args, env, cwd: expandPath(task.cwd) };\n}\n\n/**\n * Spawn a worker process to execute a task asynchronously.\n *\n * The worker is another invocation of the scheduler script with\n * --exec-task, which runs the task synchronously and updates the DB\n * when done. This function returns immediately with the PID.\n */\nexport function spawnWorker(\n schedulerPath: string,\n runId: string,\n isOneoff: boolean\n): number {\n const args = [\"--exec-task\", runId];\n if (isOneoff) args.push(\"--oneoff\");\n\n const child = spawn(process.execPath, [schedulerPath, ...args], {\n detached: true,\n stdio: \"ignore\",\n });\n\n child.unref();\n\n if (!child.pid) {\n throw new Error(\"Failed to spawn worker process\");\n }\n\n return child.pid;\n}\n\n/**\n * Execute a task synchronously and update the DB with the result.\n *\n * This is called by the --exec-task worker subprocess. It runs\n * `opencode run` to completion, parses the output, and writes\n * the result back to the DB.\n */\nexport async function execTaskAndUpdateDb(\n task: TaskExecConfig,\n runId: string,\n isOneoff: boolean,\n db: TaskDatabase\n): Promise<void> {\n const { args, env, cwd } = buildTaskCommand(task, db);\n\n // Use spawn instead of execFile -- opencode run can hang with\n // execFile due to TTY detection / buffer issues\n const { stdout, stderr, exitCode } = await new Promise<{\n stdout: string;\n stderr: string;\n exitCode: number;\n }>((resolve) => {\n const child = spawn(\"opencode\", args, {\n cwd,\n env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout.on(\"data\", (data: Buffer) => {\n stdout += data.toString();\n });\n\n child.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n child.on(\"close\", (code) => {\n resolve({ stdout, stderr, exitCode: code ?? 1 });\n });\n\n child.on(\"error\", (err) => {\n resolve({ stdout, stderr: err.message, exitCode: 1 });\n });\n });\n\n const success = exitCode === 0;\n\n const sessionId = parseSessionIdFromJsonOutput(stdout);\n\n // Update session map if needed\n if (task.sessionName && sessionId) {\n db.upsertSessionMapping(task.sessionName, sessionId, task.name);\n }\n\n // Update DB record\n if (isOneoff) {\n db.updateOneoffTaskStatus(runId, success ? \"completed\" : \"failed\", {\n sessionId,\n error: success ? undefined : stderr.slice(0, 4096),\n });\n } else {\n db.completeTaskRun(runId, success ? \"completed\" : \"failed\", {\n sessionId,\n error: success ? undefined : stderr.slice(0, 4096),\n });\n }\n}\n","import { execFileSync } from \"node:child_process\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport type Platform = \"macos-launchd\" | \"linux-systemd\" | \"unsupported\";\n\nconst LAUNCHD_LABEL = \"ai.opencode.scheduled-tasks\";\nconst SYSTEMD_SERVICE = \"opencode-scheduler.service\";\nconst SYSTEMD_TIMER = \"opencode-scheduler.timer\";\n\n/**\n * Detect the platform and init system\n */\nexport function detectPlatform(): Platform {\n if (process.platform === \"darwin\") return \"macos-launchd\";\n if (process.platform === \"linux\") {\n try {\n execFileSync(\"systemctl\", [\"--version\"], { stdio: \"ignore\" });\n return \"linux-systemd\";\n } catch {\n // systemctl not found or not working\n }\n }\n return \"unsupported\";\n}\n\n/**\n * Resolve the absolute path to the CLI script.\n *\n * Since tsup bundles installer.ts into cli.js, import.meta.url\n * already points to the CLI script when running from the bundle.\n * When running from source (ts-node/tsx), we walk up to find dist/cli.js.\n * As a final fallback, we look for the `opencode-scheduler` bin on PATH.\n */\nfunction resolveSchedulerPath(): string {\n const thisFile = fileURLToPath(import.meta.url);\n\n // Case 1: We ARE the CLI script (bundled by tsup)\n if (basename(thisFile) === \"cli.js\") {\n return resolve(thisFile);\n }\n\n // Case 2: Running from source (src/lib/installer.ts)\n // Walk up to find dist/cli.js\n const candidates = [\n join(dirname(dirname(thisFile)), \"..\", \"dist\", \"cli.js\"), // from src/lib/\n join(dirname(thisFile), \"..\", \"dist\", \"cli.js\"), // from src/\n join(dirname(dirname(thisFile)), \"cli.js\"), // from dist/lib/ (if unbundled)\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return resolve(candidate);\n }\n }\n\n // Case 3: Fallback to PATH lookup\n try {\n const result = execFileSync(\"which\", [\"opencode-scheduler\"], {\n encoding: \"utf-8\",\n }).trim();\n if (result) return resolve(result);\n } catch {\n // not found\n }\n\n throw new Error(\n \"Could not find the opencode-scheduler script. \" +\n \"Make sure the package is properly installed.\"\n );\n}\n\nfunction getHome(): string {\n return process.env.HOME ?? process.env.USERPROFILE ?? \"\";\n}\n\nfunction getLogDir(): string {\n const dir = join(getHome(), \".local\", \"share\", \"opencode\");\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n\n// --- macOS launchd ---\n\nfunction getLaunchdPlistPath(): string {\n return join(\n getHome(),\n \"Library\",\n \"LaunchAgents\",\n `${LAUNCHD_LABEL}.plist`\n );\n}\n\nfunction generateLaunchdPlist(\n nodePath: string,\n schedulerPath: string\n): string {\n const logDir = getLogDir();\n const currentPath = process.env.PATH ?? \"/usr/local/bin:/usr/bin:/bin\";\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\"\n \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${LAUNCHD_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${nodePath}</string>\n <string>${schedulerPath}</string>\n <string>--run-once</string>\n </array>\n <key>StartInterval</key>\n <integer>60</integer>\n <key>StandardOutPath</key>\n <string>${join(logDir, \"scheduler.log\")}</string>\n <key>StandardErrorPath</key>\n <string>${join(logDir, \"scheduler.err\")}</string>\n <key>RunAtLoad</key>\n <true/>\n <key>EnvironmentVariables</key>\n <dict>\n <key>PATH</key>\n <string>${currentPath}</string>\n </dict>\n</dict>\n</plist>`;\n}\n\nasync function installLaunchd(): Promise<void> {\n const nodePath = process.execPath;\n const schedulerPath = resolveSchedulerPath();\n const plistPath = getLaunchdPlistPath();\n\n // Ensure LaunchAgents directory exists\n mkdirSync(dirname(plistPath), { recursive: true });\n\n // Unload if already loaded\n try {\n execFileSync(\"launchctl\", [\"unload\", plistPath], { stdio: \"ignore\" });\n } catch {\n // Not loaded, that's fine\n }\n\n // Write plist\n const plist = generateLaunchdPlist(nodePath, schedulerPath);\n writeFileSync(plistPath, plist);\n\n // Load\n execFileSync(\"launchctl\", [\"load\", plistPath]);\n\n console.log(\"Scheduler installed (macOS launchd)\");\n console.log(` Plist: ${plistPath}`);\n console.log(` Node: ${nodePath}`);\n console.log(` Script: ${schedulerPath}`);\n console.log(` Interval: every 60 seconds`);\n console.log(` Logs: ${getLogDir()}/scheduler.{log,err}`);\n}\n\nasync function uninstallLaunchd(): Promise<void> {\n const plistPath = getLaunchdPlistPath();\n\n if (!existsSync(plistPath)) {\n console.log(\"Scheduler is not installed (no launchd plist found)\");\n return;\n }\n\n try {\n execFileSync(\"launchctl\", [\"unload\", plistPath], { stdio: \"ignore\" });\n } catch {\n // Already unloaded\n }\n\n unlinkSync(plistPath);\n console.log(\"Scheduler uninstalled (macOS launchd)\");\n console.log(` Removed: ${plistPath}`);\n}\n\nfunction isLaunchdInstalled(): boolean {\n return existsSync(getLaunchdPlistPath());\n}\n\n// --- Linux systemd ---\n\nfunction getSystemdDir(): string {\n return join(getHome(), \".config\", \"systemd\", \"user\");\n}\n\nfunction generateSystemdService(\n nodePath: string,\n schedulerPath: string\n): string {\n const currentPath = process.env.PATH ?? \"/usr/local/bin:/usr/bin:/bin\";\n\n return `[Unit]\nDescription=OpenCode Scheduled Tasks Runner\n\n[Service]\nType=oneshot\nExecStart=${nodePath} ${schedulerPath} --run-once\nEnvironment=PATH=${currentPath}\n`;\n}\n\nfunction generateSystemdTimer(): string {\n return `[Unit]\nDescription=OpenCode Scheduled Tasks Timer\n\n[Timer]\nOnBootSec=60\nOnUnitActiveSec=60\nAccuracySec=1s\n\n[Install]\nWantedBy=timers.target\n`;\n}\n\nasync function installSystemd(): Promise<void> {\n const nodePath = process.execPath;\n const schedulerPath = resolveSchedulerPath();\n const systemdDir = getSystemdDir();\n\n mkdirSync(systemdDir, { recursive: true });\n\n const servicePath = join(systemdDir, SYSTEMD_SERVICE);\n const timerPath = join(systemdDir, SYSTEMD_TIMER);\n\n // Stop if already running\n try {\n execFileSync(\"systemctl\", [\"--user\", \"stop\", SYSTEMD_TIMER], {\n stdio: \"ignore\",\n });\n } catch {\n // Not running\n }\n\n // Write unit files\n writeFileSync(servicePath, generateSystemdService(nodePath, schedulerPath));\n writeFileSync(timerPath, generateSystemdTimer());\n\n // Reload, enable, start\n execFileSync(\"systemctl\", [\"--user\", \"daemon-reload\"]);\n execFileSync(\"systemctl\", [\"--user\", \"enable\", SYSTEMD_TIMER]);\n execFileSync(\"systemctl\", [\"--user\", \"start\", SYSTEMD_TIMER]);\n\n console.log(\"Scheduler installed (Linux systemd)\");\n console.log(` Service: ${servicePath}`);\n console.log(` Timer: ${timerPath}`);\n console.log(` Node: ${nodePath}`);\n console.log(` Script: ${schedulerPath}`);\n console.log(` Interval: every 60 seconds`);\n}\n\nasync function uninstallSystemd(): Promise<void> {\n const systemdDir = getSystemdDir();\n const servicePath = join(systemdDir, SYSTEMD_SERVICE);\n const timerPath = join(systemdDir, SYSTEMD_TIMER);\n\n if (!existsSync(timerPath) && !existsSync(servicePath)) {\n console.log(\"Scheduler is not installed (no systemd units found)\");\n return;\n }\n\n try {\n execFileSync(\"systemctl\", [\"--user\", \"stop\", SYSTEMD_TIMER], {\n stdio: \"ignore\",\n });\n execFileSync(\"systemctl\", [\"--user\", \"disable\", SYSTEMD_TIMER], {\n stdio: \"ignore\",\n });\n } catch {\n // Already stopped/disabled\n }\n\n if (existsSync(servicePath)) unlinkSync(servicePath);\n if (existsSync(timerPath)) unlinkSync(timerPath);\n\n try {\n execFileSync(\"systemctl\", [\"--user\", \"daemon-reload\"]);\n } catch {\n // Best effort\n }\n\n console.log(\"Scheduler uninstalled (Linux systemd)\");\n console.log(` Removed: ${servicePath}`);\n console.log(` Removed: ${timerPath}`);\n}\n\nfunction isSystemdInstalled(): boolean {\n const systemdDir = getSystemdDir();\n return existsSync(join(systemdDir, SYSTEMD_TIMER));\n}\n\n// --- Public API ---\n\n/**\n * Install the scheduler for the detected platform\n */\nexport async function install(): Promise<void> {\n const platform = detectPlatform();\n\n switch (platform) {\n case \"macos-launchd\":\n await installLaunchd();\n break;\n case \"linux-systemd\":\n await installSystemd();\n break;\n case \"unsupported\":\n console.error(\n \"Unsupported platform. Supported: macOS (launchd), Linux (systemd).\"\n );\n console.error(\"You can still run the scheduler manually:\");\n console.error(\" npx opencode-scheduler --run-once\");\n process.exit(1);\n }\n}\n\n/**\n * Uninstall the scheduler for the detected platform\n */\nexport async function uninstall(): Promise<void> {\n const platform = detectPlatform();\n\n switch (platform) {\n case \"macos-launchd\":\n await uninstallLaunchd();\n break;\n case \"linux-systemd\":\n await uninstallSystemd();\n break;\n case \"unsupported\":\n console.error(\"No supported init system found.\");\n process.exit(1);\n }\n}\n\n/**\n * Check if the scheduler is installed\n */\nexport function isInstalled(): boolean {\n const platform = detectPlatform();\n switch (platform) {\n case \"macos-launchd\":\n return isLaunchdInstalled();\n case \"linux-systemd\":\n return isSystemdInstalled();\n default:\n return false;\n }\n}\n\n/**\n * Get info about the current installation\n */\nexport function getInstallInfo(): {\n installed: boolean;\n platform: Platform;\n details?: string;\n} {\n const platform = detectPlatform();\n const installed = isInstalled();\n\n let details: string | undefined;\n if (installed) {\n switch (platform) {\n case \"macos-launchd\":\n details = `Plist: ${getLaunchdPlistPath()}`;\n break;\n case \"linux-systemd\":\n details = `Timer: ${join(getSystemdDir(), SYSTEMD_TIMER)}`;\n break;\n }\n }\n\n return { installed, platform, details };\n}\n"],"mappings":"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","names":["fileURLToPath","existsSync","mkdirSync","dirname","join","require","prevTrigger","resolve","stdout","stderr","existsSync","mkdirSync","writeFileSync","basename","dirname","join","fileURLToPath","dirname","join","existsSync","mkdirSync"]}
|