wiwiway / src /lib /versionManager /releaseChecker.ts
Claude Code
Sync gateway codebase without binaries
116b4cb
Raw
History Blame Contribute Delete
2.95 kB
const CACHE_TTL_MS = 5 * 60 * 1000;
const releasesCache = new Map<string, { data: any; ts: number }>();
function getGitHubHeaders(): Record<string, string> {
const headers: Record<string, string> = { Accept: "application/vnd.github+json" };
const token = process.env.GITHUB_TOKEN;
if (token) headers.Authorization = `Bearer ${token}`;
return headers;
}
async function fetchJSON(url: string): Promise<any> {
const res = await fetch(url, {
headers: getGitHubHeaders(),
signal: AbortSignal.timeout(15_000),
});
if (!res.ok) throw new Error(`GitHub API ${res.status}: ${url}`);
return res.json();
}
async function cachedFetch(url: string): Promise<any> {
const cached = releasesCache.get(url);
if (cached && Date.now() - cached.ts < CACHE_TTL_MS) return cached.data;
const data = await fetchJSON(url);
releasesCache.set(url, { data, ts: Date.now() });
return data;
}
interface ReleaseInfo {
tag: string;
version: string;
assets: { name: string; url: string; size: number }[];
publishedAt: string;
body: string;
}
function parseRelease(raw: any): ReleaseInfo {
return {
tag: raw.tag_name,
version: raw.tag_name.replace(/^v/, ""),
assets: (raw.assets || []).map((a: any) => ({
name: a.name,
url: a.browser_download_url,
size: a.size,
})),
publishedAt: raw.published_at,
body: raw.body || "",
};
}
export async function getLatestRelease(): Promise<ReleaseInfo> {
const raw = await cachedFetch(
"https://api.github.com/repos/router-for-me/CLIProxyAPI/releases/latest"
);
return parseRelease(raw);
}
export async function getReleaseByVersion(version: string): Promise<ReleaseInfo | null> {
const tag = version.startsWith("v") ? version : `v${version}`;
try {
const raw = await cachedFetch(
`https://api.github.com/repos/router-for-me/CLIProxyAPI/releases/tags/${tag}`
);
return parseRelease(raw);
} catch {
return null;
}
}
export async function getAvailableVersions(): Promise<string[]> {
const raw = await cachedFetch(
"https://api.github.com/repos/router-for-me/CLIProxyAPI/releases?per_page=30"
);
return (Array.isArray(raw) ? raw : []).map((r: any) => r.tag_name);
}
export async function getChecksums(version: string): Promise<Map<string, string>> {
const tag = version.startsWith("v") ? version : `v${version}`;
const url = `https://github.com/router-for-me/CLIProxyAPI/releases/download/${tag}/checksums.txt`;
try {
const res = await fetch(url, { signal: AbortSignal.timeout(10_000) });
if (!res.ok) return new Map();
const text = await res.text();
const map = new Map<string, string>();
for (const line of text.split("\n")) {
const parts = line.trim().split(/\s+/);
if (parts.length >= 2) {
map.set(parts[1], parts[0]);
}
}
return map;
} catch {
return new Map();
}
}
export function clearCache(): void {
releasesCache.clear();
}