- Updated MarketplaceDrawer to include security notes and manual installation hints. - Refactored SkillDetailDrawer to display default icons for skills. - Simplified SkillListItem to use default icons for better readability. - Integrated gateway status checks and warnings in SkillsPage for improved user awareness. - Enhanced error handling for skill installation and fetching, providing clearer feedback to users. - Added new translations for error messages and gateway warnings to improve localization support.
153 lines
4.8 KiB
TypeScript
153 lines
4.8 KiB
TypeScript
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<HostApiResult<unknown> | 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<string, string>;
|
|
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<Record<string, unknown>>(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<Record<string, unknown>>(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<Record<string, unknown>>(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;
|
|
}
|