Spaces:
Sleeping
Sleeping
| const express = require("express"); | |
| const puppeteer = require("puppeteer-core"); | |
| const app = express(); | |
| app.use(express.json()); | |
| // CORS — allow requests from any frontend | |
| app.use((req, res, next) => { | |
| res.header("Access-Control-Allow-Origin", "*"); | |
| res.header("Access-Control-Allow-Headers", "Content-Type"); | |
| res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); | |
| if (req.method === "OPTIONS") return res.sendStatus(200); | |
| next(); | |
| }); | |
| const CF_ACCOUNT_ID = process.env.CF_ACCOUNT_ID; | |
| const CF_API_TOKEN = process.env.CF_API_TOKEN; | |
| const CF_MODEL = "@cf/meta/llama-3.1-8b-instruct"; | |
| app.get("/health", (req, res) => { | |
| res.json({ status: "ok" }); | |
| }); | |
| app.get("/debug", async (req, res) => { | |
| const hasAccountId = !!process.env.CF_ACCOUNT_ID; | |
| const hasToken = !!process.env.CF_API_TOKEN; | |
| let networkTest = "not attempted"; | |
| try { | |
| const r = await fetch("https://api.cloudflare.com/client/v4/ips"); | |
| networkTest = `reached Cloudflare, status ${r.status}`; | |
| } catch (err) { | |
| networkTest = `failed: ${err.message}`; | |
| } | |
| res.json({ hasAccountId, hasToken, networkTest }); | |
| }); | |
| async function getPlanFromAI(instruction) { | |
| const url = `https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/ai/run/${CF_MODEL}`; | |
| const response = await fetch(url, { | |
| method: "POST", | |
| headers: { | |
| Authorization: `Bearer ${CF_API_TOKEN}`, | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ | |
| messages: [ | |
| { | |
| role: "system", | |
| content: | |
| 'You convert a user\'s browsing instruction into a single JSON object: {"url": "<the website to visit>"}. ' + | |
| "Only output valid JSON, nothing else. If no clear URL or site is mentioned, guess the most likely official site.", | |
| }, | |
| { role: "user", content: instruction }, | |
| ], | |
| }), | |
| }); | |
| const data = await response.json(); | |
| if (!data.success) { | |
| throw new Error("Cloudflare AI error: " + JSON.stringify(data.errors)); | |
| } | |
| const raw = data.result.response; | |
| try { | |
| return JSON.parse(raw); | |
| } catch { | |
| throw new Error("AI did not return valid JSON: " + raw); | |
| } | |
| } | |
| app.post("/run-task", async (req, res) => { | |
| const { instruction } = req.body; | |
| if (!instruction) return res.status(400).json({ error: "Missing 'instruction' in request body" }); | |
| let plan; | |
| try { | |
| plan = await getPlanFromAI(instruction); | |
| } catch (err) { | |
| return res.status(502).json({ success: false, stage: "ai", error: err.message }); | |
| } | |
| if (!plan.url) { | |
| return res.status(502).json({ success: false, error: "AI plan missing 'url'", plan }); | |
| } | |
| let browser; | |
| try { | |
| browser = await puppeteer.launch({ | |
| executablePath: process.env.PUPPETEER_EXECUTABLE_PATH, | |
| args: ["--no-sandbox", "--disable-setuid-sandbox"], | |
| headless: true, | |
| }); | |
| const page = await browser.newPage(); | |
| await page.goto(plan.url, { waitUntil: "domcontentloaded", timeout: 30000 }); | |
| const title = await page.title(); | |
| res.json({ success: true, instruction, plan, title }); | |
| } catch (err) { | |
| res.status(500).json({ success: false, stage: "puppeteer", error: err.message }); | |
| } finally { | |
| if (browser) await browser.close(); | |
| } | |
| }); | |
| const PORT = process.env.PORT || 7860; | |
| app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); |