openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
307 lines (306 loc) • 12.7 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { b as withClawHubResponse, i as fetchClawHubJson, l as readClawHubBytes, n as createClawHubError, r as decodeClawHubResponseBody, s as parseClawHubJsonBody, t as ClawHubRequestError, v as resolveClawHubBaseUrl, y as resolveClawHubImageUrl } from "./clawhub-client-D-mEumEV.mjs";
import { t as reportClawHubInstallTelemetry } from "./clawhub-telemetry-D6AknYPM.mjs";
//#region src/infra/clawhub-skills.ts
const SKILL_CARD_MAX_BYTES = 262144;
const SKILL_VERIFICATION_MAX_BYTES = 67108864;
const CLAWHUB_SKILLS_SH_TRUST_STATE = "not-scanned-by-clawhub";
const CLAWHUB_SKILLS_SH_TRUST_LABEL = "Not scanned by ClawHub";
/** Marks a reference ClawHub resolves from an external source it never scanned. */
const CLAWHUB_SKILLS_SH_REF_PREFIX = "skills-sh:";
/** Source variants ClawHub resolves search results from. Anything else is unidentifiable. */
const CLAWHUB_NATIVE_SOURCE_KIND = "clawhub";
const CLAWHUB_SKILLS_SH_SOURCE_KIND = "skills-sh";
const CLAWHUB_SUPPORTED_INSTALL_KINDS = /* @__PURE__ */ new Set([
"clawhub",
"github",
"skills-sh"
]);
function buildVersionOrTagSearch(params) {
const version = normalizeOptionalString(params.version);
const ownerHandle = normalizeOptionalString(params.ownerHandle);
if (version) return {
version,
...ownerHandle ? { ownerHandle } : {}
};
const tag = normalizeOptionalString(params.tag);
if (tag) return {
tag,
...ownerHandle ? { ownerHandle } : {}
};
return ownerHandle ? { ownerHandle } : void 0;
}
async function searchClawHubSkills(params) {
const registry = resolveClawHubBaseUrl(params.baseUrl);
const query = params.query.trim();
const request = {
baseUrl: registry,
token: params.token,
timeoutMs: params.timeoutMs,
fetchImpl: params.fetchImpl
};
let entries;
if (query) entries = (await fetchClawHubJson({
...request,
path: "/api/v1/search",
search: {
q: query,
limit: params.limit ? String(params.limit) : void 0
}
})).results ?? [];
else entries = ((await fetchClawHubJson({
...request,
path: "/api/v1/trending",
search: {
kind: "skills",
limit: String(Math.min(params.limit ?? 20, 100))
}
})).items ?? []).map((entry) => ({
score: 0,
slug: entry.slug,
displayName: entry.displayName,
summary: entry.summary ?? void 0,
source: entry.source,
install: entry.install,
official: entry.official,
ownerHandle: entry.publisher?.handle,
updatedAt: entry.metrics?.updatedAt ?? void 0
}));
return entries.flatMap((entry) => {
const mapped = toClawHubSkillSearchResult(entry, registry);
return mapped ? [mapped] : [];
});
}
/**
* Records each result's own source once, here, so no consumer rebuilds it. A row whose source is
* unknown, or whose external reference is missing, is dropped rather than published under
* `@owner/slug`: that spelling would point install at a different publisher's skill.
*/
function toClawHubSkillSearchResult(entry, registry) {
const { install: _install, source: _source, ...rest } = entry;
const base = {
...rest,
registry,
icon: resolveClawHubImageUrl(entry.icon, registry)
};
const source = normalizeOptionalString(entry.source);
const installKind = normalizeOptionalString(entry.install?.kind);
const reference = normalizeOptionalString(entry.install?.reference);
if (installKind && !CLAWHUB_SUPPORTED_INSTALL_KINDS.has(installKind)) return;
switch (source) {
case CLAWHUB_SKILLS_SH_SOURCE_KIND:
if (!reference?.startsWith("skills-sh:")) return;
return {
...base,
installRef: reference,
installOnly: true,
trustState: CLAWHUB_SKILLS_SH_TRUST_STATE
};
case CLAWHUB_NATIVE_SOURCE_KIND: {
const ownerHandle = normalizeOptionalString(entry.ownerHandle);
return ownerHandle ? {
...base,
installRef: `@${ownerHandle}/${entry.slug}`
} : void 0;
}
default: return;
}
}
async function fetchClawHubSkillDetail(params) {
const registry = resolveClawHubBaseUrl(params.baseUrl);
const detail = await fetchClawHubJson({
...params,
baseUrl: registry,
path: `/api/v1/skills/${encodeURIComponent(params.slug)}`,
search: params.ownerHandle ? { ownerHandle: params.ownerHandle } : void 0
});
if (detail.skill && detail.skill.slug !== params.slug) throw new Error("ClawHub returned details for a different skill.");
if (params.ownerHandle && detail.owner?.handle && detail.owner.handle !== params.ownerHandle) throw new Error("ClawHub returned details for a different publisher.");
if (!params.includeInspection && params.version === void 0) return {
...detail,
skill: detail.skill ? {
...detail.skill,
icon: resolveClawHubImageUrl(detail.skill.icon, registry)
} : null
};
const ownerHandle = params.ownerHandle ?? detail.owner?.handle ?? void 0;
const version = normalizeOptionalString(params.version) ?? detail.latestVersion?.version;
const request = {
...params,
baseUrl: registry,
ownerHandle,
version
};
const warnings = [];
const [release, card] = version ? await Promise.allSettled([fetchClawHubJson({
...request,
path: `/api/v1/skills/${encodeURIComponent(params.slug)}/versions/${encodeURIComponent(version)}`,
search: ownerHandle ? { ownerHandle } : void 0
}), fetchClawHubSkillCard(request)]) : [];
if (release?.status === "rejected") warnings.push(`Selected release details unavailable: ${String(release.reason)}`);
const returnedVersion = release?.status === "fulfilled" ? release.value.version : void 0;
const selectedVersion = returnedVersion?.version === version ? returnedVersion : void 0;
const releaseMismatch = release?.status === "fulfilled" && selectedVersion === void 0;
if (releaseMismatch) warnings.push("ClawHub returned details for a different release.");
const releaseUnavailable = release?.status === "rejected" && release.reason instanceof ClawHubRequestError && [404, 410].includes(release.reason.status);
const security = selectedVersion?.security;
const validSecurity = security && typeof security.status === "string" && security.status.trim().length > 0 && typeof security.hasWarnings === "boolean" && typeof security.hasScanResult === "boolean";
const isLatest = version !== void 0 && version === detail.latestVersion?.version;
const canUseLatest = isLatest && !releaseUnavailable && !releaseMismatch;
return {
...detail,
registry,
source: "clawhub",
installRef: ownerHandle ? `@${ownerHandle}/${params.slug}` : params.slug,
selectedRelease: selectedVersion ? {
version: selectedVersion.version,
createdAt: selectedVersion.createdAt,
changelog: selectedVersion.changelog,
tags: Object.entries(detail.skill?.tags ?? {}).filter(([, taggedVersion]) => taggedVersion === selectedVersion.version).map(([tag]) => tag)
} : canUseLatest && detail.latestVersion ? {
version: detail.latestVersion.version,
createdAt: detail.latestVersion.createdAt,
changelog: detail.latestVersion.changelog
} : null,
downloadability: !version ? {
status: "unknown",
reason: "The listing has no hosted release; source-backed availability is not reported."
} : releaseUnavailable ? {
status: "unavailable",
reason: "The selected release is not available from ClawHub."
} : {
status: "unknown",
reason: release?.status === "rejected" ? `Selected release details unavailable: ${String(release.reason)}` : releaseMismatch ? "ClawHub returned details for a different release." : "ClawHub does not report downloadability for a selected skill release."
},
card: card?.status === "fulfilled" ? {
status: "available",
content: card.value
} : {
status: "unavailable",
reason: card?.status === "rejected" ? String(card.reason) : "No published release is available for a skill card."
},
requirements: canUseLatest && detail.metadata?.setup ? {
status: "available",
setup: detail.metadata.setup,
os: detail.metadata.os,
systems: detail.metadata.systems,
scope: "registry-setup",
note: "Registry setup keys combine environment and configuration requirements; binary requirements are not reported."
} : {
status: "unavailable",
reason: isLatest ? "ClawHub does not report setup requirements for this release." : "ClawHub reports structured setup requirements only for the latest release."
},
security: validSecurity ? {
status: "available",
scanStatus: security.status,
hasWarnings: security.hasWarnings,
hasScanResult: security.hasScanResult,
checkedAt: typeof security.checkedAt === "number" ? security.checkedAt : null,
summary: typeof security.scanners?.llm?.summary === "string" ? security.scanners.llm.summary : null,
virustotalUrl: typeof security.virustotalUrl === "string" ? security.virustotalUrl : null
} : {
status: "unavailable",
reason: "ClawHub has no security scan snapshot for this release."
},
warnings,
skill: detail.skill ? {
...detail.skill,
icon: resolveClawHubImageUrl(detail.skill.icon, registry)
} : null,
owner: detail.owner ? {
...detail.owner,
image: resolveClawHubImageUrl(detail.owner.image, registry)
} : detail.owner
};
}
async function fetchClawHubSkillInstallResolution(params) {
return await withClawHubResponse({
baseUrl: params.baseUrl,
path: `/api/v1/skills/${encodeURIComponent(params.slug)}/install`,
token: params.token,
timeoutMs: params.timeoutMs,
fetchImpl: params.fetchImpl,
search: {
ownerHandle: params.ownerHandle,
reference: params.requestedReference,
forceInstall: params.forceInstall ? "1" : void 0
}
}, async ({ response, url, hasToken }) => {
const isStructuredBlock = [
403,
409,
410,
423
].includes(response.status);
if (!response.ok && !isStructuredBlock) throw await createClawHubError(response, url, hasToken, params.timeoutMs);
return parseClawHubJsonBody(response, url, params.timeoutMs);
});
}
async function fetchClawHubSkillVerification(params) {
return await fetchClawHubJson({
baseUrl: params.baseUrl,
path: `/api/v1/skills/${encodeURIComponent(params.slug)}/verify`,
maxResponseBytes: SKILL_VERIFICATION_MAX_BYTES,
token: params.token,
skipAuth: params.skipAuth,
timeoutMs: params.timeoutMs,
fetchImpl: params.fetchImpl,
search: {
...buildVersionOrTagSearch(params),
reference: params.requestedReference
}
});
}
async function fetchClawHubSkillSecurityVerdicts(params) {
return await fetchClawHubJson({
baseUrl: params.baseUrl,
path: "/api/v1/skills/-/security-verdicts",
method: "POST",
json: { items: params.items },
token: params.token,
skipAuth: params.skipAuth,
timeoutMs: params.timeoutMs,
fetchImpl: params.fetchImpl
});
}
async function fetchClawHubSkillCard(params) {
const cardUrl = normalizeOptionalString(params.url);
const slug = normalizeOptionalString(params.slug);
if (!cardUrl && !slug) throw new Error("ClawHub skill card fetch requires a slug or card URL");
const providedToken = normalizeOptionalString(params.token);
const skipAuth = cardUrl != null && providedToken == null && new URL(cardUrl, `${resolveClawHubBaseUrl(params.baseUrl)}/`).origin !== new URL(`${resolveClawHubBaseUrl(params.baseUrl)}/`).origin;
return await withClawHubResponse({
baseUrl: params.baseUrl,
url: cardUrl,
path: slug ? `/api/v1/skills/${encodeURIComponent(slug)}/card` : void 0,
token: providedToken,
timeoutMs: params.timeoutMs,
fetchImpl: params.fetchImpl,
search: cardUrl ? void 0 : buildVersionOrTagSearch(params),
skipAuth
}, async ({ response, url, hasToken }) => {
if (!response.ok) throw await createClawHubError(response, url, hasToken, params.timeoutMs);
const bytes = await readClawHubBytes({
response,
maxBytes: SKILL_CARD_MAX_BYTES,
timeoutMs: params.timeoutMs,
resourceLabel: slug ? `skill card for ${slug}` : `skill card at ${url.pathname}`
});
return decodeClawHubResponseBody(bytes);
});
}
async function reportClawHubSkillInstallTelemetry(params) {
return await reportClawHubInstallTelemetry(params, () => {
const slug = params.slug.trim();
return slug ? {
event: "install",
slug,
...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {},
...params.requestedReference ? { reference: params.requestedReference } : {},
...params.trustState ? { trustState: params.trustState } : {},
version: params.version ?? void 0
} : void 0;
});
}
//#endregion
export { fetchClawHubSkillDetail as a, fetchClawHubSkillVerification as c, fetchClawHubSkillCard as i, reportClawHubSkillInstallTelemetry as l, CLAWHUB_SKILLS_SH_TRUST_LABEL as n, fetchClawHubSkillInstallResolution as o, CLAWHUB_SKILLS_SH_TRUST_STATE as r, fetchClawHubSkillSecurityVerdicts as s, CLAWHUB_SKILLS_SH_REF_PREFIX as t, searchClawHubSkills as u };