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 下载兼容性并在设置中显示更新状态;新安装默认连接本地模型,保留显式用户配置;魔法笔记待办数字不再改变侧栏菜单高度。
189 lines
4.3 KiB
TypeScript
189 lines
4.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
agentBundleManifestSchema,
|
|
agentReleaseKeyRegistrySchema,
|
|
agentRuntimeLockSchema
|
|
} from './agent-installation-contracts'
|
|
|
|
const digest = 'a'.repeat(64)
|
|
|
|
function manifest() {
|
|
return {
|
|
formatVersion: 1,
|
|
product: 'GoodBuddy',
|
|
agentVersion: '0.11.0',
|
|
platform: 'linux',
|
|
arch: 'x64',
|
|
protocol: { major: 1, minor: 0 },
|
|
signingKeyId: 'test-key',
|
|
entrypoint: {
|
|
path: 'goodbuddy-agent',
|
|
runtimePath: 'node',
|
|
scriptPath: 'lib/agent.cjs'
|
|
},
|
|
files: [
|
|
{
|
|
path: 'goodbuddy-agent',
|
|
size: 1,
|
|
sha256: digest,
|
|
mode: '0755'
|
|
},
|
|
{
|
|
path: 'node',
|
|
size: 1,
|
|
sha256: digest,
|
|
mode: '0755'
|
|
},
|
|
{
|
|
path: 'lib/agent.cjs',
|
|
size: 1,
|
|
sha256: digest,
|
|
mode: '0644'
|
|
},
|
|
{
|
|
path: 'licenses/license.txt',
|
|
size: 1,
|
|
sha256: digest,
|
|
mode: '0644'
|
|
}
|
|
],
|
|
licenses: [
|
|
{
|
|
package: 'fixture',
|
|
version: '1.0.0',
|
|
spdx: 'MIT',
|
|
path: 'licenses/license.txt'
|
|
}
|
|
]
|
|
} as const
|
|
}
|
|
|
|
describe('Agent installation contracts', () => {
|
|
it('accepts a complete strict manifest', () => {
|
|
expect(agentBundleManifestSchema.parse(manifest())).toMatchObject({
|
|
arch: 'x64'
|
|
})
|
|
expect(() =>
|
|
agentBundleManifestSchema.parse({
|
|
...manifest(),
|
|
agentVersion: 'release_10'
|
|
})
|
|
).toThrow()
|
|
})
|
|
|
|
it('rejects unsafe, duplicate, undeclared, and incorrectly-modeled files', () => {
|
|
expect(() =>
|
|
agentBundleManifestSchema.parse({
|
|
...manifest(),
|
|
files: [
|
|
...manifest().files,
|
|
{
|
|
path: '../outside',
|
|
size: 1,
|
|
sha256: digest,
|
|
mode: '0644'
|
|
}
|
|
]
|
|
})
|
|
).toThrow()
|
|
expect(() =>
|
|
agentBundleManifestSchema.parse({
|
|
...manifest(),
|
|
files: [...manifest().files, manifest().files[0]]
|
|
})
|
|
).toThrow()
|
|
expect(() =>
|
|
agentBundleManifestSchema.parse({
|
|
...manifest(),
|
|
licenses: [
|
|
{
|
|
package: 'fixture',
|
|
version: '1.0.0',
|
|
spdx: 'MIT',
|
|
path: 'licenses/missing.txt'
|
|
}
|
|
]
|
|
})
|
|
).toThrow()
|
|
expect(() =>
|
|
agentBundleManifestSchema.parse({
|
|
...manifest(),
|
|
files: manifest().files.map((file) =>
|
|
file.path === 'goodbuddy-agent'
|
|
? { ...file, mode: '0644' }
|
|
: file
|
|
)
|
|
})
|
|
).toThrow()
|
|
})
|
|
|
|
it('rejects duplicate keys and extra registry fields', () => {
|
|
const key = {
|
|
keyId: 'test-key',
|
|
publicKeySpkiBase64: 'AAAA',
|
|
environment: 'test'
|
|
}
|
|
expect(() =>
|
|
agentReleaseKeyRegistrySchema.parse({
|
|
formatVersion: 1,
|
|
keys: [key, key],
|
|
revocations: [],
|
|
unknown: true
|
|
})
|
|
).toThrow()
|
|
})
|
|
|
|
it('rejects Base64 encodings with non-zero padding bits', () => {
|
|
expect(() =>
|
|
agentReleaseKeyRegistrySchema.parse({
|
|
formatVersion: 1,
|
|
keys: [{
|
|
keyId: 'test-key',
|
|
publicKeySpkiBase64: 'ZE==',
|
|
environment: 'test'
|
|
}],
|
|
revocations: []
|
|
})
|
|
).toThrow('canonical Base64')
|
|
})
|
|
|
|
it('requires both locked Linux targets and the fixed Node patch', () => {
|
|
const target = {
|
|
archive: 'node.tar.gz',
|
|
sha256: digest,
|
|
binaryPath: 'node/bin/node',
|
|
licensePath: 'node/LICENSE'
|
|
}
|
|
expect(
|
|
agentRuntimeLockSchema.parse({
|
|
formatVersion: 1,
|
|
agentVersion: '0.11.0',
|
|
protocol: { major: 1, minor: 0 },
|
|
node: {
|
|
version: '24.19.0',
|
|
source: 'https://nodejs.org/dist/v24.19.0/',
|
|
targets: {
|
|
'linux-x64': target,
|
|
'linux-arm64': target
|
|
}
|
|
},
|
|
koffi: { version: '3.1.4' }
|
|
}).koffi.version
|
|
).toBe('3.1.4')
|
|
expect(() =>
|
|
agentRuntimeLockSchema.parse({
|
|
formatVersion: 1,
|
|
agentVersion: '0.11.0',
|
|
protocol: { major: 1, minor: 0 },
|
|
node: {
|
|
version: '24.18.0',
|
|
source: 'http://nodejs.org/',
|
|
targets: { 'linux-x64': target }
|
|
},
|
|
koffi: { version: '3.1.3' }
|
|
})
|
|
).toThrow()
|
|
})
|
|
|
|
})
|