UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

790 lines (789 loc) 30.5 kB
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { n as isErrno } from "./errno-CkbDOfLk.js"; import { d as pathExists } from "./fs-safe-B6pvPGnf.js"; import { n as ok, t as err } from "./result-BQGgYouL.js"; import { t as formatCliCommand } from "./command-format-C7YfyMTd.js"; import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js"; import { a as sha256Hex } from "./crypto-digest-C4hqTb_e.js"; import { B as downloadClawHubSkillArchive, R as downloadClawHubGitHubSkillArchive, U as normalizeClawHubSha256Integrity, V as downloadClawHubSkillArchiveUrl } from "./official-external-plugin-catalog-Dzu7dwBN.js"; import { n as ClawHubRequestError, s as isDefaultClawHubBaseUrl, y as resolveClawHubBaseUrl } from "./clawhub-client-I4w9Lg5d.js"; import { a as fetchClawHubSkillDetail, c as fetchClawHubSkillVerification, l as reportClawHubSkillInstallTelemetry, n as CLAWHUB_SKILLS_SH_TRUST_LABEL, o as fetchClawHubSkillInstallResolution } from "./clawhub-skills-C9qmDBtk.js"; import { r as withClawPackageLifecycleLease } from "./claw-package-lifecycle-lease-CH70IHcU.js"; import { n as withExtractedArchiveRoot } from "./install-flow-CNwyvboF.js"; import { E as validateRequestedSkillSlug, S as installExtractedSkillRoot, T as resolveWorkspaceSkillInstallDir, b as writeClawHubSkillsLockfile, d as formatClawHubSkillRef, f as normalizeGitHubCommitSegment, h as readClawHubSkillsLockfile, i as planTrackedClawHubSkillState, m as readClawHubSkillOrigin, n as checkClawHubSkillPlanAtPath, o as resolveClawHubSkillStatusLinkSync, p as parseRequestedClawHubSkillRef, u as digestClawHubSkillTree, w as normalizeTrackedSkillSlug, x as CLAWHUB_SKILL_ARCHIVE_ROOT_MARKERS, y as writeClawHubSkillOrigin } from "./clawhub-uninstall-Dg6VF3Pf.js"; import { n as checkClawHubPackageTrust } from "./clawhub-install-trust-cxtpyCVE.js"; import { t as markClawPackageIndependentlyOwned } from "./claw-package-adoption-Ba25vCgq.js"; import path from "node:path"; import fs from "node:fs/promises"; //#region src/skills/lifecycle/clawhub-request-error.ts function formatClawHubSkillRequestError(error, params) { if (!(error instanceof ClawHubRequestError)) return formatErrorMessage(error); const skillPath = `/api/v1/skills/${encodeURIComponent(params.slug)}`; if (error.status === 404 && (error.requestPath.endsWith(skillPath) || error.requestPath.endsWith(`${skillPath}/install`) || error.requestPath.endsWith(`${skillPath}/verify`))) return `Skill "${params.slug}" not found on ClawHub. Run \`${formatCliCommand(`openclaw skills search ${params.slug}`)}\` to find the right skill reference.`; const action = params.operation === "install" ? "installing" : "verifying"; if (error.status === 401) return `ClawHub authentication failed while ${action} skill "${params.slug}". Authenticate with ClawHub and try again.`; if (error.status === 403) return `ClawHub denied access while ${action} skill "${params.slug}". Check your ClawHub access and try again.`; if (error.status === 429) return `ClawHub rate limit reached while ${action} skill "${params.slug}". Wait and try again later.`; if (error.status >= 500) return `ClawHub is temporarily unavailable while ${action} skill "${params.slug}". Try again later.`; return `ClawHub could not ${params.operation} skill "${params.slug}". Check the skill reference and try again.`; } //#endregion //#region src/skills/lifecycle/clawhub-install-core.ts function normalizeExpectedArtifactIntegrity(expectedIntegrity) { if (expectedIntegrity === void 0) return; const normalized = normalizeClawHubSha256Integrity(expectedIntegrity); if (!normalized) throw new Error(`Invalid expected ClawHub archive integrity: ${expectedIntegrity}`); return normalized; } function assertDownloadedArtifactIntegrity(archive, expectedIntegrity) { const normalizedExpected = normalizeExpectedArtifactIntegrity(expectedIntegrity); if (normalizedExpected && archive.integrity !== normalizedExpected) throw new Error(`ClawHub archive integrity mismatch: expected ${normalizedExpected}, got ${archive.integrity}.`); } function hasOfficialClawHubFlag(value) { return value?.channel === "official" || value?.official === true || value?.isOfficial === true; } function isDefaultOfficialClawHubSkillSource(params) { if (!isDefaultClawHubBaseUrl(params.baseUrl)) return false; return hasOfficialClawHubFlag(params.detail?.skill) || hasOfficialClawHubFlag(params.detail?.owner) || hasOfficialClawHubFlag(params.resolution) || params.resolution?.installKind === "archive" && hasOfficialClawHubFlag(params.resolution.archive); } async function fetchDefaultClawHubSkillDetailIfOfficial(params) { if (!isDefaultClawHubBaseUrl(params.baseUrl)) return; try { const detail = await fetchClawHubSkillDetail({ slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, baseUrl: params.baseUrl }); return isDefaultOfficialClawHubSkillSource({ baseUrl: params.baseUrl, detail }) ? detail : void 0; } catch { return; } } async function resolveInstallVersion(params) { const detail = await fetchClawHubSkillDetail({ slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, baseUrl: params.baseUrl }); if (!detail.skill) throw new Error(`Skill "${params.slug}" not found on ClawHub.`); const version = params.version ?? detail.latestVersion?.version; if (!version) throw new Error(`Skill "${params.slug}" has no installable version.`); return { detail, version }; } function normalizeGitHubSourcePath(raw) { const parts = raw.replaceAll("\\", "/").split("/").filter(Boolean); if (parts.length === 0 || parts.some((part) => part === "." || part === "..")) throw new Error(`Invalid GitHub skill source path: ${raw}`); return parts.join("/"); } function buildGitHubTreeUrl(params) { const [owner, name] = params.repo.split("/"); return `https://github.com/${[ owner, name, "tree", params.commit, ...params.sourcePath ? params.sourcePath.split("/") : [] ].map(encodeURIComponent).join("/")}`; } function readVerifiedClawHubSkillSourceUrl(raw) { const provenance = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : void 0; if (provenance?.source !== "server-resolved-github-import") return; const repo = normalizeOptionalString(provenance.repo); const repoParts = repo?.split("/"); const commit = normalizeGitHubCommitSegment(provenance.commit); if (!repo || repoParts?.length !== 2 || repoParts.some((part) => !/^[A-Za-z0-9._-]+$/.test(part)) || !commit) return; const pathValue = normalizeOptionalString(provenance.path); try { const sourcePath = pathValue ? normalizeGitHubSourcePath(pathValue) : void 0; return buildGitHubTreeUrl({ repo, commit, ...sourcePath ? { sourcePath } : {} }); } catch { return; } } function buildDownloadedArtifactLock(archive) { return { kind: archive.artifact, sha256: archive.sha256Hex, integrity: archive.integrity }; } function snapshotClawHubSkillVerification(verification) { return { schema: verification.schema, ok: verification.ok, decision: verification.decision, reasons: [...verification.reasons], ...verification.card !== void 0 ? { card: verification.card } : {}, ...verification.artifact !== void 0 ? { artifact: verification.artifact } : {}, ...verification.provenance !== void 0 ? { provenance: verification.provenance } : {}, ...verification.security !== void 0 ? { security: verification.security } : {}, ...verification.signature !== void 0 ? { signature: verification.signature } : {} }; } async function fetchInstallVerificationLock(params) { try { return snapshotClawHubSkillVerification(await fetchClawHubSkillVerification({ slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, ...params.requestedReference ? { requestedReference: params.requestedReference } : {}, version: params.version, baseUrl: params.baseUrl })); } catch (err) { params.logger?.warn?.(`Skill verification for ${formatClawHubSkillRef(params)} failed: ${formatErrorMessage(err)}`); return; } } async function readInstalledSkillFileLock(skillDir) { for (const marker of CLAWHUB_SKILL_ARCHIVE_ROOT_MARKERS) try { return { path: marker, sha256: sha256Hex(await fs.readFile(path.join(skillDir, marker))) }; } catch { continue; } } function resolveGitHubSkillSourceDir(repoRoot, sourcePath) { return path.join(repoRoot, ...normalizeGitHubSourcePath(sourcePath).split("/")); } async function installArchiveResolution(params) { return await withExtractedArchiveRoot({ archivePath: params.archivePath, tempDirPrefix: "openclaw-skill-clawhub-", timeoutMs: 12e4, rootMarkers: CLAWHUB_SKILL_ARCHIVE_ROOT_MARKERS, onExtracted: async (rootDir) => await installExtractedSkillRoot({ workspaceDir: params.workspaceDir, slug: params.slug, extractedRoot: rootDir, mode: params.force ? "update" : "install", logger: params.logger, onAfterBackup: params.onAfterBackup, policy: { config: params.config, onInstallPolicyWarning: params.onInstallPolicyWarning, installId: "clawhub", origin: { type: "clawhub", registry: params.registry, slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, version: params.version }, source: { kind: "clawhub", authority: params.authority, mutable: false, network: true }, requestedSpecifier: `clawhub:${formatClawHubSkillRef(params)}@${params.version}` }, rootMarkers: CLAWHUB_SKILL_ARCHIVE_ROOT_MARKERS }) }); } async function installGitHubResolution(params) { return await withExtractedArchiveRoot({ archivePath: params.archivePath, tempDirPrefix: "openclaw-skill-clawhub-github-", timeoutMs: 12e4, onExtracted: async (repoRoot) => await installExtractedSkillRoot({ workspaceDir: params.workspaceDir, slug: params.slug, extractedRoot: resolveGitHubSkillSourceDir(repoRoot, params.sourcePath), mode: params.force ? "update" : "install", logger: params.logger, onAfterBackup: params.onAfterBackup, policy: { config: params.config, onInstallPolicyWarning: params.onInstallPolicyWarning, installId: "clawhub", origin: { type: "clawhub", registry: params.registry, slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, version: params.commit, repo: params.repo, path: params.sourcePath, commit: params.commit, ...params.requestedReference ? { reference: params.requestedReference } : {}, ...params.trustState ? { trustState: params.trustState } : {} }, source: { kind: "git", authority: params.authority, mutable: false, network: true }, requestedSpecifier: params.requestedReference ?? `clawhub:${formatClawHubSkillRef(params)}@${params.commit}` }, rootMarkers: CLAWHUB_SKILL_ARCHIVE_ROOT_MARKERS }) }); } function assertInstallResolutionAllowed(resolution) { if (!resolution.ok) { if (resolution.reason === "ambiguous_slug") { const message = resolution.message ? ` ${resolution.message}` : ""; throw new Error(`Skill "${resolution.slug}" is ambiguous on ClawHub. Install an owner-qualified skill, for example: openclaw skills install @owner/${resolution.slug}.${message}`); } throw new Error(resolution.message || `Skill "${resolution.slug}" is not installable.`); } if (resolution.installKind !== "github") return resolution; const commit = normalizeGitHubCommitSegment(resolution.github.commit)?.toLowerCase(); if (!commit) throw new Error(`Skill "${resolution.slug}" resolved to a mutable or invalid GitHub source ref; expected a full 40-character commit SHA.`); return { ...resolution, github: { ...resolution.github, commit } }; } async function checkClawHubSkillTrust(params) { if (params.skipClawHubTrustCheck) return { ok: true }; const result = await checkClawHubPackageTrust({ subject: { kind: "skill", packageName: params.slug, workspaceDir: params.workspaceDir, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {} }, version: params.version, baseUrl: params.baseUrl, logger: params.logger, mode: params.force ? "update" : "install", confirmInstall: params.confirmInstall }); return result.ok ? { ok: true, ...result.warning ? { warning: result.warning } : {} } : { ok: false, error: result.error, ...result.code ? { code: result.code } : {}, ...result.warning ? { warning: result.warning } : {} }; } async function performClawHubSkillInstall(params) { try { normalizeExpectedArtifactIntegrity(params.expectedIntegrity); const targetDir = resolveWorkspaceSkillInstallDir(params.workspaceDir, params.slug); const registry = resolveClawHubBaseUrl(params.baseUrl); if (!params.force && await pathExists(targetDir)) return { ok: false, error: `Skill already exists at ${targetDir}. Re-run with force/update.` }; await readClawHubSkillsLockfile(params.workspaceDir); let version; let detail; let resolution; let trustWarning; let official = false; let archive; if (params.version) { const resolved = await resolveInstallVersion(params); detail = resolved.detail; version = resolved.version; official = isDefaultOfficialClawHubSkillSource({ baseUrl: params.baseUrl, detail }); const trust = await checkClawHubSkillTrust({ ...params, version, skipClawHubTrustCheck: official }); if (!trust.ok) return { ...trust, version }; trustWarning = trust.warning; params.logger?.info?.(`Downloading ${params.slug}@${version} from ClawHub…`); archive = await downloadClawHubSkillArchive({ slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, version, baseUrl: params.baseUrl }); } else { resolution = assertInstallResolutionAllowed(await fetchClawHubSkillInstallResolution({ slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, ...params.requestedReference ? { requestedReference: params.requestedReference } : {}, baseUrl: params.baseUrl, ...params.forceInstall ? { forceInstall: true } : {} })); if (params.requestedReference) { if (resolution.installKind !== "github" || resolution.trust?.state !== "not-scanned-by-clawhub") throw new Error(`Skill "${params.slug}" did not resolve to an unscanned, commit-pinned GitHub source.`); trustWarning = CLAWHUB_SKILLS_SH_TRUST_LABEL; params.logger?.warn?.(CLAWHUB_SKILLS_SH_TRUST_LABEL); } detail = isDefaultOfficialClawHubSkillSource({ baseUrl: params.baseUrl, resolution }) ? void 0 : await fetchDefaultClawHubSkillDetailIfOfficial({ baseUrl: params.baseUrl, slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {} }); official = isDefaultOfficialClawHubSkillSource({ baseUrl: params.baseUrl, detail, resolution }); if (resolution.installKind === "github") { version = resolution.github.commit; params.logger?.info?.(`Downloading ${params.slug}@${version} from GitHub…`); archive = await downloadClawHubGitHubSkillArchive({ repo: resolution.github.repo, commit: resolution.github.commit }); } else { version = resolution.archive.version; const trust = await checkClawHubSkillTrust({ ...params, version, skipClawHubTrustCheck: official }); if (!trust.ok) return { ...trust, version }; trustWarning = trust.warning; params.logger?.info?.(`Downloading ${params.slug}@${version} from ClawHub…`); archive = await downloadClawHubSkillArchiveUrl({ url: resolution.archive.downloadUrl, baseUrl: params.baseUrl }); } } try { assertDownloadedArtifactIntegrity(archive, params.expectedIntegrity); if (!params.version && !resolution) throw new Error(`Skill "${params.slug}" has no install resolution.`); const install = resolution?.installKind === "github" && !params.version ? await installGitHubResolution({ workspaceDir: params.workspaceDir, slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, sourcePath: resolution.github.path, archivePath: archive.archivePath, registry, authority: official ? "official" : "third-party", repo: resolution.github.repo, commit: resolution.github.commit, requestedReference: params.requestedReference, trustState: params.trustState, force: params.force, logger: params.logger, config: params.config, onInstallPolicyWarning: params.onInstallPolicyWarning, onAfterBackup: params.onAfterBackup }) : await installArchiveResolution({ workspaceDir: params.workspaceDir, slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, version, archivePath: archive.archivePath, registry, authority: official ? "official" : isDefaultClawHubBaseUrl(params.baseUrl) ? "openclaw" : "third-party", force: params.force, logger: params.logger, config: params.config, onInstallPolicyWarning: params.onInstallPolicyWarning, onAfterBackup: params.onAfterBackup }); if (!install.ok) return { ok: false, error: install.error }; const installedAt = Date.now(); const artifact = buildDownloadedArtifactLock(archive); const fileTreeSha256 = await digestClawHubSkillTree(install.targetDir); const verificationVersion = resolution?.installKind === "github" && !params.version ? void 0 : version; const [skillFile, verification] = await Promise.all([readInstalledSkillFileLock(install.targetDir), fetchInstallVerificationLock({ slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, ...params.requestedReference ? { requestedReference: params.requestedReference } : {}, version: verificationVersion, baseUrl: params.baseUrl, logger: params.logger })]); const sourceUrl = (resolution?.installKind === "github" ? normalizeOptionalString(resolution.github.sourceUrl) : void 0) ?? readVerifiedClawHubSkillSourceUrl(verification?.provenance); const trackedMetadata = { ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, ...params.requestedReference ? { requestedReference: params.requestedReference } : {}, ...params.trustState ? { trustState: params.trustState } : {}, installedAt, ...sourceUrl ? { sourceUrl } : {}, artifact, ...skillFile ? { skillFile } : {}, fileTreeSha256 }; await writeClawHubSkillOrigin(install.targetDir, { version: 1, registry, slug: params.slug, ...trackedMetadata, installedVersion: version }); const lock = await readClawHubSkillsLockfile(params.workspaceDir); lock.skills[params.slug] = { version, registry, ...trackedMetadata, ...verification ? { verification } : {} }; await writeClawHubSkillsLockfile(params.workspaceDir, lock); if (!params.clawManaged) markClawPackageIndependentlyOwned({ kind: "skill", source: "clawhub", ref: formatClawHubSkillRef(params), version, workspace: params.workspaceDir }); await reportClawHubSkillInstallTelemetry({ baseUrl: params.baseUrl, slug: params.slug, ...params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}, version, ...params.requestedReference ? { requestedReference: params.requestedReference } : {}, ...params.trustState ? { trustState: params.trustState } : {} }).catch(() => void 0); return { ok: true, slug: params.slug, version, targetDir: install.targetDir, ...detail ? { detail } : {}, ...trustWarning ? { warning: trustWarning } : {} }; } finally { await archive.cleanup().catch(() => void 0); } } catch (err) { return { ok: false, error: formatClawHubSkillRequestError(err, { slug: params.slug, operation: "install" }) }; } } //#endregion //#region src/skills/lifecycle/clawhub.ts async function verifySkillWithClawHub(params) { try { return ok(await fetchClawHubSkillVerification(params)); } catch (error) { return err(formatClawHubSkillRequestError(error, { slug: params.slug, operation: "verify" })); } } async function resolveRequestedUpdateSlug(params) { const requested = params.requestedSlug.trim(); const requestedRef = requested.startsWith("@") || requested.startsWith("skills-sh:") ? parseRequestedClawHubSkillRef(requested) : { slug: normalizeTrackedSkillSlug(requested) }; const trackedSlug = requestedRef.slug; const trackedOrigin = await readClawHubSkillOrigin(resolveWorkspaceSkillInstallDir(params.workspaceDir, trackedSlug)); const trackedLockEntry = params.lock.skills[trackedSlug]; if (!trackedOrigin && !trackedLockEntry) return validateRequestedSkillSlug(requestedRef.slug); const trackedOwnerHandle = trackedOrigin?.ownerHandle ?? trackedLockEntry?.ownerHandle; if (requestedRef.ownerHandle && trackedOwnerHandle !== requestedRef.ownerHandle) { const trackedRef = trackedOwnerHandle ? `@${trackedOwnerHandle}/${trackedSlug}` : trackedSlug; throw new Error(`Skill "${trackedSlug}" is tracked as ${trackedRef}, not @${requestedRef.ownerHandle}/${trackedSlug}.`); } const trackedRequestedReference = trackedOrigin?.requestedReference ?? trackedLockEntry?.requestedReference; if (requestedRef.requestedReference && trackedRequestedReference !== requestedRef.requestedReference) throw new Error(`Skill "${trackedSlug}" is not tracked from ${requestedRef.requestedReference}.`); return trackedSlug; } async function installRequestedSkillFromClawHub(params) { try { const ref = parseRequestedClawHubSkillRef(params.slug); if (ref.requestedReference && params.version) throw new Error("--version is not supported for skills-sh references."); return await performClawHubSkillInstall({ ...params, slug: ref.slug, ...ref.ownerHandle ? { ownerHandle: ref.ownerHandle } : {}, ...ref.requestedReference ? { requestedReference: ref.requestedReference } : {}, ...ref.trustState ? { trustState: ref.trustState } : {} }); } catch (err) { return { ok: false, error: formatErrorMessage(err) }; } } async function installTrackedSkillFromClawHub(params) { try { return await performClawHubSkillInstall({ ...params, slug: normalizeTrackedSkillSlug(params.slug) }); } catch (err) { return { ok: false, error: formatErrorMessage(err) }; } } async function preflightSkillOwnerState(params) { const targetDir = resolveWorkspaceSkillInstallDir(params.workspaceDir, params.requested.slug); if (!await pathExists(targetDir)) return { ok: true, action: "install", integrity: params.integrity }; const status = resolveClawHubSkillStatusLinkSync({ workspaceDir: params.workspaceDir, skillDir: targetDir, skillKey: params.requested.slug }); if (status?.status === "linked" && status.installedVersion === params.version && status.ownerHandle === params.requested.ownerHandle && status.artifact?.integrity === params.integrity) return { ok: true, action: "reuse", integrity: params.integrity }; return { ok: false, code: "skill_version_conflict", error: `Skill ${params.requestedLabel}@${params.version} conflicts with the existing workspace skill at ${targetDir}.` }; } async function preflightSkillFromClawHub(params) { try { const requested = parseRequestedClawHubSkillRef(params.slug); const resolved = await resolveInstallVersion({ slug: requested.slug, ...requested.ownerHandle ? { ownerHandle: requested.ownerHandle } : {}, version: params.version, baseUrl: params.baseUrl }); if (resolved.version !== params.version) return { ok: false, code: "skill_version_resolution_mismatch", error: `Skill ${params.slug}@${params.version} resolved to ${resolved.version}.` }; const trust = await checkClawHubSkillTrust({ workspaceDir: params.workspaceDir, slug: requested.slug, ...requested.ownerHandle ? { ownerHandle: requested.ownerHandle } : {}, version: resolved.version, baseUrl: params.baseUrl, logger: params.logger, skipClawHubTrustCheck: isDefaultOfficialClawHubSkillSource({ baseUrl: params.baseUrl, detail: resolved.detail }) }); if (!trust.ok) return { ok: false, code: trust.code ?? "skill_trust_required", error: trust.error }; if (params.expectedIntegrity) { const integrity = normalizeExpectedArtifactIntegrity(params.expectedIntegrity); const owner = await preflightSkillOwnerState({ workspaceDir: params.workspaceDir, requested, requestedLabel: params.slug, version: resolved.version, integrity }); return owner.ok && trust.warning ? { ...owner, warning: trust.warning } : owner; } const archive = await downloadClawHubSkillArchive({ slug: requested.slug, ...requested.ownerHandle ? { ownerHandle: requested.ownerHandle } : {}, version: resolved.version, baseUrl: params.baseUrl }); try { const integrity = normalizeClawHubSha256Integrity(archive.integrity); if (!integrity) return { ok: false, code: "skill_integrity_unavailable", error: `Skill ${params.slug}@${params.version} did not resolve a valid artifact integrity.` }; const owner = await preflightSkillOwnerState({ workspaceDir: params.workspaceDir, requested, requestedLabel: params.slug, version: resolved.version, integrity }); return owner.ok && trust.warning ? { ...owner, warning: trust.warning } : owner; } finally { await archive.cleanup().catch(() => void 0); } } catch (err) { return { ok: false, code: "skill_preflight_failed", error: formatErrorMessage(err) }; } } async function resolveTrackedUpdateTarget(params) { const origin = await readClawHubSkillOrigin(resolveWorkspaceSkillInstallDir(params.workspaceDir, params.slug)); const lockEntry = params.lock.skills[params.slug]; if (!origin && !lockEntry) return { ok: false, slug: params.slug, error: `Skill "${params.slug}" is not tracked as a ClawHub install.` }; const ownerHandle = origin?.ownerHandle ?? lockEntry?.ownerHandle; const requestedReference = origin?.requestedReference ?? lockEntry?.requestedReference; const trustState = origin?.trustState ?? lockEntry?.trustState; return { ok: true, slug: params.slug, ...ownerHandle ? { ownerHandle } : {}, ...requestedReference ? { requestedReference } : {}, ...trustState ? { trustState } : {}, baseUrl: origin?.registry ?? params.baseUrl, previousVersion: origin?.installedVersion ?? lockEntry?.version ?? null }; } async function installSkillFromClawHub(params) { if (params.clawManaged) return await installRequestedSkillFromClawHub(params); return await withClawPackageLifecycleLease({ kind: "skill", source: "clawhub", ref: params.slug, workspace: params.workspaceDir }, () => installRequestedSkillFromClawHub(params)); } async function guardTrackedSkillLocalState(params) { const targetDir = resolveWorkspaceSkillInstallDir(params.workspaceDir, params.slug); try { await fs.lstat(targetDir); } catch (error) { if (isErrno(error) && error.code === "ENOENT") return { ok: true, plan: void 0 }; return { ok: false, error: String(error) }; } const local = await planTrackedClawHubSkillState({ workspaceDir: params.workspaceDir, requestedRef: { slug: params.slug }, expectedVersion: params.previousVersion ?? "" }); if (local.ok) return { ok: true, plan: local.plan }; return { ok: false, error: local.error }; } async function updateSkillsFromClawHub(params) { const lock = await readClawHubSkillsLockfile(params.workspaceDir); const slugs = params.slug ? [await resolveRequestedUpdateSlug({ workspaceDir: params.workspaceDir, requestedSlug: params.slug, lock })] : Object.keys(lock.skills).map((slug) => normalizeTrackedSkillSlug(slug)); const results = []; for (const slug of slugs) { const tracked = await resolveTrackedUpdateTarget({ workspaceDir: params.workspaceDir, slug, lock, baseUrl: params.baseUrl }); if (!tracked.ok) { results.push({ ok: false, error: tracked.error }); continue; } const install = await withClawPackageLifecycleLease({ kind: "skill", source: "clawhub", ref: tracked.slug, workspace: params.workspaceDir }, async () => { let replaceBlocked; let localPlan; if (!params.force) { const local = await guardTrackedSkillLocalState({ workspaceDir: params.workspaceDir, slug: tracked.slug, previousVersion: tracked.previousVersion }); if (!local.ok) return { ok: false, code: "force_required", error: `${local.error} Updating replaces the installed skill directory.` }; localPlan = local.plan; } const installed = await installTrackedSkillFromClawHub({ workspaceDir: params.workspaceDir, slug: tracked.slug, ...tracked.ownerHandle ? { ownerHandle: tracked.ownerHandle } : {}, ...tracked.requestedReference ? { requestedReference: tracked.requestedReference } : {}, ...tracked.trustState ? { trustState: tracked.trustState } : {}, baseUrl: tracked.baseUrl, force: true, forceInstall: params.forceInstall, logger: params.logger, config: params.config, onInstallPolicyWarning: params.onInstallPolicyWarning, ...params.force ? {} : { onAfterBackup: async (backupDir) => { const current = localPlan ? await checkClawHubSkillPlanAtPath(localPlan, backupDir) : { ok: false, error: `Skill ${JSON.stringify(tracked.slug)} appeared during update.` }; replaceBlocked = current.ok ? void 0 : `${current.error} Updating replaces the installed skill directory.`; return replaceBlocked; } } }); if (!installed.ok && replaceBlocked) return { ok: false, code: "force_required", error: replaceBlocked }; return installed; }, { required: true }); results.push(install.ok ? { ok: true, slug: tracked.slug, previousVersion: tracked.previousVersion, version: install.version, changed: tracked.previousVersion !== install.version, targetDir: install.targetDir, ...install.warning ? { warning: install.warning } : {} } : install); } return results; } //#endregion export { readVerifiedClawHubSkillSourceUrl as a, verifySkillWithClawHub as i, preflightSkillFromClawHub as n, updateSkillsFromClawHub as r, installSkillFromClawHub as t };