/** * Skill marketplace client. * Interacts with the infra's skill registry API to list, search, install, and publish skills. */ import type { AppConfig } from "../auth/config" export interface MarketplaceSkill { name: string description: string trustLevel: "local" | "community" | "verified" | "signed" allowedTools: string[] author?: string version?: string installs?: number rating?: number tags?: string[] } export interface SearchResult { skills: MarketplaceSkill[] total: number page: number } function getApiBase(config: AppConfig): string { return String(config.apiBase ?? "https://api.llmtune.io/api/agent/v1").replace(/\/$/, "") } function getHeaders(config: AppConfig): Record { return { "Content-Type": "application/json", Authorization: `Bearer ${config.apiKey}`, } } /** * List all skills in the marketplace. */ export async function listSkills( config: AppConfig, options?: { search?: string; tag?: string; page?: number; limit?: number }, ): Promise { const base = getApiBase(config) const params = new URLSearchParams() if (options?.search) params.set("search", options.search) if (options?.tag) params.set("tag", options.tag) if (options?.page) params.set("page", String(options.page)) if (options?.limit) params.set("limit", String(options.limit)) const url = `${base}/skills?${params.toString()}` const response = await fetch(url, { headers: getHeaders(config) }) if (!response.ok) { throw new Error(`Failed to list skills: HTTP ${response.status}`) } return (await response.json()) as SearchResult } /** * Get details for a specific skill. */ export async function getSkillDetails(config: AppConfig, name: string): Promise { const base = getApiBase(config) const url = `${base}/skills/${encodeURIComponent(name)}` const response = await fetch(url, { headers: getHeaders(config) }) if (!response.ok) { if (response.status === 404) throw new Error(`Skill not found: ${name}`) throw new Error(`Failed to get skill: HTTP ${response.status}`) } return (await response.json()) as MarketplaceSkill } /** * Install a skill from the marketplace to the local skills directory. */ export async function installSkill(config: AppConfig, name: string): Promise { const base = getApiBase(config) const url = `${base}/skills/install` const response = await fetch(url, { method: "POST", headers: getHeaders(config), body: JSON.stringify({ skillName: name }), }) if (!response.ok) { const body = await response.text() throw new Error(`Failed to install skill: ${body}`) } const data = (await response.json()) as { installPath: string; skillName: string } return data.installPath } /** * Publish a local skill to the marketplace. */ export async function publishSkill( config: AppConfig, skill: { name: string; description: string; content: string; trustLevel: string; allowedTools: string[] }, ): Promise<{ published: boolean; skillName: string }> { const base = getApiBase(config) const url = `${base}/skills` const response = await fetch(url, { method: "POST", headers: getHeaders(config), body: JSON.stringify(skill), }) if (!response.ok) { const body = await response.text() throw new Error(`Failed to publish skill: ${body}`) } return (await response.json()) as { published: boolean; skillName: string } }