/**
 * ClawHub API client.
 *
 * Talks to the public ClawHub v1 API (clawhub.ai) to browse,
 * search, and download skills and packages.
 *
 * All read endpoints are public — no auth required.
 */
import { mkdir, writeFile, rm } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
const DEFAULT_REGISTRY = "https://clawhub.ai";
const USER_AGENT = "Jait/1.0";
/* ------------------------------------------------------------------ */
/*  Client                                                              */
/* ------------------------------------------------------------------ */
export class ClawHubClient {
    baseUrl;
    constructor(registryUrl) {
        this.baseUrl = (registryUrl ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
    }
    // ── Skills ──────────────────────────────────────────────────────────
    /** Vector search for skills. */
    async searchSkills(query, limit = 20) {
        const url = new URL("/api/v1/search", this.baseUrl);
        url.searchParams.set("q", query);
        url.searchParams.set("limit", String(limit));
        const res = await this.get(url);
        const data = (await res.json());
        return data.results ?? [];
    }
    /** Browse skills with sorting. */
    async listSkills(opts) {
        const url = new URL("/api/v1/skills", this.baseUrl);
        if (opts?.sort)
            url.searchParams.set("sort", opts.sort);
        if (opts?.limit)
            url.searchParams.set("limit", String(opts.limit));
        if (opts?.cursor)
            url.searchParams.set("cursor", opts.cursor);
        const res = await this.get(url);
        return res.json();
    }
    /** Get a single skill detail. */
    async getSkill(slug) {
        const url = new URL(`/api/v1/skills/${encodeURIComponent(slug)}`, this.baseUrl);
        const res = await this.get(url);
        return res.json();
    }
    /** Download skill zip. Returns raw bytes. */
    async downloadSkill(slug, version) {
        const url = new URL("/api/v1/download", this.baseUrl);
        url.searchParams.set("slug", slug);
        if (version)
            url.searchParams.set("version", version);
        const res = await fetch(url.toString(), {
            headers: { "User-Agent": USER_AGENT },
        });
        if (!res.ok) {
            throw new Error(`Download failed for '${slug}' (HTTP ${res.status})`);
        }
        return res.arrayBuffer();
    }
    // ── Packages / Plugins ──────────────────────────────────────────────
    /** List packages (plugins). */
    async listPackages(opts) {
        const url = new URL("/api/v1/plugins", this.baseUrl);
        if (opts?.limit)
            url.searchParams.set("limit", String(opts.limit));
        const res = await this.get(url);
        const data = (await res.json());
        return Array.isArray(data) ? data : (data.items ?? []);
    }
    // ── Helpers ─────────────────────────────────────────────────────────
    async get(url) {
        const res = await fetch(url.toString(), {
            headers: {
                "User-Agent": USER_AGENT,
                Accept: "application/json",
            },
        });
        if (!res.ok) {
            throw new Error(`ClawHub API error: ${res.status} ${res.statusText} (${url.pathname})`);
        }
        return res;
    }
}
/* ------------------------------------------------------------------ */
/*  Install helpers                                                     */
/* ------------------------------------------------------------------ */
/**
 * Extract a zip buffer into a destination directory.
 * Uses platform-native tools (tar on Windows, unzip on *nix).
 */
export async function extractZip(zipBuffer, destDir) {
    await mkdir(destDir, { recursive: true });
    const tmpFile = join(tmpdir(), `clawhub-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`);
    await writeFile(tmpFile, Buffer.from(zipBuffer));
    try {
        if (process.platform === "win32") {
            await execFileAsync("tar", ["-xf", tmpFile, "-C", destDir]);
        }
        else {
            await execFileAsync("unzip", ["-o", tmpFile, "-d", destDir]);
        }
    }
    finally {
        await rm(tmpFile, { force: true });
    }
}
/**
 * Write origin metadata for a ClawHub-installed skill.
 * Stored as `<skillDir>/.clawhub/origin.json` to match ClawHub CLI convention.
 */
export async function writeOrigin(skillDir, meta) {
    const dir = join(skillDir, ".clawhub");
    await mkdir(dir, { recursive: true });
    await writeFile(join(dir, "origin.json"), JSON.stringify(meta, null, 2));
}
//# sourceMappingURL=client.js.map