export const prerender = false; import type { APIRoute } from "astro"; import { spawn } from "node:child_process"; import { readFile, writeFile, mkdir } from "node:fs/promises"; import { resolve, dirname } from "node:path"; const REPO = process.env.REPO_ROOT || resolve("/Users/ace/palacering"); const DATA = resolve(REPO, "palaces", "manglasabang", "palaceappsdata", "palacehealth"); function dayPath(date: string) { const [y, m, d] = date.split("-"); return resolve(DATA, y, m, `${d}.json`); } async function readDay(date: string) { try { return JSON.parse(await readFile(dayPath(date), "utf-8")); } catch { return null; } } function json(data: object, status = 200) { return new Response(JSON.stringify(data), { status, headers: { "Content-Type": "application/json" }, }); } export const POST: APIRoute = async ({ request }) => { const { message, date } = await request.json(); if (!message || !date) return json({ error: "message and date required" }, 400); const existing = await readDay(date); const existingStr = existing ? JSON.stringify(existing, null, 2) : "{}"; const now = new Date(); const timeNow = now.toTimeString().slice(0, 5); const prompt = `You are the Palace Health butler. The user said: "${message}" Current time: ${timeNow} Today's date: ${date} Current health data for today: ${existingStr} Based on what the user said, determine what health data to record. Respond with ONLY a JSON object with this structure: { "section": "food" | "exercise" | "sleep" | "symptoms" | "metrics", "entry": { ... } | null, "sleep": { ... } | null, "metrics": { ... } | null, "_message": "Brief confirmation message to show the user" } Rules: - For food: entry = { "time": "HH:MM", "what": "...", "tags": [...] } - For exercise: entry = { "time": "HH:MM", "what": "...", "tags": [...] } - For symptoms: entry = { "time": "HH:MM", "what": "...", "severity": 1-5 } - For sleep: sleep = { "bedtime": "HH:MM", "wakeup": "HH:MM", "quality": "good|fair|poor|excellent", "note": "..." } - For metrics: metrics = { "weight": number, "steps": number, "bp": "120/80" } (only include fields mentioned) - If time not specified, use current time ${timeNow} - Tags for food: vegetable, fruit, protein, grain, dairy, processed, beverage, supplement - Only return valid JSON. No markdown, no explanation.`; return new Promise((resolve) => { const child = spawn("claude", ["-p", "--output-format", "json"], { env: { ...process.env }, }); let stdout = ""; child.stdout.on("data", (d: Buffer) => { stdout += d.toString(); }); child.on("close", async () => { try { const result = JSON.parse(stdout); const text = result.result || result.content || stdout; const cleaned = text.replace(/```json\n?/g, "").replace(/```\n?/g, "").trim(); const parsed = JSON.parse(cleaned); const fp = dayPath(date); let day: any; try { day = JSON.parse(await readFile(fp, "utf-8")); } catch { const DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; day = { date, dayOfWeek: DAYS[new Date(date + "T12:00:00").getDay()], food: [], exercise: [], sleep: null, symptoms: [], metrics: {} }; } const { section, entry, sleep, metrics } = parsed; if (section === "food" && entry) { day.food.push(entry); day.food.sort((a: any, b: any) => a.time.localeCompare(b.time)); } else if (section === "exercise" && entry) { day.exercise.push(entry); day.exercise.sort((a: any, b: any) => a.time.localeCompare(b.time)); } else if (section === "symptoms" && entry) { day.symptoms.push(entry); day.symptoms.sort((a: any, b: any) => a.time.localeCompare(b.time)); } else if (section === "sleep" && sleep) { day.sleep = sleep; } else if (section === "metrics" && metrics) { day.metrics = { ...day.metrics, ...metrics }; } await mkdir(dirname(fp), { recursive: true }); await writeFile(fp, JSON.stringify(day, null, 2) + "\n"); resolve(new Response(JSON.stringify({ _message: parsed._message || "Recorded" }), { status: 200, headers: { "Content-Type": "application/json" }, })); } catch { resolve(new Response(JSON.stringify({ error: "Failed to parse" }), { status: 500, headers: { "Content-Type": "application/json" }, })); } }); child.stdin.write(prompt); child.stdin.end(); }); };