Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions docs/specs/provider-deeplink-import/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Provider Deeplink Import 实施计划

## 1. 当前实现基线

### 1.1 Deeplink 现状

1. `src/main/presenter/deeplinkPresenter/index.ts` 已支持 `deepchat://start` 和 `deepchat://mcp/install`。
2. 设置窗口已经支持通过 `SETTINGS_EVENTS.NAVIGATE` 进行页面跳转。
3. 设置 App 已有 MCP deeplink 的初始化处理,可复用设置窗口 ready 后接收事件的模式。

### 1.2 Provider 设置页现状

1. Provider 列表与配置由 `providerStore` 驱动。
2. Provider 详情页可基于路由参数 `providerId` 切换目标 provider。
3. 自定义 provider 已有手动新增流程,可复用新增后的选中逻辑。

## 2. 设计决策

### 2.1 Payload 与共享类型

新增共享模块 `src/shared/providerDeeplink.ts`:

1. 常量:
- `PROVIDER_INSTALL_ROUTE`
- `PROVIDER_INSTALL_VERSION`
2. 类型:
- `ProviderInstallDeeplinkPayload`
- `ProviderInstallPreview`
3. 工具函数:
- `maskApiKey`
- custom type 校验

### 2.2 主进程事件流

入口:`deepchat://provider/install?v=1&data=...`

处理顺序:

1. `DeeplinkPresenter.handleDeepLink` 识别 `provider/install`
2. Base64 解码 + JSON 解析 + 字段校验
3. built-in:
- 校验 `id`
- 拒绝 `acp`
4. custom:
- 校验 `name/type`
- 校验 `type` 在允许列表中
- 拒绝 `acp`
5. 创建/聚焦设置窗口
6. 发送:
- `SETTINGS_EVENTS.NAVIGATE -> settings-provider`
- `SETTINGS_EVENTS.PROVIDER_INSTALL -> preview`

错误策略:

1. 解析失败或 payload 不合法时,发 `NOTIFICATION_EVENTS.SHOW_ERROR`
2. 失败时不写任何 provider 配置

### 2.3 渲染进程事件流

`src/renderer/settings/App.vue`:

1. 监听 `SETTINGS_EVENTS.PROVIDER_INSTALL`
2. 确保 provider store 已初始化
3. built-in 导入时切到 `settings-provider/:providerId`
4. custom 导入时切到 `settings-provider`
5. 把 preview 放入新的 pending import store

`src/renderer/src/stores/providerDeeplinkImport.ts`:

1. 只维护当前 pending preview
2. 对话框开关由 preview 是否存在推导

### 2.4 对话框与落库行为

`ProviderDeeplinkImportDialog` 只负责展示解析结果,不自行写配置。

展示规则:

1. built-in:`icon + id`
2. custom:`icon + name`,并额外显示 `type`
3. 两类都展示 `baseUrl`
4. 两类都展示脱敏 `apiKey`
5. built-in 额外显示覆盖 warning

确认逻辑放在 `ModelProviderSettings.vue`:

1. built-in:
- 更新 `baseUrl/apiKey`
- 若未启用则自动启用
- 刷新该 provider 模型
- 切换到对应 provider 页面
2. custom:
- 生成新 `id`
- 创建 `custom: true` provider
- 默认 `enable: true`
- 刷新新 provider 模型
- 切换到新 provider 页面
3. cancel:
- 清空 pending preview
- 不写配置

### 2.5 Provider 兼容策略

1. built-in provider 以 `id` 作为唯一匹配键,因此导入是覆盖语义。
2. custom provider 以 `type/apiType` 校验,但确认后总是新增实例,因此是追加语义。
3. `vertex`、`aws-bedrock`、`github-copilot` 等允许部分导入,即使后续仍需补专属字段,也不阻塞 `baseUrl/apiKey` 导入。
4. `acp` 独立于本流程,不进入 `settings-provider` 导入链路。

## 3. Manual Playground

新增:

- `test/manual/deeplink-playground.html`

页面结构:

1. `start`
2. `mcp/install`
3. `provider/install`
4. `provider/install builder`

规则:

1. built-in 列出当前所有默认 provider `id`,排除 `acp`
2. custom 列出当前所有允许导入的 `apiType`,排除 `acp`
3. 每项展示:
- label
- raw JSON
- deeplink
- `Open`
- `Copy`
4. 示例数据全部使用假地址和假 key

## 4. 测试策略

### 4.1 Main

1. built-in payload 成功时:
- 打开设置窗
- 发送 `NAVIGATE`
- 发送 `PROVIDER_INSTALL`
2. custom payload 成功时:
- 发送 custom preview
3. 非法 payload:
- 不发送导入事件
- 发送错误通知

### 4.2 Renderer

