( fileId: string, config: FilesApiConfig, )
| 130 | * @returns The file content as a Buffer |
| 131 | */ |
| 132 | export async function downloadFile( |
| 133 | fileId: string, |
| 134 | config: FilesApiConfig, |
| 135 | ): Promise<Buffer> { |
| 136 | const baseUrl = config.baseUrl || getDefaultApiBaseUrl() |
| 137 | const url = `${baseUrl}/v1/files/${fileId}/content` |
| 138 | |
| 139 | const headers = { |
| 140 | Authorization: `Bearer ${config.oauthToken}`, |
| 141 | 'anthropic-version': ANTHROPIC_VERSION, |
| 142 | 'anthropic-beta': FILES_API_BETA_HEADER, |
| 143 | } |
| 144 | |
| 145 | logDebug(`Downloading file ${fileId} from ${url}`) |
| 146 | |
| 147 | return retryWithBackoff(`Download file ${fileId}`, async () => { |
| 148 | try { |
| 149 | const response = await axios.get(url, { |
| 150 | headers, |
| 151 | responseType: 'arraybuffer', |
| 152 | timeout: 60000, // 60 second timeout for large files |
| 153 | validateStatus: status => status < 500, |
| 154 | }) |
| 155 | |
| 156 | if (response.status === 200) { |
| 157 | logDebug(`Downloaded file ${fileId} (${response.data.length} bytes)`) |
| 158 | return { done: true, value: Buffer.from(response.data) } |
| 159 | } |
| 160 | |
| 161 | // Non-retriable errors - throw immediately |
| 162 | if (response.status === 404) { |
| 163 | throw new Error(`File not found: ${fileId}`) |
| 164 | } |
| 165 | if (response.status === 401) { |
| 166 | throw new Error('Authentication failed: invalid or missing API key') |
| 167 | } |
| 168 | if (response.status === 403) { |
| 169 | throw new Error(`Access denied to file: ${fileId}`) |
| 170 | } |
| 171 | |
| 172 | return { done: false, error: `status ${response.status}` } |
| 173 | } catch (error) { |
| 174 | if (!axios.isAxiosError(error)) { |
| 175 | throw error |
| 176 | } |
| 177 | return { done: false, error: error.message } |
| 178 | } |
| 179 | }) |
| 180 | } |
| 181 | |
| 182 | /** |
| 183 | * Normalizes a relative path, strips redundant prefixes, and builds the full |
no test coverage detected