From 7754d3cf6eecf74379b0053c52feccdc10ff4c39 Mon Sep 17 00:00:00 2001 From: KiriAky 107 Date: Tue, 8 Sep 2026 21:55:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(community):=20=E9=80=9A=E8=BF=87=20Host=20?= =?UTF-8?q?=E5=AE=A1=E9=98=85=E7=A1=AE=E8=AE=A4=E6=A1=8C=E9=9D=A2=E6=9D=A5?= =?UTF-8?q?=E6=BA=90=E4=BF=A1=E4=BB=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/contracts/community.ts | 2 +- .../features/community/CommunityView.spec.ts | 26 ++++++++++ .../src/features/community/CommunityView.vue | 47 ++++++++++++++----- .../src/services/communityService.spec.ts | 2 +- frontend/src/services/communityService.ts | 9 ++-- .../services/extensionTrustService.spec.ts | 21 +++++++++ .../src/services/extensionTrustService.ts | 30 ++++++++++++ 7 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 frontend/src/features/community/CommunityView.spec.ts create mode 100644 frontend/src/services/extensionTrustService.spec.ts create mode 100644 frontend/src/services/extensionTrustService.ts diff --git a/frontend/src/contracts/community.ts b/frontend/src/contracts/community.ts index 74aec5f..852018a 100644 --- a/frontend/src/contracts/community.ts +++ b/frontend/src/contracts/community.ts @@ -1,7 +1,7 @@ /** 社区协议 v1;签名元数据与安装运行状态分离。 */ export type PackageKind = 'theme' | 'skill' | 'plugin' | 'mcp' | 'persona' | 'template' | 'model' export interface CommunityKey { key_id: string; namespace: string; public_key: string; revoked: boolean } -export interface CommunitySource { id: string; url: string; enabled: boolean; keys: CommunityKey[]; fetchedAt?: string } +export interface CommunitySource { source_id?: string; id: string; url: string; enabled: boolean; keys: CommunityKey[]; fetchedAt?: string } export interface CommunityRelease { schema_version: 1; namespace: string; package_id: string; type: PackageKind; version: string name: string; author_id: string; license: string; description: string; sha256: string; size: number diff --git a/frontend/src/features/community/CommunityView.spec.ts b/frontend/src/features/community/CommunityView.spec.ts new file mode 100644 index 0000000..b6bbbf4 --- /dev/null +++ b/frontend/src/features/community/CommunityView.spec.ts @@ -0,0 +1,26 @@ +// @vitest-environment jsdom +import { flushPromises, mount } from '@vue/test-utils' +import { beforeEach, expect, it, vi } from 'vitest' +const service = vi.hoisted(() => ({ discover: vi.fn(), review: vi.fn(), confirm: vi.fn(), save: vi.fn() })) +vi.mock('@/services/platform/desktop', () => ({ isDesktop: () => true })) +vi.mock('@/services/extensionTrustService', () => ({ reviewTrust: service.review, confirmTrust: service.confirm })) +vi.mock('@/services/communityService', () => ({ loadSources: () => [], saveSources: service.save, discoverSource: service.discover, cachedCatalog: vi.fn(), fetchCatalog: vi.fn(), installRelease: vi.fn() })) +import CommunityView from './CommunityView.vue' +beforeEach(() => { vi.clearAllMocks(); localStorage.clear() }) +it('requires explicit confirmation and does not save when Host rejects it', async () => { + service.discover.mockResolvedValue({ source_id: 'catalog', keys: [{ key_id: 'key', namespace: 'examples', public_key: 'public', revoked: false }] }) + service.review.mockResolvedValue([{ review_id: 'review', fingerprint: 'visible-digest', previous: null, proposed: { namespace: 'examples', key_id: 'key' } }]) + const wrapper = mount(CommunityView, { global: { stubs: { AppDialog: { template: '
' } } } }) + await wrapper.get('input').setValue('https://catalog.example') + await wrapper.findAll('button').find(b => b.text() === '检查来源与公钥')!.trigger('click') + await flushPromises() + expect(wrapper.text()).toContain('visible-digest') + expect(service.confirm).not.toHaveBeenCalled() + expect(service.save).not.toHaveBeenCalled() + service.confirm.mockRejectedValueOnce(new Error('确认已过期')) + await wrapper.findAll('button').find(b => b.text() === '确认来源设置')!.trigger('click') + await flushPromises() + expect(wrapper.text()).toContain('确认已过期') + expect(service.save).not.toHaveBeenCalled() + wrapper.unmount() +}) diff --git a/frontend/src/features/community/CommunityView.vue b/frontend/src/features/community/CommunityView.vue index 5f40fd0..25aca7c 100644 --- a/frontend/src/features/community/CommunityView.vue +++ b/frontend/src/features/community/CommunityView.vue @@ -1,7 +1,9 @@ @@ -109,10 +125,17 @@ function toggleSource() {

已保存的声明式候选

这些候选尚未应用到人设、MCP 或模型运行配置。

{{ item.key.replace('community-candidate:', '') }}
{{ item.value }}
- -

{{ candidateUrl }}

请与来源维护者公布的公钥核对。确认后固定这些公钥;密钥改变时不会自动信任。

+ +

{{ error }}

{{ candidateUrl }}

请与来源维护者公布的公钥核对。确认后固定这些公钥;密钥改变时不会自动信任。

+

来源标识:{{ candidateSourceId }} · {{ candidateEnabled ? '启用' : '停用' }}

{{ JSON.stringify(candidateKeys, null, 2) }}
- +
+

{{ review.proposed.namespace }} / {{ review.proposed.key_id }}:{{ review.previous ? '更新已有信任设置' : '首次确认' }}

+
原有公钥与状态
{{ JSON.stringify(review.previous, null, 2) }}
+

确认摘要:{{ review.fingerprint }}

+
+

确认在两分钟内有效。过期或设置已改变时,请关闭对话框并重新检查来源。

+