export interface GitHubTreeItem { name: string; path: string; contentType: "directory" | "file"; } export interface GitHubTree { path: string; branch: string; items: GitHubTreeItem[]; repo: { name: string; owner: string; isPublic: boolean; }; } export async function getGitHubTree( owner: string, repo: string, branch: string, path: string, ): Promise { const url = `https://github.com/${owner}/${repo}/tree/${branch}/${path}`; // GitHub throttles/blocks requests without realistic browser headers. // These headers make the request appear as a standard browser visit. const response = await fetch(url, { headers: { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", }, }); const html = await response.text(); // GitHub uses different formats for root vs subdirectories. let payload; // Try root format first. const rootFormatMatches = [ ...html.matchAll( /