fix: restore GitHub Agent update checks with paginated discovery

The GitHub release history exceeded the catalog response limit, preventing users from checking Agent updates. Read ten releases per page and stop at the first eligible catalog while preserving the existing 100-release search window and signature checks.

Release note: 修复选择 GitHub 来源时 Agent 更新检查提示发布目录超出大小限制的问题;镜像更新和已有本地包保持不变。
This commit is contained in:
mesalogo
2026-09-06 19:25:40 +08:00
parent e692362414
commit 7003141e56
4 changed files with 73 additions and 14 deletions
@@ -257,6 +257,48 @@ function response(
}
describe('AgentPackageManager remote install candidates', () => {
it('pages GitHub release metadata and stops at the first signed catalog', async () => {
const fixture = await createFixture('github')
const pages: number[] = []
const desktopReleases = Array.from({ length: 10 }, () => ({
tag_name: 'v9.0.0',
body: 'release notes'.repeat(1500),
assets: []
}))
expect(JSON.stringify(Array(10).fill(desktopReleases)).length)
.toBeGreaterThan(1024 * 1024)
const transport = vi.fn<typeof fetch>(async (input) => {
const url = new URL(input.toString())
if (url.hostname === 'api.github.com') {
expect(url.searchParams.get('per_page')).toBe('10')
const page = Number(url.searchParams.get('page'))
pages.push(page)
return response(JSON.stringify(
page === 1 ? desktopReleases : fixture.releases
))
}
return response(url.pathname.endsWith('.sig')
? fixture.signature : fixture.catalogBytes)
})
const snapshot = await fixture.manager(transport).getSnapshot()
expect(snapshot.catalog.state).toBe('available')
expect(snapshot.entries[0]?.latestVersion).toBe('2.0.0')
expect(pages).toEqual([1, 2])
})
it.each([0, 10])('bounds GitHub discovery when each page contains %i releases', async (count) => {
const fixture = await createFixture('github')
const transport = vi.fn<typeof fetch>(async () =>
response(JSON.stringify(Array.from({ length: count }, () => ({
tag_name: 'v9.0.0'
}))))
)
const snapshot = await fixture.manager(transport).getSnapshot()
expect(snapshot.catalog.state).toBe('unavailable')
expect(transport).toHaveBeenCalledTimes(count === 0 ? 1 : 10)
})
it('selects Linux and Darwin arm64 independently from one catalog', async () => {
await createFixture('mirror', (catalog) => {
for (const platform of ['linux', 'darwin'] as const) {
+21 -9
View File
@@ -61,7 +61,7 @@ const CATALOG_SIGNATURE_DOMAIN = Buffer.from(
const MIRROR_ROOT =
'https://goodbuddy.oss-cn-beijing.aliyuncs.com/agent-releases/'
const GITHUB_RELEASES_API =
'https://api.github.com/repos/mesalogo/goodbuddy/releases?per_page=100'
'https://api.github.com/repos/mesalogo/goodbuddy/releases?per_page=10'
const GITHUB_RELEASE_ROOT =
'https://github.com/mesalogo/goodbuddy/releases/download/'
const MAXIMUM_CATALOG_BYTES = 1024 * 1024
@@ -1449,17 +1449,29 @@ export class AgentPackageManager {
])
}
async *#githubReleases(signal?: AbortSignal): AsyncGenerator<unknown> {
// Keep the existing 100-release search window without downloading all
// historical release bodies and asset metadata in one response.
for (let page = 1; page <= 10; page += 1) {
signal?.throwIfAborted()
const releases = await this.#fetchJson(
`${GITHUB_RELEASES_API}&page=${page}`,
signal
)
if (!Array.isArray(releases)) {
throw new Error('GitHub Agent 发布目录无效')
}
yield* releases.slice(0, 10)
if (releases.length < 10) {
return
}
}
}
async #fetchGithubCatalog(
signal?: AbortSignal
): Promise<[Buffer, Buffer]> {
const releases = await this.#fetchJson(
GITHUB_RELEASES_API,
signal
)
if (!Array.isArray(releases)) {
throw new Error('GitHub Agent 发布目录无效')
}
for (const untrusted of releases.slice(0, 100)) {
for await (const untrusted of this.#githubReleases(signal)) {
if (
typeof untrusted !== 'object' ||
untrusted === null ||