feat: Enhance Marketplace and Skill Management UI with improved error handling and user feedback

- 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.
This commit is contained in:
duanshuwen
2026-04-19 20:33:44 +08:00
parent 2cedc1c234
commit 38bea97197
230 changed files with 77824 additions and 163 deletions

View File

@@ -0,0 +1,152 @@
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;
}