openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
220 lines (219 loc) • 8.68 kB
JavaScript
import { t as pruneMapToMaxSize } from "./map-size-BlrH_CnE.mjs";
import { r as resolveOsHomeDir } from "./home-dir-D0b-ywue.mjs";
import { o as resolveUserPath } from "./home-dir-Dc6nN6HB.mjs";
import { p as normalizeTrimmedStringList, y as uniqueStrings } from "./string-normalization-D6DKCpwE.mjs";
import { c as readFileDescriptorBoundedSync, o as openRootFileSync, r as isRootFileMissingFailure } from "./boundary-file-read-CCth2Hwg.mjs";
import { o as safeRealpathSync } from "./boundary-path-CAv_plX7.mjs";
import { g as resolveConfigDir, t as CONFIG_DIR } from "./utils-CitNzLyF.mjs";
import { r as isPathInside } from "./path-guards-CH7noY0Z.mjs";
import { r as resolveActivePluginInstallRoots, t as hasActivePluginInstallRoots } from "./install-root-context-BJfgtQE2.mjs";
import { t as parseSkillFrontmatter } from "./frontmatter-JGnV9USz.mjs";
import { t as materializeSkill } from "./skill-materializer-BwnrFtya.mjs";
import { t as resolveWorkshopSkillsDir } from "./skills-root-BEm7oxJo.mjs";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
//#region src/skills/loading/symlink-targets.ts
function resolveAllowedSkillSymlinkTargetRealPaths(config) {
const targetPaths = normalizeTrimmedStringList(config?.skills?.load?.allowSymlinkTargets).map((dir) => safeRealpathSync(resolveUserPath(dir))).filter((dir) => Boolean(dir));
return uniqueStrings(targetPaths);
}
const tryRealpath = safeRealpathSync;
//#endregion
//#region src/skills/loading/skill-paths.ts
function resolvePluginSkillsDir() {
const stateDir = hasActivePluginInstallRoots() ? resolveActivePluginInstallRoots().stateDir : CONFIG_DIR;
return path.join(stateDir, "plugin-skills");
}
/** Workspace sync excludes repository internals and installed dependencies at every depth. */
function shouldSyncSkillPath(filePath) {
const name = path.basename(filePath);
return name !== ".git" && name !== "node_modules";
}
function resolveSkillsUserHomeDir() {
return resolveOsHomeDir(process.env, os.homedir);
}
function resolveNativeUserHomeDir() {
try {
return path.resolve(os.homedir());
} catch {
return;
}
}
function resolveCompactHomePrefixes() {
const resolvedHomes = [resolveSkillsUserHomeDir(), resolveNativeUserHomeDir()].filter((home) => Boolean(home)).map((home) => path.resolve(home));
const realHomes = resolvedHomes.map((home) => tryRealpath(home)).filter((home) => Boolean(home));
return uniqueStrings([...resolvedHomes, ...realHomes]).toSorted((a, b) => b.length - a.length).flatMap(compactHomePrefixesForHome);
}
/** Compact prompt-facing skill paths while preserving managed paths that `~` cannot reach. */
function compactPromptSkills(skills, options = {}) {
const prefixes = resolveCompactHomePrefixes();
if (prefixes.length === 0) return skills;
const preservedRoots = resolvePreservedPromptSkillPathRoots(options);
const tildeRoots = resolvePromptTildeRoots();
return skills.map((skill) => ({
...skill,
filePath: shouldPreservePromptSkillPath(skill.filePath, preservedRoots, tildeRoots) ? skill.filePath : compactHomePath(skill.filePath, prefixes)
}));
}
function resolvePreservedPromptSkillPathRoots(options) {
const configDir = resolveConfigDir();
const promptSkillDirs = [
...options.config && options.agentId ? [resolveWorkshopSkillsDir(options.config, options.agentId)] : [],
path.resolve(configDir, "skills"),
path.resolve(configDir, "plugin-skills")
];
const realPromptSkillDirs = promptSkillDirs.map((dir) => tryRealpath(dir)).filter((dir) => Boolean(dir));
return uniqueStrings([...promptSkillDirs, ...realPromptSkillDirs]);
}
function resolvePromptTildeRoots() {
const nativeHome = resolveNativeUserHomeDir();
if (!nativeHome) return [];
const resolvedNativeHome = path.resolve(nativeHome);
if (isContainerStateHomeWherePromptTildeEscapes(resolvedNativeHome)) return [];
const realNativeHome = tryRealpath(resolvedNativeHome);
return uniqueStrings([resolvedNativeHome, ...realNativeHome ? [realNativeHome] : []]);
}
function isContainerStateHomeWherePromptTildeEscapes(home) {
const configDir = path.resolve(resolveConfigDir());
return home === "/data" && (configDir === "/data/.openclaw" || isPathInside("/data/.openclaw", configDir));
}
function shouldPreservePromptSkillPath(filePath, roots, tildeRoots) {
const resolvedFilePath = path.resolve(filePath);
if (!roots.some((root) => isPathInside(root, resolvedFilePath))) return false;
return !tildeRoots.some((root) => isPathInside(root, resolvedFilePath));
}
function compactHomePath(filePath, prefixes) {
for (const prefix of prefixes) if (filePath.startsWith(prefix)) {
const relative = filePath.slice(prefix.length);
return "~/" + (prefix.includes("\\") ? relative.replace(/\\/g, "/") : relative);
}
return filePath;
}
function compactHomePrefixesForHome(home) {
const prefixes = [home.endsWith(path.sep) ? home : home + path.sep];
if (home.includes("\\") && !home.endsWith("\\")) prefixes.push(home + "\\");
return prefixes;
}
function compactSkillPath(filePath) {
return compactHomePath(filePath, resolveCompactHomePrefixes());
}
//#endregion
//#region src/skills/loading/local-loader.ts
const MAX_CACHED_SKILL_CONTENT_CHARS = 16384;
const MAX_CACHED_SKILL_CONTENTS = 256;
const localSkillContentCache = /* @__PURE__ */ new Map();
function readSkillFileSync(params) {
const opened = openRootFileSync({
absolutePath: params.filePath,
rootPath: params.rootRealPath,
rootRealPath: params.rootRealPath,
boundaryLabel: "skill root",
rejectSymlinks: false,
rejectHardlinks: params.rejectHardlinks !== false
});
if (!opened.ok) {
if (!isRootFileMissingFailure(opened)) {
const message = opened.error instanceof Error ? opened.error.message : `failed to open skill file (${opened.reason})`;
params.onDiagnostic?.({
kind: opened.reason === "validation" ? "invalid" : "read",
path: params.filePath,
message
});
}
return null;
}
try {
return params.maxBytes === void 0 ? fs.readFileSync(opened.fd, "utf8") : readFileDescriptorBoundedSync(opened.fd, params.maxBytes).toString("utf8");
} catch (error) {
const message = error instanceof Error ? error.message : "failed to read skill file";
params.onDiagnostic?.({
kind: error instanceof RangeError ? "invalid" : "read",
path: params.filePath,
message
});
return null;
} finally {
fs.closeSync(opened.fd);
}
}
function loadSingleSkillDirectory(params) {
const skillFilePath = path.join(params.skillDir, "SKILL.md");
const raw = readSkillFileSync({
rootRealPath: params.rootRealPath,
filePath: skillFilePath,
maxBytes: params.maxBytes,
rejectHardlinks: params.rejectHardlinks,
onDiagnostic: params.onDiagnostic
});
if (raw === null) return null;
const fallbackName = path.basename(params.skillDir).trim();
const filePath = path.resolve(skillFilePath);
const baseDir = path.resolve(params.skillDir);
let loaded = localSkillContentCache.get(raw);
if (loaded && !loaded.frontmatter.name?.trim() && loaded.skill.name !== fallbackName) loaded = void 0;
if (!loaded) {
let frontmatter;
try {
frontmatter = parseSkillFrontmatter(raw);
} catch (error) {
const message = error instanceof Error ? error.message : "failed to parse skill frontmatter";
params.onDiagnostic?.({
kind: "invalid",
path: skillFilePath,
message
});
return null;
}
const name = frontmatter.name?.trim() || fallbackName;
const description = frontmatter.description?.trim();
if (!name || !description) {
params.onDiagnostic?.({
kind: "invalid",
path: skillFilePath,
message: !name ? "name is required" : "description is required"
});
return null;
}
loaded = {
skill: materializeSkill({
content: raw,
frontmatter,
name,
description,
filePath,
baseDir,
source: params.source,
sourceOptions: {
source: params.source,
scope: "project",
origin: "top-level"
}
}),
frontmatter
};
}
if (raw.length <= MAX_CACHED_SKILL_CONTENT_CHARS) {
localSkillContentCache.delete(raw);
localSkillContentCache.set(raw, loaded);
pruneMapToMaxSize(localSkillContentCache, MAX_CACHED_SKILL_CONTENTS);
}
return {
skill: {
...loaded.skill,
filePath,
baseDir,
source: params.source,
sourceInfo: {
...loaded.skill.sourceInfo,
path: filePath,
baseDir,
source: params.source
}
},
frontmatter: { ...loaded.frontmatter },
content: raw
};
}
//#endregion
export { resolveSkillsUserHomeDir as a, tryRealpath as c, resolvePluginSkillsDir as i, compactPromptSkills as n, shouldSyncSkillPath as o, compactSkillPath as r, resolveAllowedSkillSymlinkTargetRealPaths as s, loadSingleSkillDirectory as t };