( packageName: string, version: string, filePath: string, )
| 105 | * Fetch file content from jsDelivr CDN with size limit. |
| 106 | */ |
| 107 | export async function fetchSkillFile( |
| 108 | packageName: string, |
| 109 | version: string, |
| 110 | filePath: string, |
| 111 | ): Promise<string> { |
| 112 | const url = `https://cdn.jsdelivr.net/npm/${packageName}@${version}/${filePath}` |
| 113 | const response = await fetch(url) |
| 114 | |
| 115 | if (!response.ok) { |
| 116 | if (response.status === 404) { |
| 117 | throw createError({ statusCode: 404, message: 'File not found' }) |
| 118 | } |
| 119 | throw createError({ statusCode: 502, message: 'Failed to fetch file from jsDelivr' }) |
| 120 | } |
| 121 | |
| 122 | const contentLength = response.headers.get('content-length') |
| 123 | if (contentLength && parseInt(contentLength, 10) > MAX_SKILL_FILE_SIZE) { |
| 124 | throw createError({ |
| 125 | statusCode: 413, |
| 126 | message: `File too large (${(parseInt(contentLength, 10) / 1024 / 1024).toFixed(1)}MB). Maximum size is ${MAX_SKILL_FILE_SIZE / 1024}KB.`, |
| 127 | }) |
| 128 | } |
| 129 | |
| 130 | const content = await response.text() |
| 131 | |
| 132 | if (content.length > MAX_SKILL_FILE_SIZE) { |
| 133 | throw createError({ |
| 134 | statusCode: 413, |
| 135 | message: `File too large (${(content.length / 1024 / 1024).toFixed(1)}MB). Maximum size is ${MAX_SKILL_FILE_SIZE / 1024}KB.`, |
| 136 | }) |
| 137 | } |
| 138 | |
| 139 | return content |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Fetch and parse SKILL.md content for a skill. |
no test coverage detected