mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-26 01:34:51 +08:00
Skill discovery, install, check-updates and update all download the whole GitHub repo ZIP. The compressed body was collected into a Vec in memory and capped at 128 MiB, so repos whose docs/ images dwarf the skills themselves (K-Dense-AI/scientific-agent-skills: 235 MiB ZIP, 277 MiB uncompressed, 20 MiB of skills) failed before extraction even though they fit the 512 MiB extraction budget and 30k entry cap. The download cap also reused ARCHIVE_TOO_LARGE, so the UI blamed the extraction limit. Stream the body chunk by chunk into an anonymous temp file (deleted on drop, including error paths), still counting actually received bytes, and open the ZipArchive from that file. With memory no longer the constraint, align the download cap with the extraction budget, since a larger compressed body could not be extracted anyway. Report the download cap under its own ARCHIVE_DOWNLOAD_TOO_LARGE code with four-locale text. Extraction budget, entry cap and path/symlink protections are unchanged. Refs #8421
49 lines
1.6 KiB
TypeScript
49 lines
1.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { TFunction } from "i18next";
|
|
import { skillErrorReason } from "@/lib/errors/skillErrorParser";
|
|
|
|
const t = ((key: string, options?: Record<string, unknown>) =>
|
|
options && Object.keys(options).length
|
|
? `${key}:${JSON.stringify(options)}`
|
|
: key) as unknown as TFunction;
|
|
|
|
describe("skillErrorReason", () => {
|
|
it("uses the HTTP explanation for rate-limited downloads", () => {
|
|
const error = JSON.stringify({
|
|
code: "DOWNLOAD_FAILED",
|
|
context: { status: "403" },
|
|
suggestion: "http403",
|
|
});
|
|
expect(skillErrorReason(error, t)).toBe("skills.error.http403");
|
|
});
|
|
|
|
it("uses the error code text with its context otherwise", () => {
|
|
const error = JSON.stringify({
|
|
code: "DOWNLOAD_TIMEOUT",
|
|
context: { owner: "a", name: "b", timeout: "60" },
|
|
suggestion: "checkNetwork",
|
|
});
|
|
expect(skillErrorReason(error, t)).toBe(
|
|
'skills.error.downloadTimeout:{"owner":"a","name":"b","timeout":"60"}',
|
|
);
|
|
});
|
|
|
|
it("tells an oversized download apart from the extraction limit", () => {
|
|
// #8421:下载体超限曾复用 ARCHIVE_TOO_LARGE,被说成「解压后超过上限」
|
|
const error = JSON.stringify({
|
|
code: "ARCHIVE_DOWNLOAD_TOO_LARGE",
|
|
context: { limit_mb: "512" },
|
|
});
|
|
expect(skillErrorReason(error, t)).toBe(
|
|
'skills.error.archiveDownloadTooLarge:{"limit_mb":"512"}',
|
|
);
|
|
});
|
|
|
|
it("keeps the first line of an unstructured error", () => {
|
|
expect(skillErrorReason("connection reset\nstack…", t)).toBe(
|
|
"connection reset",
|
|
);
|
|
expect(skillErrorReason("", t)).toBe("common.error");
|
|
});
|
|
});
|