feat(02-03): add 7 skill IPC channels with Zod schemas and register handlers
- Add importSkill, listSkills, getSkillDetail, deleteSkill, assignSkillToAgent, removeSkillFromAgent, checkSkillUpdate IPC channels - Add Zod validation schemas for all skill payloads in contracts.ts - Create ipc-skill-handlers.ts with company existence checks and domain-changed events on mutations - Add SkillRecord, SkillImportResult, SkillUpdateStatus types to shared/types.ts - Wire registerSkillHandlers into ipc-domain-handlers.ts - Add skillService to RegisterDomainHandlersDependencies interface
This commit is contained in:
1 parent
c9111d2f6d
commit
b5ad164338
5 files changed
+225
-1
No files matched your search
@@ -3,6 +3,7 @@ import { z } from "zod";
|
||||
import type { DesktopResult } from "@shared/contracts";
|
||||
import type { bootstrapOnboardingSchema } from "@shared/contracts";
|
||||
import type { AppDatabase } from "./database";
|
||||
import type { createSkillService } from "./skill-service";
|
||||
|
||||
export interface BrowserActionControl {
|
||||
clearSocialAccountData: (socialAccountId: string, screenshotPaths?: string[]) => Promise<void>;
|
||||
@@ -38,6 +39,7 @@ export interface RegisterDomainHandlersDependencies {
|
||||
bootstrapOnboarding: (input: z.infer<typeof bootstrapOnboardingSchema>) => unknown;
|
||||
getBrowserActions: () => BrowserActionControl | null;
|
||||
randomFallbackCompanyId: () => string;
|
||||
skillService: ReturnType<typeof createSkillService> | null;
|
||||
}
|
||||
|
||||
export function ok<T>(data: T): DesktopResult<T> {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { registerBrowserHandlers } from "./ipc-browser-handlers";
|
||||
import { registerContentOperationsHandlers } from "./ipc-content-operations-handlers";
|
||||
import { registerOnboardingHandlers } from "./ipc-onboarding-handlers";
|
||||
import { registerRecoveryHandlers } from "./ipc-recovery-handlers";
|
||||
import { registerSkillHandlers } from "./ipc-skill-handlers";
|
||||
|
||||
export function registerDomainHandlers(dependencies: RegisterDomainHandlersDependencies) {
|
||||
registerOnboardingHandlers(dependencies);
|
||||
@@ -13,4 +14,10 @@ export function registerDomainHandlers(dependencies: RegisterDomainHandlersDepen
|
||||
registerAutomationOperationsHandlers(dependencies);
|
||||
registerAgentCoordinationHandlers(dependencies);
|
||||
registerRecoveryHandlers(dependencies);
|
||||
registerSkillHandlers({
|
||||
registerHandle: dependencies.registerHandle,
|
||||
ensureCompanyExists: dependencies.ensureCompanyExists,
|
||||
publishDomainChanged: dependencies.publishDomainChanged,
|
||||
skillService: dependencies.skillService,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import {
|
||||
IPC_CHANNELS,
|
||||
importSkillSchema,
|
||||
listSkillsSchema,
|
||||
getSkillDetailSchema,
|
||||
deleteSkillSchema,
|
||||
assignSkillToAgentSchema,
|
||||
removeSkillFromAgentSchema,
|
||||
checkSkillUpdateSchema,
|
||||
} from "@shared/contracts";
|
||||
import { fail, ok, type RegisterDomainHandlersDependencies } from "./ipc-domain-common";
|
||||
import type { createSkillService } from "./skill-service";
|
||||
|
||||
interface SkillHandlerDependencies extends Pick<
|
||||
RegisterDomainHandlersDependencies,
|
||||
"registerHandle" | "ensureCompanyExists" | "publishDomainChanged"
|
||||
> {
|
||||
skillService: ReturnType<typeof createSkillService> | null;
|
||||
}
|
||||
|
||||
export function registerSkillHandlers({
|
||||
registerHandle,
|
||||
ensureCompanyExists,
|
||||
publishDomainChanged,
|
||||
skillService,
|
||||
}: SkillHandlerDependencies) {
|
||||
function requireSkillService() {
|
||||
if (!skillService) {
|
||||
return fail("SERVICE_NOT_READY", "Skill service is not initialized.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---- Import skill from source (GitHub URL, local path, raw URL, skills.sh) ----
|
||||
registerHandle(IPC_CHANNELS.importSkill, async (_event, payload) => {
|
||||
const parsed = importSkillSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const serviceError = requireSkillService();
|
||||
if (serviceError) return serviceError;
|
||||
const result = await skillService!.importFromSource(parsed.companyId, parsed.source);
|
||||
if (result.ok) {
|
||||
publishDomainChanged();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
// ---- List all skills for a company ----
|
||||
registerHandle(IPC_CHANNELS.listSkills, async (_event, payload) => {
|
||||
const parsed = listSkillsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const serviceError = requireSkillService();
|
||||
if (serviceError) return serviceError;
|
||||
return ok(skillService!.listForCompany(parsed.companyId));
|
||||
});
|
||||
|
||||
// ---- Get skill detail ----
|
||||
registerHandle(IPC_CHANNELS.getSkillDetail, async (_event, payload) => {
|
||||
const parsed = getSkillDetailSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const serviceError = requireSkillService();
|
||||
if (serviceError) return serviceError;
|
||||
return skillService!.getDetail(parsed.companyId, parsed.skillId);
|
||||
});
|
||||
|
||||
// ---- Delete a skill ----
|
||||
registerHandle(IPC_CHANNELS.deleteSkill, async (_event, payload) => {
|
||||
const parsed = deleteSkillSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const serviceError = requireSkillService();
|
||||
if (serviceError) return serviceError;
|
||||
const result = await skillService!.deleteSkill(parsed.companyId, parsed.skillId);
|
||||
if (result.ok) {
|
||||
publishDomainChanged();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
// ---- Assign skill to agent ----
|
||||
registerHandle(IPC_CHANNELS.assignSkillToAgent, async (_event, payload) => {
|
||||
const parsed = assignSkillToAgentSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const serviceError = requireSkillService();
|
||||
if (serviceError) return serviceError;
|
||||
const result = skillService!.assignToAgent(parsed.companyId, parsed.agentId, parsed.skillId);
|
||||
if (result.ok) {
|
||||
publishDomainChanged();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
// ---- Remove skill from agent ----
|
||||
registerHandle(IPC_CHANNELS.removeSkillFromAgent, async (_event, payload) => {
|
||||
const parsed = removeSkillFromAgentSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const serviceError = requireSkillService();
|
||||
if (serviceError) return serviceError;
|
||||
const result = skillService!.removeFromAgent(parsed.companyId, parsed.agentId, parsed.skillId);
|
||||
if (result.ok) {
|
||||
publishDomainChanged();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
// ---- Check if skill has update available ----
|
||||
registerHandle(IPC_CHANNELS.checkSkillUpdate, async (_event, payload) => {
|
||||
const parsed = checkSkillUpdateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const serviceError = requireSkillService();
|
||||
if (serviceError) return serviceError;
|
||||
return skillService!.checkUpdateStatus(parsed.companyId, parsed.skillId);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user