feat: wire qoder into the download API allowlist

Add qoder to FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS so the download endpoint
accepts /api/download/skill/qoder/* and resolves to dist/qoder/.qoder/.
Without this, the website install surface returned 400 Invalid provider
even though qoder was a first-class harness everywhere else.

Cover the new provider with two assertions in download-validation.test.js
(allowlist + path resolution).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vinaywho
2026-04-28 16:00:30 +05:30
co-authored by Claude Opus 4.7
parent 7cfa7759f5
commit c812d76b6f
2 changed files with 12 additions and 0 deletions
+1
View File
@@ -8,6 +8,7 @@ export const FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS = Object.freeze({
kiro: '.kiro',
opencode: '.opencode',
pi: '.pi',
qoder: '.qoder',
});
export const FILE_DOWNLOAD_PROVIDERS = Object.freeze(
+11
View File
@@ -19,6 +19,11 @@ describe('download provider validation', () => {
expect(isAllowedFileProvider('github')).toBe(true);
});
test('allows qoder as an individual download provider', () => {
expect(ALLOWED_FILE_PROVIDERS).toContain('qoder');
expect(isAllowedFileProvider('qoder')).toBe(true);
});
test('separates file downloads from bundle downloads', () => {
expect(ALLOWED_BUNDLE_PROVIDERS).toContain('universal');
expect(isAllowedBundleProvider('universal')).toBe(true);
@@ -46,6 +51,12 @@ describe('download file paths', () => {
);
});
test('maps qoder skills into the .qoder config directory', () => {
expect(getFilePath('skill', 'qoder', 'impeccable')).toBe(
path.join(process.cwd(), 'dist', 'qoder', '.qoder', 'skills', 'impeccable', 'SKILL.md')
);
});
test('rejects bundle-only providers on the individual download route', async () => {
const response = await handleFileDownload('skill', 'universal', 'impeccable');
expect(response.status).toBe(400);