mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 10:01:29 -07:00
fix(extensions): resolve GitHub API 415 error for source tarballs (#13319)
This commit is contained in:
@@ -355,7 +355,17 @@ export async function downloadFromGitHubRelease(
|
||||
}
|
||||
|
||||
try {
|
||||
await downloadFile(archiveUrl, downloadedAssetPath);
|
||||
// GitHub API requires different Accept headers for different types of downloads:
|
||||
// 1. Binary Assets (e.g. release artifacts): Require 'application/octet-stream' to return the raw content.
|
||||
// 2. Source Tarballs (e.g. /tarball/{ref}): Require 'application/vnd.github+json' (or similar) to return
|
||||
// a 302 Redirect to the actual download location (codeload.github.com).
|
||||
// Sending 'application/octet-stream' for tarballs results in a 415 Unsupported Media Type error.
|
||||
const headers = {
|
||||
...(asset
|
||||
? { Accept: 'application/octet-stream' }
|
||||
: { Accept: 'application/vnd.github+json' }),
|
||||
};
|
||||
await downloadFile(archiveUrl, downloadedAssetPath, { headers });
|
||||
} catch (error) {
|
||||
return {
|
||||
failureReason: 'failed to download asset',
|
||||
@@ -472,24 +482,42 @@ export function findReleaseAsset(assets: Asset[]): Asset | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function downloadFile(url: string, dest: string): Promise<void> {
|
||||
const headers: {
|
||||
'User-agent': string;
|
||||
Accept: string;
|
||||
Authorization?: string;
|
||||
} = {
|
||||
export interface DownloadOptions {
|
||||
headers?: Record<string, string>;
|
||||
}
|
||||
|
||||
export async function downloadFile(
|
||||
url: string,
|
||||
dest: string,
|
||||
options?: DownloadOptions,
|
||||
redirectCount: number = 0,
|
||||
): Promise<void> {
|
||||
const headers: Record<string, string> = {
|
||||
'User-agent': 'gemini-cli',
|
||||
Accept: 'application/octet-stream',
|
||||
...options?.headers,
|
||||
};
|
||||
const token = getGitHubToken();
|
||||
if (token) {
|
||||
headers.Authorization = `token ${token}`;
|
||||
headers['Authorization'] = `token ${token}`;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
https
|
||||
.get(url, { headers }, (res) => {
|
||||
if (res.statusCode === 302 || res.statusCode === 301) {
|
||||
downloadFile(res.headers.location!, dest).then(resolve).catch(reject);
|
||||
if (redirectCount >= 10) {
|
||||
return reject(new Error('Too many redirects'));
|
||||
}
|
||||
|
||||
if (!res.headers.location) {
|
||||
return reject(
|
||||
new Error('Redirect response missing Location header'),
|
||||
);
|
||||
}
|
||||
downloadFile(res.headers.location, dest, options, redirectCount + 1)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
return;
|
||||
}
|
||||
if (res.statusCode !== 200) {
|
||||
|
||||
Reference in New Issue
Block a user