





















@@ -23,6 +23,7 @@ import { resolvePluginSkillDirs } from "./plugin-skills.js";
2323import { serializeByKey } from "./serialize.js";
2424import { formatSkillsForPrompt, type Skill } from "./skill-contract.js";
2525import type {
26+OpenClawSkillMetadata,
2627ParsedSkillFrontmatter,
2728SkillEligibilityContext,
2829SkillEntry,
@@ -31,6 +32,8 @@ import type {
31323233const fsp = fs.promises;
3334const skillsLogger = createSubsystemLogger("skills");
35+const SKILL_SOURCE_ORIGIN_RELATIVE_PATH = path.join(".openclaw", "source-origin.json");
36+const MAX_SKILL_SOURCE_ORIGIN_BYTES = 16 * 1024;
34373538/**
3639 * Replace the user's home directory prefix with `~` in skill file paths
@@ -407,6 +410,48 @@ function loadContainedSkillRecords(params: {
407410 : records;
408411}
409412413+function readSourceInstallSkillKey(skillDir: string): string | undefined {
414+try {
415+const sourceOriginPath = path.join(skillDir, SKILL_SOURCE_ORIGIN_RELATIVE_PATH);
416+const stat = fs.lstatSync(sourceOriginPath);
417+if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_SKILL_SOURCE_ORIGIN_BYTES) {
418+return undefined;
419+}
420+const skillDirRealPath = tryRealpath(skillDir);
421+const sourceOriginRealPath = tryRealpath(sourceOriginPath);
422+if (
423+!skillDirRealPath ||
424+!sourceOriginRealPath ||
425+!isPathInside(skillDirRealPath, sourceOriginRealPath)
426+) {
427+return undefined;
428+}
429+const raw = fs.readFileSync(sourceOriginPath, "utf8");
430+const parsed = JSON.parse(raw) as { slug?: unknown };
431+return normalizeOptionalString(parsed.slug);
432+} catch {
433+return undefined;
434+}
435+}
436+437+function resolveSkillEntryMetadata(params: {
438+frontmatter: ParsedSkillFrontmatter;
439+skillDir: string;
440+}): OpenClawSkillMetadata | undefined {
441+const metadata = resolveOpenClawMetadata(params.frontmatter);
442+if (metadata?.skillKey) {
443+return metadata;
444+}
445+const sourceInstallSkillKey = readSourceInstallSkillKey(params.skillDir);
446+if (!sourceInstallSkillKey) {
447+return metadata;
448+}
449+return {
450+ ...metadata,
451+skillKey: sourceInstallSkillKey,
452+};
453+}
454+410455function canonicalizeLoadedSkillRecord(
411456record: LoadedSkillRecord,
412457canonicalSkillDir: string,
@@ -932,7 +977,7 @@ function loadSkillEntries(
932977return {
933978 skill,
934979 frontmatter,
935-metadata: resolveOpenClawMetadata(frontmatter),
980+metadata: resolveSkillEntryMetadata({ frontmatter, skillDir: skill.baseDir }),
936981 invocation,
937982 ...(record.syncSourceDir !== undefined ? { syncSourceDir: record.syncSourceDir } : {}),
938983 ...(record.syncDirName !== undefined ? { syncDirName: record.syncDirName } : {}),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。