






















@@ -9,6 +9,8 @@ import {
99downloadClawHubPackageArchive,
1010downloadClawHubSkillArchive,
1111downloadClawHubSkillArchiveUrl,
12+fetchClawHubSkillDetail,
13+fetchClawHubSkillInstallResolution,
1214fetchClawHubSkillCard,
1315fetchClawHubSkillSecurityVerdicts,
1416fetchClawHubPackageArtifact,
@@ -316,6 +318,65 @@ describe("clawhub helpers", () => {
316318expect(url.searchParams.get("q")).toBe("calendar");
317319});
318320321+it("sends owner-qualified skill detail lookups as slug plus ownerHandle", async () => {
322+let requestedUrl = "";
323+324+await expect(
325+fetchClawHubSkillDetail({
326+slug: "weather",
327+ownerHandle: "demo-owner",
328+fetchImpl: async (input) => {
329+requestedUrl = input instanceof Request ? input.url : String(input);
330+return new Response(
331+JSON.stringify({
332+skill: {
333+slug: "weather",
334+displayName: "Weather",
335+createdAt: 1,
336+updatedAt: 2,
337+},
338+}),
339+{ headers: { "content-type": "application/json" } },
340+);
341+},
342+}),
343+).resolves.toMatchObject({ skill: { slug: "weather" } });
344+345+const url = new URL(requestedUrl);
346+expect(url.pathname).toBe("/api/v1/skills/weather");
347+expect(url.searchParams.get("ownerHandle")).toBe("demo-owner");
348+});
349+350+it("sends owner-qualified skill install resolution lookups as slug plus ownerHandle", async () => {
351+let requestedUrl = "";
352+353+await expect(
354+fetchClawHubSkillInstallResolution({
355+slug: "weather",
356+ownerHandle: "demo-owner",
357+fetchImpl: async (input) => {
358+requestedUrl = input instanceof Request ? input.url : String(input);
359+return new Response(
360+JSON.stringify({
361+ok: true,
362+slug: "weather",
363+installKind: "archive",
364+archive: {
365+version: "1.0.0",
366+downloadUrl: "https://clawhub.ai/api/v1/download?slug=weather&version=1.0.0",
367+},
368+}),
369+{ headers: { "content-type": "application/json" } },
370+);
371+},
372+}),
373+).resolves.toMatchObject({ ok: true, slug: "weather" });
374+375+const url = new URL(requestedUrl);
376+expect(url.pathname).toBe("/api/v1/skills/weather/install");
377+expect(url.searchParams.get("ownerHandle")).toBe("demo-owner");
378+});
379+319380it("fetches skill verification reports and lets version take precedence over tag", async () => {
320381let requestedUrl = "";
321382const envelope = {
@@ -360,6 +421,43 @@ describe("clawhub helpers", () => {
360421expect(url.searchParams.has("tag")).toBe(false);
361422});
362423424+it("sends owner-qualified skill verification lookups as slug plus ownerHandle", async () => {
425+let requestedUrl = "";
426+427+await expect(
428+fetchClawHubSkillVerification({
429+slug: "weather",
430+ownerHandle: "demo-owner",
431+version: "1.0.0",
432+fetchImpl: async (input) => {
433+requestedUrl = input instanceof Request ? input.url : String(input);
434+return new Response(
435+JSON.stringify({
436+schema: "clawhub.skill.verify.v1",
437+ok: true,
438+decision: "pass",
439+reasons: [],
440+skill: {},
441+publisher: {},
442+version: {},
443+card: {},
444+artifact: {},
445+provenance: {},
446+security: {},
447+signature: {},
448+}),
449+{ headers: { "content-type": "application/json" } },
450+);
451+},
452+}),
453+).resolves.toMatchObject({ schema: "clawhub.skill.verify.v1" });
454+455+const url = new URL(requestedUrl);
456+expect(url.pathname).toBe("/api/v1/skills/weather/verify");
457+expect(url.searchParams.get("ownerHandle")).toBe("demo-owner");
458+expect(url.searchParams.get("version")).toBe("1.0.0");
459+});
460+363461it("posts bulk skill security verdict requests", async () => {
364462let requestedUrl = "";
365463let requestedInit: RequestInit | undefined;
@@ -858,6 +956,32 @@ describe("clawhub helpers", () => {
858956}
859957});
860958959+it("sends owner-qualified skill archive downloads as slug plus ownerHandle", async () => {
960+let requestedUrl = "";
961+const archive = await downloadClawHubSkillArchive({
962+slug: "weather",
963+ownerHandle: "demo-owner",
964+version: "1.0.0",
965+fetchImpl: async (input) => {
966+requestedUrl = input instanceof Request ? input.url : String(input);
967+return new Response(new Uint8Array([7, 8, 9]), {
968+status: 200,
969+headers: { "content-type": "application/zip" },
970+});
971+},
972+});
973+974+try {
975+const url = new URL(requestedUrl);
976+expect(url.pathname).toBe("/api/v1/download");
977+expect(url.searchParams.get("slug")).toBe("weather");
978+expect(url.searchParams.get("ownerHandle")).toBe("demo-owner");
979+expect(url.searchParams.get("version")).toBe("1.0.0");
980+} finally {
981+await archive.cleanup();
982+}
983+});
984+861985it("does not send ambient ClawHub auth tokens to off-registry resolver archive URLs", async () => {
862986process.env.OPENCLAW_CLAWHUB_TOKEN = "env-token-123";
863987let requestedUrl = "";
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。