1. `App.vue` 收到 `PROVIDER_INSTALL` 后正确导航并写入 preview store
2. `ModelProviderSettings.vue`:
- built-in confirm 覆盖并启用 provider
- custom confirm 新增并选中新 provider
- cancel 不写配置
3. `ProviderDeeplinkImportDialog.vue` 正确展示 built-in/custom 解析结果

### 4.3 Manual

1. playground 中三类 deeplink 都能生成合法协议链接
2. built-in/custom 列表覆盖范围正确
3. builder 输出格式与应用解析格式一致

## 5. 风险与缓解

1. 风险:设置窗口创建后事件发送早于页面监听注册。
缓解:复用现有 settings 事件通道,并在 App 侧做独立导航兜底。

2. 风险:部分 provider 启用后仍缺专属字段,模型刷新可能失败。
缓解:允许部分导入;模型刷新失败只记录日志,不回滚导入。

3. 风险:手工验证页 provider 列表与真实支持集合漂移。
缓解:built-in 与 custom 列表以当前代码中的 provider 集合为准,变更时同步更新此页。

## 6. 质量门槛

1. `pnpm run format`
2. `pnpm run i18n`
3. `pnpm run lint`
4. `pnpm run typecheck`
5. 关键 main/renderer 测试通过
136 changes: 136 additions & 0 deletions docs/specs/provider-deeplink-import/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Provider Deeplink Import 规格

## 概述

新增 provider 导入 deeplink:

- `deepchat://provider/install?v=1&data=<base64(JSON)>`

其中 `data` 只接受两种结构,且 `id` 与 `type` 必须二选一:

1. `{ id, baseUrl, apiKey }`
2. `{ name, type, baseUrl, apiKey }`

导入后统一进入 Provider Settings,先展示确认对话框;用户确认后才写入配置,取消则直接丢弃。

## 背景与动机

1. 用户经常需要在多个 built-in provider 与 custom provider 之间切换配置。
2. 当前 provider 配置主要依赖手动录入,分享和一键导入成本高。
3. DeepLink 已经用于 `start` 和 `mcp/install`,provider 导入应沿用同一套唤起能力。
4. 需要一个独立的手工验证页,降低联调和回归验证成本。

## 用户故事

### US-1:一键导入内置 Provider

作为用户,我希望点击一个 deeplink 后直接进入对应 provider 设置,并在确认后覆盖它的 `baseUrl` 与 `apiKey`。

### US-2:一键新增 Custom Provider

作为用户,我希望通过 deeplink 快速新增一个 custom provider,而不是手动新建并逐项填写。

### US-3:导入前确认

作为用户,我希望在真正写入前看到解析结果,避免误覆盖现有配置。

### US-4:手工验证入口

作为开发者或测试者,我希望仓库里有一个静态网页,能集中打开所有支持的 deeplink。

## 功能需求

### A. Provider Deeplink 协议

- [ ] 新增 `deepchat://provider/install?v=1&data=<base64(JSON)>`
- [ ] `v=1` 是当前唯一支持版本
- [ ] `data` Base64 解码后必须是 JSON object
- [ ] payload 只允许两种结构:
- [ ] `{ id, baseUrl, apiKey }`
- [ ] `{ name, type, baseUrl, apiKey }`
- [ ] `id` 与 `type` 同时存在或同时缺失时,必须拒绝

### B. 内置 Provider 导入

- [ ] 当 payload 包含 `id` 时,按内置 provider id 匹配
- [ ] `id='acp'` 必须拒绝
- [ ] unknown `id` 必须拒绝
- [ ] 确认后覆盖目标 provider 的 `baseUrl` 与 `apiKey`
- [ ] 若目标 provider 当前未启用,确认后自动启用
- [ ] 完成后停留在对应 provider 设置页
- [ ] 若是 `vertex`、`aws-bedrock`、`github-copilot` 等仍需额外字段的 provider,允许部分导入,不阻塞确认

### C. Custom Provider 导入

- [ ] 当 payload 包含 `type` 时,按 provider `apiType` 匹配
- [ ] `type='acp'` 必须拒绝
- [ ] unknown `type` 必须拒绝
- [ ] custom payload 必须包含 `name`
- [ ] 确认后总是新增一条 custom provider,不复用旧条目
- [ ] 新 provider 默认 `enable=true`
- [ ] 完成后停留在新 provider 设置页

### D. 设置页行为

- [ ] deeplink 唤起后自动进入 `settings-provider`
- [ ] 在真正写入前弹出 `Import Provider` 对话框
- [ ] built-in 对话框只展示:
- [ ] `id + icon`
- [ ] `baseUrl`
- [ ] 脱敏 `apiKey`
- [ ] custom 对话框只展示:
- [ ] `name + icon`
- [ ] `type`
- [ ] `baseUrl`
- [ ] 脱敏 `apiKey`
- [ ] built-in 导入需要展示“将覆盖当前配置”的提示
- [ ] 取消后不写入任何 provider 配置

