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

@@ -5,12 +5,18 @@ import { fail, ok, parseJsonBody } from '../route-utils';
import { getAllSkillConfigs, updateSkillConfig } from '../../utils/skill-config';
import { shell } from 'electron';
import { getOpenClawConfigDir } from '../../utils/paths';
import {
SkillInstallService,
SkillInstallServiceError,
type SkillInstallRequest,
} from '@service/skill-install-service';
export async function handleSkillRoutes(
request: NormalizedHostApiRequest,
ctx: HostApiContext,
): Promise<HostApiResult<unknown> | null> {
const { pathname, method } = request;
const installService = new SkillInstallService({ clawHubService: ctx.clawHubService });
if (pathname === '/api/skills/configs' && method === 'GET') {
try {
@@ -65,13 +71,26 @@ export async function handleSkillRoutes(
}
}
if (pathname === '/api/skills/install' && method === 'POST') {
try {
const body = parseJsonBody<SkillInstallRequest>(request.body);
return ok(await installService.install(body));
} catch (error) {
return failInstall(error);
}
}
if (pathname === '/api/clawhub/install' && method === 'POST') {
try {
const body = parseJsonBody<Record<string, unknown>>(request.body);
await ctx.clawHubService.install(body as { slug: string; version?: string; force?: boolean });
return ok({ success: true });
const body = parseJsonBody<{ slug: string; version?: string; force?: boolean }>(request.body);
return ok(await installService.install({
kind: 'marketplace',
slug: body.slug,
version: body.version,
force: body.force,
}));
} catch (error) {
return fail(500, error instanceof Error ? error.message : String(error));
return failInstall(error);
}
}
@@ -150,3 +169,11 @@ export async function handleSkillRoutes(
return null;
}
function failInstall(error: unknown): HostApiResult<unknown> {
if (error instanceof SkillInstallServiceError) {
return fail(error.status, error.message);
}
return fail(500, error instanceof Error ? error.message : String(error));
}