mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-26 07:19:32 +00:00
Expose verified ClawHub source in skill verify output (#93532)
* fix(skills): expose verified ClawHub source in verify output * fix(ci): repair verify check regressions * fix(ci): refresh prompt snapshots * fix(skills): require pinned ClawHub verify commits
This commit is contained in:
@@ -85,6 +85,7 @@ const mocks = vi.hoisted(() => {
|
||||
installSkillFromSourceMock: vi.fn(),
|
||||
updateSkillsFromClawHubMock: vi.fn(),
|
||||
readTrackedClawHubSkillSlugsMock: vi.fn(),
|
||||
readVerifiedClawHubSkillSourceUrlMock: vi.fn(),
|
||||
resolveClawHubSkillVerificationTargetMock: vi.fn(),
|
||||
readClawHubSkillsLockfileStatusSyncMock: vi.fn((..._args: unknown[]) => ({ kind: "missing" })),
|
||||
resolveClawHubSkillStatusLinkSyncMock: vi.fn(),
|
||||
@@ -110,6 +111,7 @@ const {
|
||||
installSkillFromSourceMock,
|
||||
updateSkillsFromClawHubMock,
|
||||
readTrackedClawHubSkillSlugsMock,
|
||||
readVerifiedClawHubSkillSourceUrlMock,
|
||||
resolveClawHubSkillVerificationTargetMock,
|
||||
readClawHubSkillsLockfileStatusSyncMock,
|
||||
resolveClawHubSkillStatusLinkSyncMock,
|
||||
@@ -191,6 +193,8 @@ vi.mock("../skills/lifecycle/clawhub.js", () => ({
|
||||
updateSkillsFromClawHub: (...args: unknown[]) => mocks.updateSkillsFromClawHubMock(...args),
|
||||
readTrackedClawHubSkillSlugs: (...args: unknown[]) =>
|
||||
mocks.readTrackedClawHubSkillSlugsMock(...args),
|
||||
readVerifiedClawHubSkillSourceUrl: (...args: unknown[]) =>
|
||||
mocks.readVerifiedClawHubSkillSourceUrlMock(...args),
|
||||
resolveClawHubSkillVerificationTarget: (...args: unknown[]) =>
|
||||
mocks.resolveClawHubSkillVerificationTargetMock(...args),
|
||||
readClawHubSkillsLockfileStatusSync: (...args: unknown[]) =>
|
||||
@@ -255,6 +259,7 @@ describe("skills cli commands", () => {
|
||||
installSkillFromSourceMock.mockReset();
|
||||
updateSkillsFromClawHubMock.mockReset();
|
||||
readTrackedClawHubSkillSlugsMock.mockReset();
|
||||
readVerifiedClawHubSkillSourceUrlMock.mockReset();
|
||||
resolveClawHubSkillVerificationTargetMock.mockReset();
|
||||
readClawHubSkillsLockfileStatusSyncMock.mockReset();
|
||||
resolveClawHubSkillStatusLinkSyncMock.mockReset();
|
||||
@@ -278,6 +283,7 @@ describe("skills cli commands", () => {
|
||||
});
|
||||
updateSkillsFromClawHubMock.mockResolvedValue([]);
|
||||
readTrackedClawHubSkillSlugsMock.mockResolvedValue([]);
|
||||
readVerifiedClawHubSkillSourceUrlMock.mockReturnValue(undefined);
|
||||
readClawHubSkillsLockfileStatusSyncMock.mockReturnValue({ kind: "missing" });
|
||||
resolveClawHubSkillStatusLinkSyncMock.mockReturnValue(undefined);
|
||||
resolveLocalSkillCardStatusSyncMock.mockReturnValue(undefined);
|
||||
@@ -844,6 +850,47 @@ describe("skills cli commands", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("includes verified ClawHub source URLs in verify JSON output", async () => {
|
||||
const provenance = {
|
||||
source: "server-resolved-github-import",
|
||||
repo: "openclaw/skills",
|
||||
commit: "0123456789abcdef0123456789abcdef01234567",
|
||||
path: "agentreceipt",
|
||||
};
|
||||
const verifiedSourceUrl =
|
||||
"https://github.com/openclaw/skills/tree/0123456789abcdef0123456789abcdef01234567/agentreceipt";
|
||||
readVerifiedClawHubSkillSourceUrlMock.mockReturnValueOnce(verifiedSourceUrl);
|
||||
fetchClawHubSkillVerificationMock.mockResolvedValueOnce({
|
||||
schema: "clawhub.skill.verify.v1",
|
||||
ok: true,
|
||||
decision: "pass",
|
||||
reasons: [],
|
||||
skill: { slug: "agentreceipt", displayName: "Agent Receipt" },
|
||||
publisher: { handle: "openclaw" },
|
||||
version: { version: "1.2.3" },
|
||||
card: {
|
||||
available: true,
|
||||
url: "https://private.example.com/clawhub/api/v1/skills/agentreceipt/card?version=1.2.3",
|
||||
},
|
||||
artifact: {
|
||||
sourceFingerprint: "source-fingerprint",
|
||||
bundleFingerprints: ["generated-bundle-fingerprint"],
|
||||
},
|
||||
provenance,
|
||||
security: { status: "clean" },
|
||||
signature: { status: "unsigned" },
|
||||
});
|
||||
|
||||
await runCommand(["skills", "verify", "agentreceipt"]);
|
||||
|
||||
expect(readVerifiedClawHubSkillSourceUrlMock).toHaveBeenCalledWith(provenance);
|
||||
const payload = JSON.parse(runtimeStdout.at(-1) ?? "{}") as {
|
||||
openclaw?: { verifiedSourceUrl?: string };
|
||||
};
|
||||
expect(payload.openclaw?.verifiedSourceUrl).toBe(verifiedSourceUrl);
|
||||
expect(defaultRuntime.exit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("fetches generated Skill Card markdown for --card", async () => {
|
||||
fetchClawHubSkillVerificationMock.mockResolvedValueOnce({
|
||||
schema: "clawhub.skill.verify.v1",
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import {
|
||||
installSkillFromClawHub,
|
||||
readVerifiedClawHubSkillSourceUrl,
|
||||
readTrackedClawHubSkillSlugs,
|
||||
resolveClawHubSkillVerificationTarget,
|
||||
searchSkillsFromClawHub,
|
||||
@@ -151,6 +152,7 @@ function buildSkillVerificationOutput(
|
||||
result: ClawHubSkillVerificationResponse,
|
||||
target: ResolvedClawHubSkillVerificationTarget,
|
||||
): Record<string, unknown> {
|
||||
const verifiedSourceUrl = readVerifiedClawHubSkillSourceUrl(result.provenance);
|
||||
return {
|
||||
...result,
|
||||
openclaw: {
|
||||
@@ -160,6 +162,7 @@ function buildSkillVerificationOutput(
|
||||
registry: target.resolution.registry,
|
||||
installedVersion: target.resolution.installedVersion,
|
||||
},
|
||||
...(verifiedSourceUrl ? { verifiedSourceUrl } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -193,4 +193,70 @@ describe("skills verify CLI", () => {
|
||||
expect(mocks.defaultRuntime.exit).not.toHaveBeenCalled();
|
||||
expect(mocks.runtimeErrors).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it("surfaces only server-verified source provenance in verify JSON", async () => {
|
||||
const sourceUrl = "https://github.com/openclaw/skills/tree/main/agentreceipt";
|
||||
const verifiedSourceUrl =
|
||||
"https://github.com/openclaw/skills/tree/0123456789abcdef0123456789abcdef01234567/agentreceipt";
|
||||
mocks.fetchClawHubSkillVerificationMock.mockResolvedValueOnce({
|
||||
schema: "clawhub.skill.verify.v1",
|
||||
ok: true,
|
||||
decision: "pass",
|
||||
reasons: [],
|
||||
skill: { slug: "agentreceipt" },
|
||||
publisher: { handle: "openclaw" },
|
||||
version: { version: "1.0.0" },
|
||||
card: { available: true },
|
||||
artifact: { sourceFingerprint: "source-fp" },
|
||||
provenance: {
|
||||
source: "server-resolved-github-import",
|
||||
kind: "github",
|
||||
url: sourceUrl,
|
||||
repo: "openclaw/skills",
|
||||
ref: "main",
|
||||
commit: "0123456789abcdef0123456789abcdef01234567",
|
||||
path: "agentreceipt",
|
||||
},
|
||||
security: { status: "clean" },
|
||||
signature: { status: "unsigned" },
|
||||
});
|
||||
|
||||
await runCommand(["skills", "verify", "agentreceipt"]);
|
||||
|
||||
const payload = JSON.parse(mocks.runtimeStdout.at(-1) ?? "{}") as {
|
||||
openclaw?: { verifiedSourceUrl?: string };
|
||||
};
|
||||
expect(payload.openclaw?.verifiedSourceUrl).toBe(verifiedSourceUrl);
|
||||
expect(mocks.defaultRuntime.exit).not.toHaveBeenCalled();
|
||||
expect(mocks.runtimeErrors).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it("does not promote unavailable provenance URLs in verify JSON", async () => {
|
||||
mocks.fetchClawHubSkillVerificationMock.mockResolvedValueOnce({
|
||||
schema: "clawhub.skill.verify.v1",
|
||||
ok: true,
|
||||
decision: "pass",
|
||||
reasons: [],
|
||||
skill: { slug: "agentreceipt" },
|
||||
publisher: { handle: "openclaw" },
|
||||
version: { version: "1.0.0" },
|
||||
card: { available: true },
|
||||
artifact: { sourceFingerprint: "source-fp" },
|
||||
provenance: {
|
||||
source: "unavailable",
|
||||
url: "https://github.com/openclaw/skills/tree/unverified/agentreceipt",
|
||||
},
|
||||
security: { status: "clean" },
|
||||
signature: { status: "unsigned" },
|
||||
});
|
||||
|
||||
await runCommand(["skills", "verify", "agentreceipt"]);
|
||||
|
||||
const payload = JSON.parse(mocks.runtimeStdout.at(-1) ?? "{}") as {
|
||||
openclaw?: { verifiedSourceUrl?: string };
|
||||
};
|
||||
expect(payload.openclaw?.verifiedSourceUrl).toBeUndefined();
|
||||
expect(mocks.defaultRuntime.exit).not.toHaveBeenCalled();
|
||||
expect(mocks.runtimeErrors).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user