- Implement tests for random ID generation, ensuring preference for crypto.randomUUID. - Create tests for runtime context capabilities, validating the injection of enabled skill capabilities. - Add tests for skill capability parsing, including classification and command example extraction. - Introduce tests for the skill planner, verifying tool call planning based on user requests and attachment requirements. - Establish tests for UV setup, ensuring proper handling of Python installation scenarios and environment checks.
128 lines
4.1 KiB
TypeScript
128 lines
4.1 KiB
TypeScript
import { BrowserWindow } from 'electron';
|
|
import { ClawHubService } from '@electron/gateway/clawhub';
|
|
import {
|
|
hydrateSkillCapabilityRegistry,
|
|
refreshSkillCapabilityRegistry,
|
|
} from '@electron/gateway/skill-capability-registry';
|
|
import { windowManager } from '@electron/service/window-service';
|
|
import {
|
|
SkillInstallService,
|
|
SkillInstallServiceError,
|
|
type SkillInstallRequest,
|
|
} from '@electron/service/skill-install-service';
|
|
import { getAllSkillConfigs, updateSkillConfig } from '@electron/utils/skill-config';
|
|
import type { GatewayEvent, GatewayRpcParams, GatewayRpcReturns } from '../types';
|
|
|
|
type GatewayBroadcast = (event: GatewayEvent) => void;
|
|
|
|
function broadcastGatewayEvent(event: GatewayEvent): void {
|
|
const mainWindow = BrowserWindow.getAllWindows().find(
|
|
(win) => windowManager.getName(win) === 'main',
|
|
) ?? BrowserWindow.getAllWindows().find((win) => !win.isDestroyed());
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
mainWindow.webContents.send('gateway:event', event);
|
|
}
|
|
}
|
|
|
|
function broadcastSkillsRuntimeChanged(broadcast?: GatewayBroadcast, reason = 'skills:changed'): void {
|
|
const event: GatewayEvent = {
|
|
type: 'runtime:changed',
|
|
topics: ['skills'],
|
|
reason,
|
|
syncedAt: new Date().toISOString(),
|
|
};
|
|
|
|
if (broadcast) {
|
|
broadcast(event);
|
|
return;
|
|
}
|
|
|
|
broadcastGatewayEvent(event);
|
|
}
|
|
|
|
export async function handleSkillsStatus(): Promise<GatewayRpcReturns['skills.status']> {
|
|
const configs = await getAllSkillConfigs();
|
|
hydrateSkillCapabilityRegistry(configs);
|
|
|
|
return {
|
|
skills: Object.entries(configs).map(([skillKey, config]) => ({
|
|
skillKey,
|
|
slug: config.slug || skillKey,
|
|
name: config.name || skillKey,
|
|
description: config.description || '',
|
|
disabled: config.enabled === false,
|
|
emoji: config.icon,
|
|
version: config.version || '1.0.0',
|
|
author: config.author,
|
|
config: {
|
|
...(config.config || {}),
|
|
...(config.apiKey ? { apiKey: config.apiKey } : {}),
|
|
...(config.env ? { env: config.env } : {}),
|
|
},
|
|
bundled: typeof config.isBundled === 'boolean' ? config.isBundled : config.source === 'openclaw-bundled',
|
|
always: Boolean(config.isCore),
|
|
source: config.source,
|
|
baseDir: config.baseDir,
|
|
filePath: config.filePath,
|
|
})),
|
|
};
|
|
}
|
|
|
|
export async function handleSkillsUpdate(
|
|
params: { skillKey: string; enabled?: boolean },
|
|
broadcast?: GatewayBroadcast,
|
|
): Promise<GatewayRpcReturns['skills.update']> {
|
|
const { skillKey, enabled } = params;
|
|
if (!skillKey || !String(skillKey).trim()) {
|
|
throw new Error('skillKey is required');
|
|
}
|
|
|
|
const result = await updateSkillConfig(String(skillKey).trim(), { enabled });
|
|
if (!result.success) {
|
|
throw new Error(result.error || 'Failed to update skill');
|
|
}
|
|
|
|
try {
|
|
const configs = await getAllSkillConfigs();
|
|
hydrateSkillCapabilityRegistry(configs);
|
|
} catch (error) {
|
|
console.warn('Failed to refresh skill capability registry after skills.update:', error);
|
|
}
|
|
|
|
const normalizedSkillKey = String(skillKey).trim();
|
|
const action = enabled === false ? 'disabled' : enabled === true ? 'enabled' : 'updated';
|
|
broadcastSkillsRuntimeChanged(broadcast, `skills:update:${normalizedSkillKey}:${action}`);
|
|
|
|
return { success: true };
|
|
}
|
|
|
|
export async function handleSkillsInstall(
|
|
params: GatewayRpcParams['skills.install'],
|
|
broadcast?: GatewayBroadcast,
|
|
): Promise<GatewayRpcReturns['skills.install']> {
|
|
const request = params as SkillInstallRequest;
|
|
const installService = new SkillInstallService({
|
|
clawHubService: new ClawHubService(),
|
|
});
|
|
|
|
try {
|
|
const result = await installService.install(request);
|
|
try {
|
|
await refreshSkillCapabilityRegistry();
|
|
} catch (error) {
|
|
console.warn('Failed to refresh skill capability registry after skills.install:', error);
|
|
}
|
|
broadcastSkillsRuntimeChanged(
|
|
broadcast,
|
|
`skills:install:${result.source}:${result.slug}`,
|
|
);
|
|
return result;
|
|
} catch (error) {
|
|
if (error instanceof SkillInstallServiceError) {
|
|
throw new Error(error.message);
|
|
}
|
|
|
|
throw error instanceof Error ? error : new Error(String(error));
|
|
}
|
|
}
|