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:
@@ -133,6 +133,20 @@ describe('Agent installation contracts', () => {
|
||||
).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',
|
||||
|
||||
@@ -6,6 +6,19 @@ const sha256Pattern = /^[a-f0-9]{64}$/u
|
||||
const keyIdPattern = /^[a-z0-9][a-z0-9._-]{0,63}$/u
|
||||
const canonicalBase64Pattern =
|
||||
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u
|
||||
const canonicalDoublePaddingCharacterPattern = /[AQgw]==$/u
|
||||
const canonicalSinglePaddingCharacterPattern =
|
||||
/[AEIMQUYcgkosw048]=$/u
|
||||
|
||||
function hasCanonicalBase64Padding(value: string): boolean {
|
||||
if (value.endsWith('==')) {
|
||||
return canonicalDoublePaddingCharacterPattern.test(value)
|
||||
}
|
||||
if (value.endsWith('=')) {
|
||||
return canonicalSinglePaddingCharacterPattern.test(value)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export const agentArchitectureSchema = z.enum(['x64', 'arm64'])
|
||||
|
||||
@@ -151,7 +164,11 @@ export const agentReleaseKeySchema = z
|
||||
.string()
|
||||
.min(4)
|
||||
.max(4_096)
|
||||
.regex(canonicalBase64Pattern),
|
||||
.regex(canonicalBase64Pattern)
|
||||
.refine(
|
||||
hasCanonicalBase64Padding,
|
||||
'Agent public key must use canonical Base64'
|
||||
),
|
||||
environment: z.enum(['production', 'test'])
|
||||
})
|
||||
.strict()
|
||||
|
||||
@@ -67,12 +67,19 @@ describe('Agent package contracts', () => {
|
||||
platform: 'linux',
|
||||
state: 'not-downloaded',
|
||||
version: null,
|
||||
latestVersion: null,
|
||||
updateAvailable: false,
|
||||
remoteRuntimeVersion: null,
|
||||
agentProtocol: null
|
||||
} as const
|
||||
expect(
|
||||
agentPackageInventorySchema.parse({
|
||||
checkedAt: '2026-08-25T00:00:00.000Z',
|
||||
catalog: {
|
||||
state: 'not-checked',
|
||||
checkedAt: null,
|
||||
error: null
|
||||
},
|
||||
entries: [
|
||||
{ ...entry, architecture: 'x64' },
|
||||
{ ...entry, architecture: 'arm64' }
|
||||
@@ -82,6 +89,11 @@ describe('Agent package contracts', () => {
|
||||
expect(() =>
|
||||
agentPackageInventorySchema.parse({
|
||||
checkedAt: '2026-08-25T00:00:00.000Z',
|
||||
catalog: {
|
||||
state: 'not-checked',
|
||||
checkedAt: null,
|
||||
error: null
|
||||
},
|
||||
entries: [
|
||||
{ ...entry, architecture: 'x64' },
|
||||
{ ...entry, architecture: 'x64' }
|
||||
|
||||
@@ -209,14 +209,42 @@ export const agentPackageInventoryEntrySchema = z
|
||||
architecture: agentArchitectureSchema,
|
||||
state: agentPackageStateSchema,
|
||||
version: semanticVersionSchema.nullable(),
|
||||
latestVersion: semanticVersionSchema.nullable(),
|
||||
updateAvailable: z.boolean(),
|
||||
remoteRuntimeVersion: semanticVersionSchema.nullable(),
|
||||
agentProtocol: agentProtocolVersionSchema.nullable()
|
||||
})
|
||||
.strict()
|
||||
|
||||
export const agentPackageCatalogStatusSchema = z
|
||||
.discriminatedUnion('state', [
|
||||
z
|
||||
.object({
|
||||
state: z.literal('not-checked'),
|
||||
checkedAt: z.null(),
|
||||
error: z.null()
|
||||
})
|
||||
.strict(),
|
||||
z
|
||||
.object({
|
||||
state: z.literal('available'),
|
||||
checkedAt: z.string().datetime(),
|
||||
error: z.null()
|
||||
})
|
||||
.strict(),
|
||||
z
|
||||
.object({
|
||||
state: z.literal('unavailable'),
|
||||
checkedAt: z.string().datetime(),
|
||||
error: z.string().trim().min(1).max(2_000)
|
||||
})
|
||||
.strict()
|
||||
])
|
||||
|
||||
export const agentPackageInventorySchema = z
|
||||
.object({
|
||||
checkedAt: z.string().datetime(),
|
||||
catalog: agentPackageCatalogStatusSchema,
|
||||
entries: z
|
||||
.array(agentPackageInventoryEntrySchema)
|
||||
.length(agentArchitectureSchema.options.length)
|
||||
|
||||
@@ -477,10 +477,10 @@ export const defaultContextCompressionSettings = {
|
||||
|
||||
export const defaultRuntimeSettings = {
|
||||
provider: 'model',
|
||||
modelBaseUrl: 'https://bigtoken.ai',
|
||||
modelName: 'sonnet-5',
|
||||
modelProtocol: 'anthropic-messages',
|
||||
modelAuthentication: 'api-key',
|
||||
modelBaseUrl: 'http://127.0.0.1:11434/v1',
|
||||
modelName: 'qwen3',
|
||||
modelProtocol: 'openai-chat-completions',
|
||||
modelAuthentication: 'none',
|
||||
supportsImageInput: false,
|
||||
imageGenerationQuality: 'auto',
|
||||
opencodeBaseUrl: '',
|
||||
|
||||
Reference in New Issue
Block a user