diff --git a/README.md b/README.md index 6ed8cde..e329425 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,28 @@ A simple, automatically updated site providing the latest download links for the **Live Site:** [downloadcursor.app](https://downloadcursor.app) +### JSON API (LLM-friendly) + +- **Latest release (all platforms):** `https://downloadcursor.app/api/latest-download` +- **Latest for a specific platform:** `https://downloadcursor.app/api/latest-download?platform=` + +Supported platform keys include: `win32-x64-user`, `win32-x64-system`, `win32-arm64-user`, `win32-arm64-system`, `darwin-universal`, `darwin-arm64`, `darwin-x64`, `linux-x64`, `linux-arm64`. Aliases accepted: `windows`, `windows-user`, `windows-system`, `mac`, `macos`, `macos-arm64`, `macos-x64`, `linux`. + +Example response (platform-filtered): + +```json +{ + "version": "", + "date": "", + "platform": "win32-x64-user", + "url": "https://downloads.cursor.com/.../CursorUserSetup-x64-.exe", + "sizeBytes": , + "sha256": "" +} +``` + +For the full machine-readable history, you can also use `https://downloadcursor.app/version-history.json`. + ![GitHub stars](https://img.shields.io/github/stars/accesstechnology-mike/cursor-downloads?style=social) ![Last commit](https://img.shields.io/github/last-commit/accesstechnology-mike/cursor-downloads) ![Update workflow](https://img.shields.io/github/actions/workflow/status/accesstechnology-mike/cursor-downloads/update.yml?branch=main) diff --git a/api/latest-download.js b/api/latest-download.js new file mode 100644 index 0000000..f050814 --- /dev/null +++ b/api/latest-download.js @@ -0,0 +1,114 @@ +const fs = require("fs"); +const path = require("path"); + +async function readVersionHistory(req) { + const candidatePaths = [ + path.join(process.cwd(), "version-history.json"), + path.join(__dirname, "..", "version-history.json"), + ]; + + for (const historyPath of candidatePaths) { + try { + if (fs.existsSync(historyPath)) { + const raw = fs.readFileSync(historyPath, "utf-8"); + return JSON.parse(raw); + } + } catch {} + } + + // Fallback to HTTP fetch from the same host if local file not available + try { + const base = req?.headers?.host + ? `https://${req.headers.host}` + : "https://downloadcursor.app"; + const res = await fetch(new URL("/version-history.json", base)); + if (res.ok) { + return await res.json(); + } + } catch {} + + throw new Error("version-history.json not found"); +} + +function getLatestVersion(history) { + if (!history || !Array.isArray(history.versions) || history.versions.length === 0) { + return null; + } + return history.versions[0]; +} + +function normalizePlatformParam(param) { + if (!param) return null; + const p = String(param).toLowerCase(); + // Accept canonical keys exactly as in version-history.json + // Also accept a few friendly aliases commonly used by users/tools + const aliases = { + windows: "win32-x64-user", + "windows-user": "win32-x64-user", + "windows-system": "win32-x64-system", + mac: "darwin-universal", + macos: "darwin-universal", + "macos-arm64": "darwin-arm64", + "macos-x64": "darwin-x64", + linux: "linux-x64", + "linux-x64": "linux-x64", + "linux-arm64": "linux-arm64", + }; + return aliases[p] || param; // if user passed canonical key, keep as-is +} + +module.exports = async (req, res) => { + if (req.method !== "GET") { + return res.status(405).json({ error: "Method not allowed" }); + } + + res.setHeader("Content-Type", "application/json; charset=utf-8"); + res.setHeader("Cache-Control", "no-store, must-revalidate"); + + try { + const history = await readVersionHistory(req); + const latest = getLatestVersion(history); + + if (!latest) { + return res.status(500).json({ error: "No version data available" }); + } + + const urlParams = new URL(req.url, `http://${req.headers.host}`); + const platformParam = normalizePlatformParam(urlParams.searchParams.get("platform")); + + // If a platform is specified, return a minimal, LLM-friendly JSON for that platform + if (platformParam) { + const url = latest.platforms?.[platformParam]; + const details = latest.platformDetails?.[platformParam]; + + if (!url) { + const available = Object.keys(latest.platforms || {}); + return res.status(404).json({ + error: "Unknown or unavailable platform", + platform: platformParam, + availablePlatforms: available, + }); + } + + return res.status(200).json({ + version: latest.version, + date: latest.date, + platform: platformParam, + url, + sizeBytes: details?.sizeBytes ?? null, + sha256: details?.sha256 ?? null, + }); + } + + // Otherwise return concise metadata for the latest release + return res.status(200).json({ + version: latest.version, + date: latest.date, + platforms: latest.platforms || {}, + platformDetails: latest.platformDetails || {}, + }); + } catch (err) { + console.error("/api/latest-download error:", err); + return res.status(500).json({ error: "Internal server error" }); + } +}; diff --git a/index.html b/index.html index d245733..cac67b6 100644 --- a/index.html +++ b/index.html @@ -2693,6 +2693,9 @@

>GitHub. +
+ JSON API: /api/latest-download (optionally ?platform=<key>) +
\` + +Supported platform keys include: \`win32-x64-user\`, \`win32-x64-system\`, \`win32-arm64-user\`, \`win32-arm64-system\`, \`darwin-universal\`, \`darwin-arm64\`, \`darwin-x64\`, \`linux-x64\`, \`linux-arm64\`. Aliases accepted: \`windows\`, \`windows-user\`, \`windows-system\`, \`mac\`, \`macos\`, \`macos-arm64\`, \`macos-x64\`, \`linux\`. + +Example response (platform-filtered): + +\`\`\`json +{ + "version": "${latestEntry.version}", + "date": "${latestEntry.date}", + "platform": "win32-x64-user", + "url": "${platforms["win32-x64-user"] || "https://downloads.cursor.com/.../CursorUserSetup-x64.exe"}", + "sizeBytes": ${latestDetails["win32-x64-user"]?.sizeBytes ?? 0}, + "sha256": "${latestDetails["win32-x64-user"]?.sha256 || ""}" +} +\`\`\` + +For the full machine-readable history, you can also use \`${liveSiteUrl}/version-history.json\`. + ![GitHub stars](https://img.shields.io/github/stars/accesstechnology-mike/cursor-downloads?style=social) ![Last commit](https://img.shields.io/github/last-commit/accesstechnology-mike/cursor-downloads) ![Update workflow](https://img.shields.io/github/actions/workflow/status/accesstechnology-mike/cursor-downloads/update.yml?branch=main) diff --git a/vercel.json b/vercel.json index 6109739..9350e2d 100644 --- a/vercel.json +++ b/vercel.json @@ -16,6 +16,14 @@ "Cache-Control": "no-store, must-revalidate" } }, + { + "src": "/api/latest-download", + "dest": "/api/latest-download.js", + "headers": { + "Content-Type": "application/json", + "Cache-Control": "no-store, must-revalidate" + } + }, { "src": "/(.*\\.(js|css|json|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot))", "dest": "/$1"