fix: make Agent updates reliable and visible
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build Linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build Linux x64 Agent (push) Canceled after 0s
Valid Agent packages could be rejected when packaged trusted-key JSON used CRLF, and Settings could not show whether a newer compatible Agent existed. Registry formatting is now parsed independently of cryptographic trust, and Settings reads only the signed catalog to report local and latest versions. New installations use a credential-free local Ollama-compatible profile. The v20 migration changes only untouched historical defaults, while explicit profiles and legacy environment aliases remain compatible. Magic Notes count badges no longer shift navigation rows. Release note: 修复 Agent 下载兼容性并在设置中显示更新状态;新安装默认连接本地模型,保留显式用户配置;魔法笔记待办数字不再改变侧栏菜单高度。
This commit is contained in:
+124
-1
@@ -472,6 +472,74 @@ describe('compound Agent packages', () => {
|
||||
).rejects.toThrow('does not match its Agent bundle')
|
||||
})
|
||||
|
||||
it('accepts a signed package whose key registry uses CRLF', async () => {
|
||||
const files = unzipSync(
|
||||
new Uint8Array(readFileSync(packagePath))
|
||||
)
|
||||
const registryBytes = Buffer.from(
|
||||
Buffer.from(files['agent-release-keys.json']!)
|
||||
.toString('utf8')
|
||||
.replace(/\n/gu, '\r\n')
|
||||
)
|
||||
files['agent-release-keys.json'] =
|
||||
new Uint8Array(registryBytes)
|
||||
const descriptor = agentPackageDescriptorSchema.parse(
|
||||
JSON.parse(
|
||||
Buffer.from(files['agent-package.json']!).toString('utf8')
|
||||
)
|
||||
)
|
||||
const updatedDescriptor = {
|
||||
...descriptor,
|
||||
files: descriptor.files.map((file) =>
|
||||
file.path === 'agent-release-keys.json'
|
||||
? {
|
||||
...file,
|
||||
size: registryBytes.byteLength,
|
||||
sha256: createHash('sha256')
|
||||
.update(registryBytes)
|
||||
.digest('hex')
|
||||
}
|
||||
: file
|
||||
)
|
||||
}
|
||||
updatedDescriptor.contentDigest =
|
||||
agentPackage.descriptorContentDigest(updatedDescriptor)
|
||||
const descriptorBytes =
|
||||
agentPackage.descriptorBytes(updatedDescriptor)
|
||||
files['agent-package.json'] = new Uint8Array(descriptorBytes)
|
||||
files['agent-package.sig'] = new Uint8Array(
|
||||
Buffer.from(
|
||||
`${sign(
|
||||
null,
|
||||
Buffer.concat([
|
||||
agentPackage.signatureDomain,
|
||||
descriptorBytes
|
||||
]),
|
||||
privateKey
|
||||
).toString('base64')}\n`
|
||||
)
|
||||
)
|
||||
const crlfPath = join(
|
||||
temporaryRoot,
|
||||
'crlf-registry.gbagent'
|
||||
)
|
||||
writeFileSync(crlfPath, Buffer.from(zipSync(files)))
|
||||
|
||||
await expect(
|
||||
extractAndVerifyAgentPackage({
|
||||
archivePath: crlfPath,
|
||||
destinationDirectory: join(
|
||||
temporaryRoot,
|
||||
'crlf-registry-output'
|
||||
),
|
||||
desktopVersion: '0.11.0',
|
||||
trustedRegistry: registry
|
||||
})
|
||||
).resolves.toMatchObject({
|
||||
descriptor: { architecture: 'x64' }
|
||||
})
|
||||
})
|
||||
|
||||
it('orders SemVer prereleases and selects the latest compatible protocol', () => {
|
||||
expect(compareSemanticVersions('1.0.0-e2e.10', '1.0.0-e2e.9'))
|
||||
.toBeGreaterThan(0)
|
||||
@@ -715,7 +783,7 @@ describe('compound Agent packages', () => {
|
||||
})
|
||||
|
||||
it.each(['github', 'mirror'] as const)(
|
||||
'follows the selected %s source only after a manual download',
|
||||
'checks the selected %s catalog without downloading until requested',
|
||||
async (source) => {
|
||||
const root = join(temporaryRoot, `online-${source}`)
|
||||
mkdirSync(root)
|
||||
@@ -723,6 +791,7 @@ describe('compound Agent packages', () => {
|
||||
writeFileSync(
|
||||
keyRegistryPath,
|
||||
`${JSON.stringify(registry, null, 2)}\n`
|
||||
.replace(/\n/gu, '\r\n')
|
||||
)
|
||||
const catalog = signedCatalog()
|
||||
const requested: string[] = []
|
||||
@@ -774,6 +843,42 @@ describe('compound Agent packages', () => {
|
||||
|
||||
await manager.getInventory()
|
||||
expect(requested).toEqual([])
|
||||
const snapshot = await manager.getSnapshot()
|
||||
expect(snapshot.catalog).toMatchObject({
|
||||
state: 'available',
|
||||
error: null
|
||||
})
|
||||
expect(
|
||||
snapshot.entries.find(
|
||||
(entry) => entry.architecture === 'x64'
|
||||
)
|
||||
).toMatchObject({
|
||||
state: 'not-downloaded',
|
||||
latestVersion: agentLock.agentVersion,
|
||||
updateAvailable: false
|
||||
})
|
||||
expect(requested).toContain(
|
||||
source === 'github'
|
||||
? 'https://api.github.com/repos/mesalogo/goodbuddy/releases?per_page=100'
|
||||
: 'https://goodbuddy.oss-cn-beijing.aliyuncs.com/agent-releases/latest.json'
|
||||
)
|
||||
expect(requested).not.toContain(
|
||||
source === 'github'
|
||||
? packageEntry().downloads.github.url
|
||||
: packageEntry().downloads.mirror.url
|
||||
)
|
||||
await manager.download('x64')
|
||||
const installedSnapshot = await manager.getInventory()
|
||||
expect(
|
||||
installedSnapshot.entries.find(
|
||||
(entry) => entry.architecture === 'x64'
|
||||
)
|
||||
).toMatchObject({
|
||||
state: 'verified',
|
||||
version: agentLock.agentVersion,
|
||||
latestVersion: agentLock.agentVersion,
|
||||
updateAvailable: false
|
||||
})
|
||||
await manager.download('x64')
|
||||
expect(requested).toContain(
|
||||
source === 'github'
|
||||
@@ -785,6 +890,16 @@ describe('compound Agent packages', () => {
|
||||
? packageEntry().downloads.github.url
|
||||
: packageEntry().downloads.mirror.url
|
||||
)
|
||||
expect(
|
||||
requested.filter(
|
||||
(url) =>
|
||||
url === (
|
||||
source === 'github'
|
||||
? packageEntry().downloads.github.url
|
||||
: packageEntry().downloads.mirror.url
|
||||
)
|
||||
)
|
||||
).toHaveLength(1)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -838,6 +953,14 @@ describe('compound Agent packages', () => {
|
||||
fetch: transport as typeof fetch
|
||||
})
|
||||
|
||||
await expect(manager.getSnapshot()).resolves.toMatchObject({
|
||||
catalog: {
|
||||
state: 'unavailable',
|
||||
error: 'Agent 发布目录签名校验失败'
|
||||
},
|
||||
entries: expect.any(Array)
|
||||
})
|
||||
expect(packageRequested).toBe(false)
|
||||
await expect(manager.download('x64')).rejects.toThrow(
|
||||
'Agent 发布目录签名校验失败'
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user