UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

253 lines 9.31 kB
/** * ClawHub Registry Adapter * * Connects to the ClawHub REST API at clawhub.ai for * community skill discovery and publishing. * * Supports both the ClawHub web API and the `clawhub` CLI * if installed. Falls back gracefully when neither is available. * * @implements #539 * @see https://clawhub.ai * @see https://docs.openclaw.ai/tools/clawhub */ import { execSync } from 'child_process'; import fs from 'fs'; import path from 'path'; const CLAWHUB_API_BASE = 'https://clawhub.ai/api/v1'; /** * Attempt a ClawHub API request */ async function clawHubFetch(endpoint) { try { const response = await fetch(`${CLAWHUB_API_BASE}${endpoint}`, { headers: { 'Accept': 'application/json' }, signal: AbortSignal.timeout(10000), }); if (!response.ok) return null; return await response.json(); } catch { return null; } } /** * Try clawhub CLI command */ function runClawHub(args) { try { return execSync(`clawhub ${args}`, { encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'], }).trim(); } catch { return null; } } export class ClawHubAdapter { id = 'clawhub'; name = 'ClawHub Registry'; available = null; async isAvailable() { if (this.available !== null) return this.available; // Check CLI const cliAvailable = runClawHub('--version 2>/dev/null') !== null; if (cliAvailable) { this.available = true; return true; } // Check API const apiResult = await clawHubFetch('/health'); this.available = apiResult !== null; return this.available; } async search(query) { // Try API const apiResult = await clawHubFetch(`/skills/search?q=${encodeURIComponent(query)}`); if (apiResult && Array.isArray(apiResult.results)) { return apiResult.results.map((s) => ({ name: s.name, description: s.description || '', source: 'clawhub', package: s.package || s.author, platforms: s.platforms || [], installed: false, })); } // Try CLI const cliOutput = runClawHub(`search "${query}" --json 2>/dev/null`); if (cliOutput) { try { const parsed = JSON.parse(cliOutput); if (Array.isArray(parsed)) { return parsed.map((s) => ({ name: s.name, description: s.description || '', source: 'clawhub', package: s.package || s.author, platforms: s.platforms || [], installed: false, })); } } catch { // Fall through } } return []; } async info(name) { // Try API const apiResult = await clawHubFetch(`/skills/${encodeURIComponent(name)}`); if (apiResult && apiResult.name) { return { name: apiResult.name, description: apiResult.description || '', source: 'clawhub', package: apiResult.package || apiResult.author, platforms: apiResult.platforms || [], version: apiResult.version, triggers: apiResult.triggers || [], tools: apiResult.tools || [], installed: false, }; } // Try CLI const cliOutput = runClawHub(`info "${name}" --json 2>/dev/null`); if (cliOutput) { try { const parsed = JSON.parse(cliOutput); return { name: parsed.name || name, description: parsed.description || '', source: 'clawhub', package: parsed.package || parsed.author, platforms: parsed.platforms || [], version: parsed.version, triggers: parsed.triggers || [], installed: false, }; } catch { return undefined; } } return undefined; } async list() { // Try API const apiResult = await clawHubFetch('/skills?limit=200'); if (apiResult && Array.isArray(apiResult.results)) { return apiResult.results.map((s) => ({ name: s.name, description: s.description || '', source: 'clawhub', package: s.package || s.author, platforms: s.platforms || [], installed: false, })); } // Try CLI const cliOutput = runClawHub('list --json 2>/dev/null'); if (cliOutput) { try { const parsed = JSON.parse(cliOutput); if (Array.isArray(parsed)) { return parsed.map((s) => ({ name: s.name, description: s.description || '', source: 'clawhub', package: s.package || s.author, platforms: s.platforms || [], installed: false, })); } } catch { // Fall through } } return []; } async install(name, options) { const target = options.target || 'claude'; // Determine target directory let targetDir; try { const { PlatformSkillResolver } = await import('../../smiths/skillsmith/platform-resolver.js'); targetDir = PlatformSkillResolver.getSkillPath(target, options.projectDir, name); } catch { const platformDirs = { claude: '.claude/skills', copilot: '.github/skills', factory: '.factory/skills', cursor: '.cursor/skills', codex: '.codex/skills', opencode: '.opencode/skill', warp: '.warp/skills', windsurf: '.windsurf/skills', openclaw: path.join(process.env.HOME || '~', '.openclaw', 'skills'), generic: 'skills', }; const baseDir = platformDirs[target] || `.${target}/skills`; targetDir = path.join(options.projectDir, baseDir, name); } // Try CLI install (downloads to a temp location, then we copy) const cliResult = runClawHub(`install "${name}" --output "${targetDir}" 2>/dev/null`); if (cliResult !== null) return; // Try API download const apiResult = await clawHubFetch(`/skills/${encodeURIComponent(name)}/download`); if (apiResult && apiResult.content) { fs.mkdirSync(targetDir, { recursive: true }); const skillMdPath = path.join(targetDir, 'SKILL.md'); fs.writeFileSync(skillMdPath, apiResult.content, 'utf-8'); return; } throw new Error(`Could not install '${name}' from ClawHub. ` + `Ensure the skill exists at https://clawhub.ai and the API is accessible.`); } async publish(packageDir) { // Ensure clawhub.json exists — generate from manifest.json if missing const clawHubManifestPath = path.join(packageDir, 'clawhub.json'); if (!fs.existsSync(clawHubManifestPath)) { const manifestPath = path.join(packageDir, 'manifest.json'); if (fs.existsSync(manifestPath)) { const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')); const pkgJsonPath = path.resolve(packageDir, '..', '..', '..', '..', 'package.json'); let version = '0.0.0'; try { const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')); version = pkg.version || version; } catch { // Use default version } const clawHubManifest = { name: manifest.name || path.basename(packageDir), version, description: manifest.description || '', homepage: 'https://aiwg.io', repository: 'https://github.com/jmagly/aiwg', skills: './skills', tags: manifest.tags || manifest.keywords || [], license: 'MIT', author: 'AIWG Contributors', }; fs.writeFileSync(clawHubManifestPath, JSON.stringify(clawHubManifest, null, 2) + '\n', 'utf-8'); } } // Try CLI publish const cliResult = runClawHub(`publish "${packageDir}" 2>/dev/null`); if (cliResult !== null) return; throw new Error('ClawHub publish requires the clawhub CLI. ' + 'Install it from https://docs.openclaw.ai/tools/clawhub'); } } //# sourceMappingURL=clawhub.js.map