
























@@ -636,6 +636,87 @@ async function fetchJson<T>(params: ClawHubRequestParams): Promise<T> {
636636return (await response.json()) as T;
637637}
638638639+function buildClawHubBodyTimeoutError(resourceLabel: string, timeoutMs: number): Error {
640+return new Error(`ClawHub ${resourceLabel} body stalled after ${timeoutMs}ms`);
641+}
642+643+async function readClawHubBodyChunkWithTimeout(params: {
644+reader: ReadableStreamDefaultReader<Uint8Array>;
645+timeoutMs: number;
646+resourceLabel: string;
647+}): Promise<ReadableStreamReadResult<Uint8Array>> {
648+return await new Promise((resolve, reject) => {
649+let timeout: ReturnType<typeof setTimeout> | undefined;
650+let settled = false;
651+const settle = (fn: () => void) => {
652+if (settled) {
653+return;
654+}
655+settled = true;
656+if (timeout !== undefined) {
657+clearTimeout(timeout);
658+}
659+fn();
660+};
661+662+timeout = setTimeout(() => {
663+const error = buildClawHubBodyTimeoutError(params.resourceLabel, params.timeoutMs);
664+void params.reader.cancel(error).catch(() => undefined);
665+settle(() => reject(error));
666+}, params.timeoutMs);
667+668+void params.reader.read().then(
669+(result) => settle(() => resolve(result)),
670+(error: unknown) => settle(() => reject(error)),
671+);
672+});
673+}
674+675+async function readClawHubResponseBytes(params: {
676+response: Response;
677+timeoutMs?: number;
678+resourceLabel: string;
679+}): Promise<Uint8Array> {
680+const timeoutMs = params.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
681+const body = params.response.body;
682+if (!body || typeof body.getReader !== "function") {
683+return new Uint8Array(await params.response.arrayBuffer());
684+}
685+686+const reader = body.getReader();
687+const chunks: Uint8Array[] = [];
688+let total = 0;
689+try {
690+while (true) {
691+const { done, value } = await readClawHubBodyChunkWithTimeout({
692+ reader,
693+ timeoutMs,
694+resourceLabel: params.resourceLabel,
695+});
696+if (done) {
697+break;
698+}
699+if (!value?.length) {
700+continue;
701+}
702+chunks.push(value);
703+total += value.length;
704+}
705+} finally {
706+try {
707+reader.releaseLock();
708+} catch {}
709+}
710+711+const bytes = new Uint8Array(total);
712+let offset = 0;
713+for (const chunk of chunks) {
714+bytes.set(chunk, offset);
715+offset += chunk.length;
716+}
717+return bytes;
718+}
719+639720export function resolveClawHubBaseUrl(baseUrl?: string): string {
640721return normalizeBaseUrl(baseUrl);
641722}
@@ -902,7 +983,11 @@ export async function downloadClawHubPackageArchive(params: {
902983if (!response.ok) {
903984throw await buildClawHubError(response, url, hasToken);
904985}
905-const bytes = new Uint8Array(await response.arrayBuffer());
986+const bytes = await readClawHubResponseBytes({
987+ response,
988+timeoutMs: params.timeoutMs,
989+resourceLabel: `ClawPack download for ${params.name}@${params.version}`,
990+});
906991const sha256Hex = formatSha256Hex(bytes);
907992const npmIntegrity = formatSha512Integrity(bytes);
908993const npmShasum = formatSha1Hex(bytes);
@@ -977,7 +1062,11 @@ export async function downloadClawHubPackageArchive(params: {
9771062if (!response.ok) {
9781063throw await buildClawHubError(response, url, hasToken);
9791064}
980-const bytes = new Uint8Array(await response.arrayBuffer());
1065+const bytes = await readClawHubResponseBytes({
1066+ response,
1067+timeoutMs: params.timeoutMs,
1068+resourceLabel: `package archive download for ${params.name}`,
1069+});
9811070const sha256Hex = formatSha256Hex(bytes);
9821071const target = await createTempDownloadTarget({
9831072prefix: "openclaw-clawhub-package",
@@ -1018,7 +1107,11 @@ export async function downloadClawHubSkillArchive(params: {
10181107if (!response.ok) {
10191108throw await buildClawHubError(response, url, hasToken);
10201109}
1021-const bytes = new Uint8Array(await response.arrayBuffer());
1110+const bytes = await readClawHubResponseBytes({
1111+ response,
1112+timeoutMs: params.timeoutMs,
1113+resourceLabel: `skill archive download for ${params.slug}`,
1114+});
10221115const sha256Hex = formatSha256Hex(bytes);
10231116const target = await createTempDownloadTarget({
10241117prefix: "openclaw-clawhub-skill",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。