import { readdir } from "fs/promises"; import { join } from "path"; const JOBS_DIR = join(process.cwd(), ".qwen", "qwenclaw", "jobs"); export interface Job { name: string; schedule: string; prompt: string; recurring: boolean; notify: true | false | "error"; } function parseFrontmatterValue(raw: string): string { return raw.trim().replace(/^["']|["']$/g, ""); } function parseJobFile(name: string, content: string): Job | null { const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/); if (!match) { console.error(`Invalid job file format: ${name}`); return null; } const frontmatter = match[1]; const prompt = match[2].trim(); const lines = frontmatter.split("\n").map((l) => l.trim()); const scheduleLine = lines.find((l) => l.startsWith("schedule:")); if (!scheduleLine) { return null; } const schedule = parseFrontmatterValue(scheduleLine.replace("schedule:", "")); const recurringLine = lines.find((l) => l.startsWith("recurring:")); const dailyLine = lines.find((l) => l.startsWith("daily:")); // legacy alias const recurringRaw = recurringLine ? parseFrontmatterValue(recurringLine.replace("recurring:", "")).toLowerCase() : dailyLine ? parseFrontmatterValue(dailyLine.replace("daily:", "")).toLowerCase() : ""; const recurring = recurringRaw === "true" || recurringRaw === "yes" || recurringRaw === "1"; const notifyLine = lines.find((l) => l.startsWith("notify:")); const notifyRaw = notifyLine ? parseFrontmatterValue(notifyLine.replace("notify:", "")).toLowerCase() : ""; const notify: true | false | "error" = notifyRaw === "false" || notifyRaw === "no" ? false : notifyRaw === "error" ? "error" : true; return { name, schedule, prompt, recurring, notify }; } export async function loadJobs(): Promise { const jobs: Job[] = []; let files: string[]; try { files = await readdir(JOBS_DIR); } catch { return jobs; } for (const file of files) { if (!file.endsWith(".md")) continue; const content = await Bun.file(join(JOBS_DIR, file)).text(); const job = parseJobFile(file.replace(/\.md$/, ""), content); if (job) jobs.push(job); } return jobs; } export async function clearJobSchedule(jobName: string): Promise { const path = join(JOBS_DIR, `${jobName}.md`); const content = await Bun.file(path).text(); const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/); if (!match) return; const filteredFrontmatter = match[1] .split("\n") .filter((line) => !line.trim().startsWith("schedule:")) .join("\n") .trim(); const body = match[2].trim(); const next = `---\n${filteredFrontmatter}\n---\n${body}\n`; await Bun.write(path, next); }