
























@@ -455,6 +455,7 @@ describe("collectPluginClawHubReleasePlan", () => {
455455it("retries a rate-limited trusted publisher lookup", async () => {
456456const repoDir = createTempPluginRepo();
457457let trustedPublisherRequests = 0;
458+let rateLimitedBodyCanceled = false;
458459let firstTrustedPublisherRequestAt: number | undefined;
459460let retryTrustedPublisherRequestAt: number | undefined;
460461const fetchImpl: typeof fetch = async (input) => {
@@ -468,7 +469,14 @@ describe("collectPluginClawHubReleasePlan", () => {
468469trustedPublisherRequests += 1;
469470if (trustedPublisherRequests === 1) {
470471firstTrustedPublisherRequestAt = Date.now();
471-return new Response("", { status: 429 });
472+return new Response(
473+new ReadableStream({
474+cancel() {
475+rateLimitedBodyCanceled = true;
476+},
477+}),
478+{ status: 429 },
479+);
472480}
473481retryTrustedPublisherRequestAt = Date.now();
474482return new Response(
@@ -495,6 +503,7 @@ describe("collectPluginClawHubReleasePlan", () => {
495503});
496504497505expect(trustedPublisherRequests).toBe(2);
506+expect(rateLimitedBodyCanceled).toBe(true);
498507expect(retryTrustedPublisherRequestAt).toBeGreaterThanOrEqual(
499508(firstTrustedPublisherRequestAt ?? Number.POSITIVE_INFINITY) + 900,
500509);
@@ -555,6 +564,54 @@ describe("collectPluginClawHubReleasePlan", () => {
555564);
556565});
557566567+it("falls back to the bounded retry schedule for an excessive Retry-After header", async () => {
568+const repoDir = createTempPluginRepo();
569+let trustedPublisherRequests = 0;
570+let firstTrustedPublisherRequestAt: number | undefined;
571+let retryTrustedPublisherRequestAt: number | undefined;
572+const fetchImpl: typeof fetch = async (input) => {
573+const requestUrl =
574+typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
575+const pathname = new URL(requestUrl).pathname;
576+if (pathname === "/api/v1/packages/%40openclaw%2Fdemo-plugin") {
577+return new Response("{}", { status: 200 });
578+}
579+if (pathname === "/api/v1/packages/%40openclaw%2Fdemo-plugin/trusted-publisher") {
580+trustedPublisherRequests += 1;
581+if (trustedPublisherRequests === 1) {
582+firstTrustedPublisherRequestAt = Date.now();
583+return new Response("", { status: 429, headers: { "retry-after": "999999999999" } });
584+}
585+retryTrustedPublisherRequestAt = Date.now();
586+return new Response(
587+JSON.stringify({
588+trustedPublisher: {
589+repository: "openclaw/openclaw",
590+workflowFilename: "plugin-clawhub-release.yml",
591+},
592+}),
593+{ status: 200 },
594+);
595+}
596+if (pathname === "/api/v1/packages/%40openclaw%2Fdemo-plugin/versions/2026.4.1") {
597+return new Response("", { status: 404 });
598+}
599+throw new Error(`Unexpected ClawHub request to ${pathname}`);
600+};
601+602+await collectPluginClawHubReleasePlan({
603+rootDir: repoDir,
604+selection: ["@openclaw/demo-plugin"],
605+ fetchImpl,
606+registryBaseUrl: "https://clawhub.ai",
607+});
608+609+expect(trustedPublisherRequests).toBe(2);
610+expect(retryTrustedPublisherRequestAt).toBeGreaterThanOrEqual(
611+(firstTrustedPublisherRequestAt ?? Number.POSITIVE_INFINITY) + 900,
612+);
613+});
614+558615it("routes missing package rows to bootstrap candidates instead of normal candidates", async () => {
559616const repoDir = createTempPluginRepo();
560617const { fetchImpl } = createClawHubPlanFetch({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。