From 0fd55aac9d2b946be4ffa28f6a515c11bcc94d83 Mon Sep 17 00:00:00 2001 From: m-RNA Date: Sat, 29 Aug 2026 18:06:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=98=E6=96=B9=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E4=BC=98=E5=85=88=E4=BD=BF=E7=94=A8=20Gitee=20=E4=B8=BB?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将官方 QQBot 的发布源顺序调整为 Gitee 主源、GitHub 镜像,并登记不可变的 0.0.4 版本。补充 Gitee 当前资产域名白名单与回归测试,确保更新机器人可以安全下载并校验双端制品。 --- README.md | 2 +- docs/PROTOCOL_V1.md | 3 +- extensions/packingproof.qqbot/extension.json | 5 ++ .../packingproof.qqbot/versions/0.0.4.json | 57 ++++++++++++++++ registry/catalog.v1.json | 6 +- registry/extensions/packingproof.qqbot.json | 65 +++++++++++++++++++ tests/package-validator.test.mjs | 28 +++++++- tests/releases.test.mjs | 6 +- tools/lib/package-validator.mjs | 2 +- 9 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 extensions/packingproof.qqbot/versions/0.0.4.json diff --git a/README.md b/README.md index f82dabc..d5fb20e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ PackingProof 官方扩展登记仓库,负责公开协议、发布者和扩展 - `userscript`:由 PackingProof 导入到现有油猴脚本管理流程 - `external-adapter`:PackingProof 校验并解包后展示说明和所在目录,永不自动执行 -- GitHub/Gitee 主发布源与可选镜像;主发布源发现失败时,更新机器人尝试镜像发现 +- 官方扩展优先使用 Gitee 主发布源和 GitHub 镜像;主发布源发现失败时,更新机器人尝试镜像发现 - 只有稳定 SemVer 版本;不收录 draft、prerelease 或 Raw 文件 - 官方与第三方两级来源标识;市场收录不代表安全保证 diff --git a/docs/PROTOCOL_V1.md b/docs/PROTOCOL_V1.md index bf2be7e..a71e4c2 100644 --- a/docs/PROTOCOL_V1.md +++ b/docs/PROTOCOL_V1.md @@ -24,7 +24,8 @@ Publisher ID 是与托管平台账号分离的稳定身份。GitHub/Gitee 用户 - 只接受 GitHub/Gitee Release Asset - Asset 名称固定为 `-.ppx` - 不接受 Raw、分支文件、自建 CDN、draft 或 prerelease -- 主源可以是 GitHub 或 Gitee;镜像存在时必须使用另一平台 +- 官方扩展优先使用 Gitee 主源、GitHub 镜像;第三方扩展可按实际托管情况选择主源 +- 镜像存在时必须使用另一平台 - 主源和镜像必须是同一字节流 - 更新机器人先检查主发布源;主源发现请求失败后检查镜像发布源 - 每个版本的 `primary` 指向本次实际可验证的登记源;另一可用来源记录为 `mirror` diff --git a/extensions/packingproof.qqbot/extension.json b/extensions/packingproof.qqbot/extension.json index 23bd425..eebb83e 100644 --- a/extensions/packingproof.qqbot/extension.json +++ b/extensions/packingproof.qqbot/extension.json @@ -19,6 +19,11 @@ }, "categories": ["chatbot", "recording"], "releaseSources": [ + { + "provider": "gitee", + "owner": "PackingProof", + "name": "PackingProof-QQBot" + }, { "provider": "github", "owner": "PackingProof", diff --git a/extensions/packingproof.qqbot/versions/0.0.4.json b/extensions/packingproof.qqbot/versions/0.0.4.json new file mode 100644 index 0000000..9378847 --- /dev/null +++ b/extensions/packingproof.qqbot/versions/0.0.4.json @@ -0,0 +1,57 @@ +{ + "$schema": "../../../schemas/version.v1.schema.json", + "access": { + "packingProofCapabilities": [], + "packingProofPermissions": [ + "recordings.search", + "recordings.download", + "recordings.delivery" + ], + "systemAccess": [ + { + "id": "network", + "reason": "连接 QQ 官方接口与用户配置的 PackingProof 主机" + }, + { + "id": "filesystem.read", + "reason": "读取 QQBot 本机配置、加密凭据和运行状态" + }, + { + "id": "filesystem.write", + "reason": "保存本机配置、加密凭据、日志和待发送的临时录像" + }, + { + "id": "other", + "reason": "用户启用开机自动启动时写入当前 Windows 用户的启动项" + } + ] + }, + "compatibility": { + "minPackingProofVersion": "0.0.62", + "platforms": { + "windows": [ + "x64" + ] + } + }, + "downloads": { + "mirror": { + "provider": "github", + "url": "https://github.com/PackingProof/PackingProof-QQBot/releases/download/v0.0.4/packingproof.qqbot-0.0.4.ppx" + }, + "primary": { + "provider": "gitee", + "url": "https://gitee.com/PackingProof/PackingProof-QQBot/releases/download/v0.0.4/packingproof.qqbot-0.0.4.ppx" + } + }, + "extensionId": "packingproof.qqbot", + "publishedAt": "2026-08-29T18:00:17+08:00", + "schemaVersion": 1, + "sha256": "9fbe13acb06bf160cd91114c82af88497f6717d79fb2d7e39299912bbd45b138", + "size": 72514406, + "source": { + "commit": "bddd99595e538589d2b643ac65ea6b236bd278a8", + "tag": "v0.0.4" + }, + "version": "0.0.4" +} diff --git a/registry/catalog.v1.json b/registry/catalog.v1.json index 0a17784..65e2a41 100644 --- a/registry/catalog.v1.json +++ b/registry/catalog.v1.json @@ -3,7 +3,7 @@ { "details": "extensions/packingproof.qqbot.json", "id": "packingproof.qqbot", - "latestVersion": "0.0.3", + "latestVersion": "0.0.4", "name": "PackingProof QQBot", "publisher": { "displayName": "PackingProof", @@ -15,6 +15,6 @@ } ], "schemaVersion": 1, - "sourceDigest": "aa55ae9fae7a9199ed11fc55cf7362307df7395295d13605101704fa68ad0f1c", - "updatedAt": "2026-08-29T09:37:09Z" + "sourceDigest": "b61f4cd9e7ad7fb1cee7e4336576836244fbcfd42391906e3fb83c239ef10aee", + "updatedAt": "2026-08-29T18:00:17+08:00" } diff --git a/registry/extensions/packingproof.qqbot.json b/registry/extensions/packingproof.qqbot.json index 0ecaecb..4df0624 100644 --- a/registry/extensions/packingproof.qqbot.json +++ b/registry/extensions/packingproof.qqbot.json @@ -14,6 +14,11 @@ "name": "PackingProof QQBot", "publisherId": "packingproof", "releaseSources": [ + { + "name": "PackingProof-QQBot", + "owner": "PackingProof", + "provider": "gitee" + }, { "name": "PackingProof-QQBot", "owner": "PackingProof", @@ -42,6 +47,66 @@ "schemaVersion": 1, "trust": "official", "versions": [ + { + "advisory": null, + "release": { + "access": { + "packingProofCapabilities": [], + "packingProofPermissions": [ + "recordings.search", + "recordings.download", + "recordings.delivery" + ], + "systemAccess": [ + { + "id": "network", + "reason": "连接 QQ 官方接口与用户配置的 PackingProof 主机" + }, + { + "id": "filesystem.read", + "reason": "读取 QQBot 本机配置、加密凭据和运行状态" + }, + { + "id": "filesystem.write", + "reason": "保存本机配置、加密凭据、日志和待发送的临时录像" + }, + { + "id": "other", + "reason": "用户启用开机自动启动时写入当前 Windows 用户的启动项" + } + ] + }, + "compatibility": { + "minPackingProofVersion": "0.0.62", + "platforms": { + "windows": [ + "x64" + ] + } + }, + "downloads": { + "mirror": { + "provider": "github", + "url": "https://github.com/PackingProof/PackingProof-QQBot/releases/download/v0.0.4/packingproof.qqbot-0.0.4.ppx" + }, + "primary": { + "provider": "gitee", + "url": "https://gitee.com/PackingProof/PackingProof-QQBot/releases/download/v0.0.4/packingproof.qqbot-0.0.4.ppx" + } + }, + "extensionId": "packingproof.qqbot", + "publishedAt": "2026-08-29T18:00:17+08:00", + "schemaVersion": 1, + "sha256": "9fbe13acb06bf160cd91114c82af88497f6717d79fb2d7e39299912bbd45b138", + "size": 72514406, + "source": { + "commit": "bddd99595e538589d2b643ac65ea6b236bd278a8", + "tag": "v0.0.4" + }, + "version": "0.0.4" + }, + "status": "available" + }, { "advisory": null, "release": { diff --git a/tests/package-validator.test.mjs b/tests/package-validator.test.mjs index d51a8e0..9e5a35f 100644 --- a/tests/package-validator.test.mjs +++ b/tests/package-validator.test.mjs @@ -3,12 +3,38 @@ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import test from "node:test"; -import { validatePackage } from "../tools/lib/package-validator.mjs"; +import { downloadPackage, validatePackage } from "../tools/lib/package-validator.mjs"; import { createSchemaValidator } from "../tools/lib/schemas.mjs"; import { externalManifest, userscriptManifest, writeZip } from "./helpers.mjs"; const rootDirectory = path.resolve(import.meta.dirname, ".."); +test("allows Gitee release redirects to its current asset host", async () => { + const temporary = await mkdtemp(path.join(os.tmpdir(), "ppx-gitee-download-")); + const originalFetch = globalThis.fetch; + try { + globalThis.fetch = async () => { + const response = new Response(Buffer.from("ppx"), { + status: 200, + headers: { "content-length": "3" }, + }); + Object.defineProperty(response, "url", { + value: "https://foruda.gitee.com/attach_file/example/sample.ppx", + }); + return response; + }; + const destination = path.join(temporary, "sample.ppx"); + assert.equal(await downloadPackage( + "https://gitee.com/SampleOrg/Demo/releases/download/v1.0.0/sample.ppx", + destination, + ), 3); + assert.equal(await readFile(destination, "utf8"), "ppx"); + } finally { + globalThis.fetch = originalFetch; + await rm(temporary, { recursive: true, force: true }); + } +}); + test("validates userscript package and normalizes X.Y metadata version", async () => { const temporary = await mkdtemp(path.join(os.tmpdir(), "ppx-userscript-")); try { diff --git a/tests/releases.test.mjs b/tests/releases.test.mjs index 62e4805..f38bd71 100644 --- a/tests/releases.test.mjs +++ b/tests/releases.test.mjs @@ -14,7 +14,7 @@ test("falls back to the mirror release API when primary discovery fails", async const requested = []; globalThis.fetch = async (url) => { requested.push(String(url)); - if (String(url).includes("api.github.com")) throw new Error("primary unavailable"); + if (String(url).includes("gitee.com")) throw new Error("primary unavailable"); return { ok: true, json: async () => [{ @@ -28,10 +28,10 @@ test("falls back to the mirror release API when primary discovery fails", async }; try { const result = await discoverWithFallback([ - { provider: "github", owner: "SampleOrg", name: "Demo" }, { provider: "gitee", owner: "SampleOrg", name: "Demo" }, + { provider: "github", owner: "SampleOrg", name: "Demo" }, ]); - assert.equal(result.source.provider, "gitee"); + assert.equal(result.source.provider, "github"); assert.equal(result.releases[0].tag, "v1.2.0"); assert.equal(requested.length, 2); } finally { diff --git a/tools/lib/package-validator.mjs b/tools/lib/package-validator.mjs index f77def9..91b6086 100644 --- a/tools/lib/package-validator.mjs +++ b/tools/lib/package-validator.mjs @@ -52,7 +52,7 @@ function assertAllowedRedirect(originalUrl, finalUrl) { const finalHost = new URL(finalUrl).hostname.toLowerCase(); const allowed = originalHost === "github.com" ? new Set(["github.com", "objects.githubusercontent.com", "release-assets.githubusercontent.com"]) - : new Set(["gitee.com", "files.gitee.com"]); + : new Set(["gitee.com", "files.gitee.com", "foruda.gitee.com"]); if (!allowed.has(finalHost)) throw new Error(`下载重定向到了未授权域名:${finalHost}`); }