feat: add GitHub skill installation support

- Implemented functionality to install skills from GitHub URLs.
- Updated API to handle new installation requests from GitHub.
- Enhanced UI to allow users to input GitHub skill URLs for installation.
- Added translations for new GitHub installation features in English, Thai, and Chinese.
- Created tests for the new skill installation service and API routes to ensure proper functionality.
This commit is contained in:
DEV_DSW
2026-04-23 11:41:52 +08:00
parent f80bdc7f11
commit 655e7c51d2
16 changed files with 1818 additions and 51 deletions

View File

@@ -48,6 +48,18 @@ type GatewaySkillsStatusResult = {
skills?: GatewaySkillStatus[];
};
export type SkillInstallRequest =
| { kind: 'marketplace'; slug: string; version?: string; force?: boolean }
| { kind: 'github-url'; url: string; force?: boolean };
export type SkillInstallResult = {
success: true;
slug: string;
baseDir: string;
source: 'marketplace' | 'github-url';
enabled: true;
};
function mapErrorCodeToSkillErrorKey(
message: string,
operation: 'fetch' | 'search' | 'install',
@@ -245,16 +257,18 @@ export async function apiSearchSkills(query: string): Promise<MarketplaceSkill[]
}
}
export async function apiInstallSkill(slug: string, version?: string): Promise<void> {
export async function apiInstallSkill(request: SkillInstallRequest): Promise<SkillInstallResult> {
try {
const result = await hostApiFetch<{ success: boolean; error?: string }>('/api/clawhub/install', {
const result = await hostApiFetch<SkillInstallResult>('/api/skills/install', {
method: 'POST',
body: JSON.stringify({ slug, version }),
body: JSON.stringify(request),
});
if (!result?.success) {
throw new Error(result?.error || 'Install failed');
throw new Error('Install failed');
}
return result;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(mapErrorCodeToSkillErrorKey(message, 'install'));
@@ -320,11 +334,11 @@ export async function apiOpenSkillReadme(skillKey: string, slug?: string, baseDi
export async function apiGetSkillsDir(): Promise<string> {
try {
const result = await hostApiFetch<{ success: boolean; dir?: string; path?: string; error?: string }>('/api/clawhub/skills-dir');
if (result?.success && (result.dir || result.path)) return result.dir || result.path || '~/.zn-ai/skills';
if (result?.success && (result.dir || result.path)) return result.dir || result.path || '~/.openclaw/skills';
} catch {
// Fallback to the default local path.
}
return '~/.zn-ai/skills';
return '~/.openclaw/skills';
}
export async function apiGetGatewayStatus(): Promise<{