UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

194 lines (193 loc) 8.58 kB
import { t as CONFIG_DIR } from "./utils-P__uGsPB.js"; import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js"; import { n as resolveEffectiveAgentSkillFilter } from "./agent-filter-DZ-Tmqcd.js"; import { a as normalizeSkillIndexName, t as buildSkillIndexEntries } from "./skill-index-BgtIQy35.js"; import { n as hasBinary } from "./config-eval-YFuZks3-.js"; import { c as resolveSkillsInstallPreferences, i as isSkillEnvRequirementSatisfied, n as isBundledSkillAllowed, o as resolveBundledAllowlist, r as isSkillConfigPathTruthy, s as resolveSkillConfig } from "./config-Br267cOY.js"; import { t as mergeRemoteNodeSkillEntries } from "./remote-skills-Czcw8kW6.js"; import { a as loadWorkspaceSkills } from "./workspace-skill-loader-_a5Sci8B.js"; import { t as resolveBundledSkillsDir } from "./bundled-dir-DFDIT1MV.js"; import { t as evaluateEntryRequirementsForCurrentPlatform } from "./entry-status-CG367mIi.js"; import { c as resolveLocalSkillCardStatusSync, g as readClawHubSkillsLockfileStatusSync, o as resolveClawHubSkillStatusLinkSync } from "./clawhub-uninstall-Dg6VF3Pf.js"; import "./clawhub-D-1bbvLO.js"; import path from "node:path"; //#region src/skills/discovery/status.ts const skillsLogger = createSubsystemLogger("skills"); let hasWarnedMissingBundledDir = false; function resolveSkillStatusEntry(skills, requestedName) { const raw = requestedName.trim(); if (!raw) return null; const lower = raw.toLowerCase(); const normalized = normalizeSkillIndexName(raw); const matchers = [ (skill) => skill.name === raw, (skill) => skill.skillKey === raw, (skill) => skill.name.toLowerCase() === lower || skill.skillKey.toLowerCase() === lower, (skill) => Boolean(normalized) && (normalizeSkillIndexName(skill.name) === normalized || normalizeSkillIndexName(skill.skillKey) === normalized) ]; for (const matches of matchers) { const candidates = skills.filter(matches); if (candidates.length > 0) return candidates.length === 1 ? candidates[0] : null; } return null; } function selectPreferredInstallSpec(install, prefs) { const findKind = (kind) => install.find((spec) => spec.kind === kind); const brewSpec = findKind("brew"); const brewAvailable = brewSpec && hasBinary("brew"); return (prefs.preferBrew && brewAvailable ? brewSpec : void 0) ?? findKind("uv") ?? findKind("node") ?? (brewAvailable ? brewSpec : void 0) ?? findKind("go") ?? findKind("download") ?? brewSpec ?? install[0]; } function normalizeInstallOptions(entry, prefs) { const requiredOs = entry.metadata?.os ?? []; if (requiredOs.length > 0 && !requiredOs.includes(process.platform)) return []; const install = entry.metadata?.install ?? []; if (install.length === 0) return []; const platform = process.platform; const supportsPlatform = (spec) => { const osList = spec.os ?? []; return osList.length === 0 || osList.includes(platform); }; const filtered = install.filter(supportsPlatform); if (filtered.length === 0) return []; const toOption = (spec, index) => { const id = (spec.id ?? `${spec.kind}-${index}`).trim(); const bins = spec.bins ?? []; let label = (spec.label ?? "").trim(); if (spec.kind === "node" && spec.package) label = `Install ${spec.package} (${prefs.nodeManager})`; if (!label) { if (spec.kind === "brew" && spec.formula) label = `Install ${spec.formula} (brew)`; else if (spec.kind === "node" && spec.package) label = `Install ${spec.package} (${prefs.nodeManager})`; else if (spec.kind === "go" && spec.module) label = `Install ${spec.module} (go)`; else if (spec.kind === "uv" && spec.package) label = `Install ${spec.package} (uv)`; else if (spec.kind === "download" && spec.url) { const url = spec.url.trim(); const last = url.split("/").pop(); label = `Download ${last && last.length > 0 ? last : url}`; } else label = "Run installer"; } return { id, kind: spec.kind, label, bins }; }; if (filtered.every((spec) => spec.kind === "download")) { const options = []; for (const [index, spec] of install.entries()) if (supportsPlatform(spec)) options.push(toOption(spec, index)); return options; } const preferred = selectPreferredInstallSpec(filtered, prefs); if (!preferred) return []; return [toOption(preferred, install.indexOf(preferred))]; } function buildSkillStatus(indexed, context) { const entry = indexed.entry; const skillKey = indexed.skillKey; const { config, prefs, eligibility, allowBundled, agentSkillFilter, workspaceDir } = context; const skillConfig = resolveSkillConfig(config, skillKey); const disabled = skillConfig?.enabled === false; const blockedByAllowlist = !isBundledSkillAllowed(entry, allowBundled); const blockedByAgentFilter = agentSkillFilter !== void 0 && !indexed.agentAllowed; const always = entry.metadata?.always === true; const isEnvSatisfied = (envName) => isSkillEnvRequirementSatisfied({ envName, skillConfig, primaryEnv: entry.metadata?.primaryEnv }); const isConfigSatisfied = (pathStr) => isSkillConfigPathTruthy(config, pathStr); const skillSource = indexed.source; const bundled = indexed.bundled; const { emoji, homepage, required, missing, requirementsSatisfied, configChecks } = evaluateEntryRequirementsForCurrentPlatform({ always, entry, hasLocalBin: hasBinary, remote: eligibility?.remote, isEnvSatisfied, isConfigSatisfied }); const eligible = !disabled && !blockedByAllowlist && requirementsSatisfied; const platformIncompatible = missing.os.length > 0; const availableToAgent = eligible && !blockedByAgentFilter; const userInvocable = indexed.userInvocable; const isGlobalManagedSkill = !bundled && skillSource === "openclaw-managed"; const clawhub = workspaceDir && !bundled ? resolveClawHubSkillStatusLinkSync({ workspaceDir: isGlobalManagedSkill ? path.dirname(path.resolve(context.managedSkillsDir)) : workspaceDir, skillDir: entry.skill.baseDir, skillKey, lockRead: isGlobalManagedSkill ? context.managedLockRead : context.clawhubLockRead, lockfileScope: isGlobalManagedSkill ? "managed" : "workspace" }) : void 0; const skillCard = resolveLocalSkillCardStatusSync(entry.skill.baseDir); return { name: entry.skill.name, description: entry.skill.description, source: skillSource, bundled, filePath: entry.skill.filePath, baseDir: entry.skill.baseDir, skillKey, primaryEnv: entry.metadata?.primaryEnv, emoji, homepage, always, disabled, blockedByAllowlist, blockedByAgentFilter, eligible, platformIncompatible, modelVisible: availableToAgent && indexed.promptVisible, userInvocable, commandVisible: availableToAgent && userInvocable, requirements: required, missing, configChecks, install: normalizeInstallOptions(entry, prefs), ...clawhub ? { clawhub } : {}, ...skillCard ? { skillCard } : {} }; } function buildWorkspaceSkillStatus(workspaceDir, opts) { const managedSkillsDir = opts?.managedSkillsDir ?? path.join(CONFIG_DIR, "skills"); const bundledSkillsDir = resolveBundledSkillsDir(); if (!bundledSkillsDir && !hasWarnedMissingBundledDir) { hasWarnedMissingBundledDir = true; skillsLogger.warn("Bundled skills directory could not be resolved; built-in skills may be missing."); } const agentSkillFilter = opts?.agentId ? resolveEffectiveAgentSkillFilter(opts.config, opts.agentId) : void 0; const skillEntries = mergeRemoteNodeSkillEntries(opts?.entries ?? loadWorkspaceSkills(workspaceDir, { config: opts?.config, agentId: opts?.agentId, agentSkillFilter: "ignore", managedSkillsDir, bundledSkillsDir }), { canExec: opts?.eligibility?.nodeSkills?.canExec, node: opts?.eligibility?.nodeSkills?.node }); const prefs = resolveSkillsInstallPreferences(opts?.config); const allowBundled = resolveBundledAllowlist(opts?.config); const clawhubLockRead = readClawHubSkillsLockfileStatusSync(workspaceDir); const managedParentDir = path.dirname(path.resolve(managedSkillsDir)); const managedLockRead = managedParentDir === path.resolve(workspaceDir) ? clawhubLockRead : readClawHubSkillsLockfileStatusSync(managedParentDir); const skillIndexEntries = buildSkillIndexEntries(skillEntries, { agentSkillFilter }); return { workspaceDir, managedSkillsDir, agentId: opts?.agentId, agentSkillFilter, skills: skillIndexEntries.map((entry) => buildSkillStatus(entry, { config: opts?.config, prefs, eligibility: opts?.eligibility, allowBundled, agentSkillFilter, workspaceDir, clawhubLockRead, managedSkillsDir, managedLockRead })) }; } //#endregion export { resolveSkillStatusEntry as n, buildWorkspaceSkillStatus as t };