import type { HostApiResult } from '@src/types/runtime'; import type { HostApiContext } from '../context'; import type { NormalizedHostApiRequest } from '../route-utils'; import { fail, ok, parseJsonBody } from '../route-utils'; import { getAllSkillConfigs, updateSkillConfig } from '../../utils/skill-config'; import { shell } from 'electron'; import { getOpenClawConfigDir } from '../../utils/paths'; export async function handleSkillRoutes( request: NormalizedHostApiRequest, ctx: HostApiContext, ): Promise | null> { const { pathname, method } = request; if (pathname === '/api/skills/configs' && method === 'GET') { try { return ok(await getAllSkillConfigs()); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/skills/config' && method === 'PUT') { try { const body = parseJsonBody<{ skillKey: string; apiKey?: string; env?: Record; enabled?: boolean; }>(request.body); if (!body?.skillKey || !String(body.skillKey).trim()) { return fail(400, 'skillKey is required'); } const result = await updateSkillConfig(body.skillKey, { apiKey: body.apiKey, env: body.env, enabled: body.enabled, }); return ok(result); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/clawhub/capability' && method === 'GET') { try { return ok({ success: true, capability: await ctx.clawHubService.getMarketplaceCapability(), }); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/clawhub/search' && method === 'POST') { try { const body = parseJsonBody>(request.body); return ok({ success: true, results: await ctx.clawHubService.search(body as { query: string; limit?: number }), }); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/clawhub/install' && method === 'POST') { try { const body = parseJsonBody>(request.body); await ctx.clawHubService.install(body as { slug: string; version?: string; force?: boolean }); return ok({ success: true }); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/clawhub/uninstall' && method === 'POST') { try { const body = parseJsonBody>(request.body); await ctx.clawHubService.uninstall(body as { slug: string }); return ok({ success: true }); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/clawhub/list' && method === 'GET') { try { return ok({ success: true, results: await ctx.clawHubService.listInstalled(), }); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/clawhub/open-readme' && method === 'POST') { try { const body = parseJsonBody<{ slug?: string; skillKey?: string; baseDir?: string }>(request.body); await ctx.clawHubService.openSkillReadme( body.skillKey || body.slug || '', body.slug, body.baseDir, ); return ok({ success: true }); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/clawhub/open-path' && method === 'POST') { try { const body = parseJsonBody<{ slug?: string; skillKey?: string; baseDir?: string }>(request.body); await ctx.clawHubService.openSkillPath( body.skillKey || body.slug || '', body.slug, body.baseDir, ); return ok({ success: true }); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/clawhub/skills-dir' && method === 'GET') { try { return ok({ success: true, path: `${getOpenClawConfigDir()}/skills`, }); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } if (pathname === '/api/clawhub/open-skills-dir' && method === 'POST') { try { const skillsDir = `${getOpenClawConfigDir()}/skills`; const openResult = await shell.openPath(skillsDir); if (openResult) { return fail(500, openResult); } return ok({ success: true }); } catch (error) { return fail(500, error instanceof Error ? error.message : String(error)); } } return null; }