### E. 错误处理

- [ ] 非法 Base64、非法 JSON、非法版本、缺字段、unknown `id/type` 均必须拒绝
- [ ] 非法 deeplink 需要有可见错误提示
- [ ] 拒绝场景不得写入 provider 配置

### F. Manual Playground

- [ ] 新增 `test/manual/deeplink-playground.html`
- [ ] 页面覆盖三类 deeplink:
- [ ] `start`
- [ ] `mcp/install`
- [ ] `provider/install`
- [ ] `provider/install` 区块必须列出:
- [ ] 所有 built-in provider `id`,排除 `acp`
- [ ] 所有允许的 custom `apiType`,排除 `acp`
- [ ] 每项都提供 `Open` 和 `Copy`
- [ ] 每项都展示原始 JSON 与最终 deeplink
- [ ] 页面提供一个可编辑 builder,用于临时生成 deeplink
- [ ] 所有示例数据必须为 fake data

## 验收标准

- [ ] 打开 built-in provider deeplink 时,设置窗进入对应 provider,并弹出确认对话框
- [ ] 确认 built-in provider 导入后,`baseUrl/apiKey` 被覆盖,provider 被自动启用
- [ ] 打开 custom provider deeplink 时,设置窗进入 provider 设置页,并弹出确认对话框
- [ ] 确认 custom provider 导入后,会新增一条启用中的 custom provider
- [ ] 取消导入时,不产生任何配置写入
- [ ] 非法 payload 只显示错误,不进入确认流程
- [ ] 手工验证页可直接生成并打开三类 deeplink

## 非目标

1. 不扩展 provider deeplink 的版本协商机制,本次仅支持 `v=1`。
2. 不新增 provider 专属迁移脚本或持久化 schema。
3. 不为 `acp` provider 引入导入能力。
4. 不修改现有 `start` 与 `mcp/install` 的协议格式。

## 约束

1. 保持现有 Presenter + EventBus 架构。
2. 所有用户可见文案必须走 i18n。
3. 不破坏现有 provider 配置存储结构。
4. Manual playground 不打包进应用,仅作为仓库内测试辅助页。

## 开放问题

无。
59 changes: 59 additions & 0 deletions docs/specs/provider-deeplink-import/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Provider Deeplink Import Tasks

## T0 规格与设计

- [x] 完成 `spec.md`
- [x] 完成 `plan.md`
- [x] 完成 `tasks.md`

## T1 共享协议与事件

- [x] 新增 provider deeplink 共享类型与协议常量
- [x] 新增 `SETTINGS_EVENTS.PROVIDER_INSTALL`
- [x] 补共享类型导出

## T2 主进程解析与分发

- [x] 在 `deeplinkPresenter` 新增 `provider/install` 入口
- [x] 校验 `v=1`
- [x] 校验 Base64 / JSON / 字段结构
- [x] built-in 导入按 `id` 匹配
- [x] custom 导入按 `type` 校验
- [x] 拒绝 `acp`
- [x] 发送设置页导航与 preview 事件
- [x] 非法 payload 显示错误通知

## T3 设置页预览与确认

- [x] 新增 pending import store
- [x] `App.vue` 监听 `PROVIDER_INSTALL`
- [x] built-in 导航到目标 provider
- [x] 新增 `ProviderDeeplinkImportDialog`
- [x] built-in confirm 覆盖 `baseUrl/apiKey` 并自动启用
- [x] custom confirm 新增启用中的 custom provider
- [x] cancel 只清空 pending preview

## T4 i18n 与测试

- [x] 补齐 provider import 对话框文案
- [x] 新增 main deeplink 测试
- [x] 新增 settings app 事件处理测试
- [x] 新增 provider settings confirm 测试

## T5 Manual Playground

- [x] 新增 `test/manual/deeplink-playground.html`
- [x] 覆盖 `start`
- [x] 覆盖 `mcp/install`
- [x] 覆盖 built-in provider import,排除 `acp`
- [x] 覆盖 custom provider import,排除 `acp`
- [x] 提供 builder、Open、Copy、raw JSON、deeplink 展示
- [x] 更新 `test/README.md`

## T6 质量检查

- [ ] `pnpm run format`
- [ ] `pnpm run i18n`
- [ ] `pnpm run lint`
- [ ] `pnpm run typecheck`
- [ ] 运行相关测试并记录结果
3 changes: 2 additions & 1 deletion src/main/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ export const WINDOW_EVENTS = {
export const SETTINGS_EVENTS = {
READY: 'settings:ready',
NAVIGATE: 'settings:navigate',
CHECK_FOR_UPDATES: 'settings:check-for-updates'
CHECK_FOR_UPDATES: 'settings:check-for-updates',
PROVIDER_INSTALL: 'settings:provider-install'
}

// ollama 相关事件
Expand Down
Loading
Loading