From 86f59d463eaac459614dbd07a671733ce31b7f0b 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 --- .../OpenNexus生产化实施进度-2026-09-08.md | 9 ++++ 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 ++++++++++++ 8 files changed, 129 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/docs/development/OpenNexus生产化实施进度-2026-09-08.md b/docs/development/OpenNexus生产化实施进度-2026-09-08.md index eba5b78..c805558 100644 --- a/docs/development/OpenNexus生产化实施进度-2026-09-08.md +++ b/docs/development/OpenNexus生产化实施进度-2026-09-08.md @@ -269,3 +269,12 @@ Core 的独立数据目录目前不等于已授权 Vault。Python 旧笔记写 - 安装预览由 Host 注入应用版本、OS 与架构,renderer 只提交根包、当前 Vault 和配置;后台阻塞任务中保持 Vault 绑定稳定并调用完整预览。尚未开放实际安装执行命令。 - 桌面二进制 5 项测试和全目标 Clippy -D warnings 通过,日志 `.build/extension-commands-tests.log`。新增确认凭据生命周期与并发冲突测试;ACL 文件及生成权限项已更新。 - 前端确认对话框、桌面包下载/暂存入口、安装执行编排、沙箱及真实 UI 验收尚待完成;extensions capability 仍关闭。本轮不宣称生产化或 D-04 完成。 + + +## 增量:社区来源确认界面接入 Host + +- 桌面社区检查来源后请求 Host 复核,对话框展示 source_id、候选公钥、原有设置和确认摘要;只有点击确认才调用 Host 确认命令,成功后更新本地目录设置。启用/停用也经同一确认流程更新 Host,旧本地来源不自动迁入信任库。 +- 新增 extensionTrustService,规范 HTTPS 来源、公钥解码和数量限制;确认只传 Host 签发的 review_id 与摘要。两分钟过期或并发冲突提示重新检查;多键确认是逐键调用,部分失败可能已有部分键完成确认,界面不声称整组成功,需重新检查后处理。 +- 来源发现保留服务端 source_id,Web 开发保留本地来源流程。对话框中的错误可见,忙碌时禁止重复确认。 +- 前端全量 99 文件 / 521 项通过,两套 TypeScript 项目检查通过;日志 `.build/extension-trust-ui-tests.log`。新增服务参数/非法输入测试与 Vue 组件确认前无写入、Host 拒绝后不保存测试。测试使用 Host mock,不等同于真实桌面 UI 端到端验收。 +- 桌面包下载暂存、安装确认 UI 与执行编排、沙箱、真实双端及部署验收仍未完成;整体生产化目标继续进行。 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 }}

+
+

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

+