ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Warp 云模式 Auth Secret 删除功能实现解析:从 Selector 芯片到服务端删除的完整链路

Warp 云模式 Auth Secret 删除功能实现解析:从 Selector 芯片到服务端删除的完整链路 桌面应用开发者工具人工智能AI 应用AI Agent代码智能体【免费下载链接】warpWarp is an agentic development environment, born out of the terminal.项目地址https://gitcode.com/GitHub_Trending/wa/warp点击查看免费下载导读本技术文章基于 specs/cloud-mode-auth-secret-deletion/TECH.md 与配套的 specs/cloud-mode-auth-secret-deletion/PRODUCT.md完整剖析 Warpagentic development environment中从 auth secret selector 芯片菜单直接删除 Warp 托管密钥这一功能的端到端实现。你将掌握该功能在数据模型AuthSecretEntry携带SecretOwner、共享模型HarnessAvailabilityModel删除方法、选择器 UI右侧X动作、pending 去重、确认对话框、toast 反馈与跨表面状态同步等各层的具体设计以及源码级的关键调用链与可验证的测试/检查手段。文章以当前仓库实际代码为准两份文档中描述的设计均已落地实现。一、功能背景为什么需要从 Selector 里直接删密钥在 Warp 的云模式cloud mode中非 Oz 的云端 harness如 Claude、Gemini、Codex 等会通过auth secret selector 芯片来管理各自的认证密钥。此前用户只能新建、选择或继承密钥若要删除某个密钥需要到别处操作。产品规格 PRODUCT.md 给出的目标很明确每个已存在的托管密钥行右侧显示一个X删除按钮点击X先弹出确认模态框确认后才真正删除删除以服务端响应为准只有服务端删除成功UI 才呈现已删除删除成功/失败均通过临时 toast 反馈删除当前已选中的密钥时芯片要回退到无密钥/继承状态且持久化的last_selected_auth_secret也要清理。需要特别强调的是该功能只做单密钥删除一次一个不包含编辑、重命名、新建或批量删除PRODUCT behavior 18。二、核心挑战删除必须携带正确的 Owner 信息技术规格 TECH.md 在 Context 中明确指出一个关键设计约束旧的AuthSecretEntry只保存name而服务端返回的ManagedSecret结果中包含owner。如果删除时总是假设SecretOwner::CurrentUser就无法正确处理团队team拥有的密钥——它需要以SecretOwner::Team { team_uid }调用删除接口。因此第一个结构性改动是把 owner 元数据直接并入缓存条目而不是在点击时去猜测 owner。三、数据模型AuthSecretEntry 携带 owner 元数据对应实现位于 app/src/ai/harness_availability.rs#[derive(Debug, Clone)] pub enum AuthSecretFetchState { NotFetched, Loading, Loaded(VecAuthSecretEntry), Failed(#[allow(dead_code)] String), } #[derive(Debug, Clone)] pub struct AuthSecretEntry { pub name: String, pub owner: SecretOwner, }而Space到SecretOwner的转换由 secret_owner_from_space 完成与 TECH.md 中设计的映射完全一致fn secret_owner_from_space(space: warp_graphql::object::Space) - SecretOwner { match space.type_ { warp_graphql::object::SpaceType::Team SecretOwner::Team { team_uid: space.uid.clone().into_inner(), }, warp_graphql::object::SpaceType::User SecretOwner::CurrentUser, } }SpaceType::User→SecretOwner::CurrentUserSpaceType::Team→SecretOwner::Team { team_uid: space.uid.into_inner() }该转换被两处复用拉取列表时fetch_auth_secrets 将每个ManagedSecret映射为AuthSecretEntry以及创建成功回写缓存时create_auth_secret成功分支。这样删除的 owner 信息始终与 fetch/create 的缓存保持一致避免了点击时推断 owner带来的误删风险。四、模型层HarnessAvailabilityModel 的删除方法与事件TECH.md 要求新增一个与create_auth_secret对称的删除方法实现位于 app/src/ai/harness_availability.rspub fn delete_auth_secretS: TeamScope ?Sized( mut self, team_scope: S, harness: Harness, name: String, owner: SecretOwner, ctx: mut ModelContextSelf, ) { let cache_key AuthSecretCacheKey::new(team_scope, harness); let request_team_scope RequestTeamScope::from_scope(team_scope); let manager ManagedSecretManager::handle(ctx); let delete_future manager .as_ref(ctx) .delete_secret(request_team_scope, owner.clone(), name.clone()); ctx.spawn(delete_future, move |me, result, ctx| match result { Ok(()) { me.remove_deleted_auth_secret_entries(cache_key, name, owner); ctx.emit(HarnessAvailabilityEvent::AuthSecretsChanged); ctx.emit(HarnessAvailabilityEvent::AuthSecretDeleted { harness, name, owner, }); } Err(e) { let msg e.to_string(); report_error!(e.context(Failed to delete harness auth secret)); ctx.emit(HarnessAvailabilityEvent::AuthSecretDeletionFailed { harness, name, owner, error: msg, }); } }); }该方法的语义与 TECH.md 设计一一对应成功调用remove_deleted_auth_secret_entriesapp/src/ai/harness_availability.rs仅移除匹配的(name, owner)条目然后发出AuthSecretsChanged与AuthSecretDeleted { harness, name, owner }失败缓存保持不变通过report_error!记录错误并发出AuthSecretDeletionFailed { harness, name, owner, error }。新增的两个事件定义在 app/src/ai/harness_availability.rsAuthSecretDeleted { harness: Harness, name: String, owner: SecretOwner }, AuthSecretDeletionFailed { harness: Harness, name: String, owner: SecretOwner, error: String },TECH.md 特别提示只关心菜单新鲜度的订阅者应在AuthSecretDeleted时刷新不关心的订阅者必须显式 ignore 这两个新事件以保证 match 穷尽性清晰。底层删除真正打到服务端的路径是 crates/managed_secrets/src/manager.rs 的ManagedSecretManager::delete_secret(request_scope, owner, name)其返回FutureOutput anyhow::Result()它经由 app/src/server/server_api/managed_secrets.rs 的delete_managed_secret接线到服务端 GraphQL mutation。这也解释了为何删除是否成功完全以服务端响应为准——本地缓存不会在请求发出前被改动。五、选择器层DeleteSecret 动作与 pending 去重5.1 动作枚举扩展TECH.md 要求扩展AuthSecretSelectorAction实现位于 app/src/terminal/view/ambient_agent/auth_secret_selector.rspub enum AuthSecretSelectorAction { ToggleMenu, SelectSecret(String), ClearSecret, OpenNewTypeSidecar, SelectNewType(usize), DeleteSecret { name: String, owner: SecretOwner }, }5.2 pending 删除去重为防止同一个菜单内对同一密钥重复发起删除选择器用一个小集合记录进行中的删除键为(harness, name, owner)PRODUCT behavior 9type PendingDeleteKey (Harness, String, SecretOwner); // 字段定义 pending_deletes: HashSetPendingDeleteKey,start_secret_deleteauth_secret_selector.rs通过HashSet::insert的返回值判断是否首次发起if !self.pending_deletes.insert((harness, name.clone(), owner.clone())) { return; // 已有一条同目标的删除在途忽略重复请求 } HarnessAvailabilityModel::handle(ctx).update(ctx, |model, ctx| { model.delete_auth_secret(team_scope.as_ref(), harness, name, owner, ctx); }); self.refresh_menu(ctx); // 立即重绘将 pending 行的 X 置为禁用pending 状态严格按删除目标(harness, name, owner)作用域隔离而不是同名即去重避免了误伤不同 owner 下的同名密钥。六、菜单渲染右对齐 X 与独立的右侧动作TECH.md 倾向的首选实现——扩展MenuItemFields提供可选的右侧动作——已经落地。菜单构建位于 auth_secret_selector.rsfor secret in secrets { let is_pending_delete pending_deletes.contains((harness, secret.name.clone(), secret.owner.clone())); let fields MenuItemFields::new(secret.name.clone()) .with_font_size_override(ITEM_FONT_SIZE) .with_padding_override(ITEM_VERTICAL_PADDING, MENU_HORIZONTAL_PADDING) .with_override_hover_background_color(hover_background) .with_on_select_action(AuthSecretSelectorAction::SelectSecret(secret.name.clone())) .with_right_side_icon(Icon::X) .with_right_side_icon_action(AuthSecretSelectorAction::DeleteSecret { name: secret.name.clone(), owner: secret.owner.clone(), }) .with_right_side_icon_a11y_label(format!(Delete API key {}, secret.name)) .with_right_side_icon_disabled(is_pending_delete); items.push(MenuItem::Item(fields)); }MenuItemFields的右侧动作 API 定义在 app/src/menu.rswith_right_side_icon_action、with_right_side_icon_a11y_label、with_right_side_icon_disabled并且 app/src/menu.rs 的注释明确指出右侧动作必须通过with_right_side_icon_action设置。也就是说普通菜单仅设置with_right_side_icon(Icon::X)而不设置 action 时右侧图标保持装饰性、行为不变兼容旧菜单对应风险与缓解中的 opt-in 要求设置了右侧动作后点击X会派发独立的DeleteSecret与点击行主体派发的SelectSecret完全分离通过with_right_side_icon_disabled(is_pending_delete)当该行删除在途时禁用X防止重复请求无障碍标签 Delete API key {name} 满足 PRODUCT behavior 16 对键盘可达性的要求。同时 PRODUCT behavior 3 明确X只出现在已存在的托管密钥行上——头部行、Inherit key from environment行、加载/错误占位行、New行以及 New sidecar 子菜单的密钥类型行都不显示X。从 build_main_menu_items 可以看到header、NO_SECRET_LABEL行、Loading…/Unable to load secrets占位行以及NEW_ITEM_LABEL行均未调用with_right_side_icon_actionNew 行只有装饰性ChevronRight完全符合规格。七、确认对话框点击 X 不立即删除PRODUCT behavior 5 要求点击X只打开确认模态框不得先选中该密钥、不得打开 New sidecar、也不得误触发行自身的 select 动作。实现上DeleteSecret分支auth_secret_selector.rs构造一个捕获了team_scope、harness、name、owner的PendingAuthSecretDeletion载荷关闭菜单并弹出确认对话框AuthSecretSelectorAction::DeleteSecret { name, owner } { let pending_deletion PendingAuthSecretDeletion { team_scope: Rc::new(UserWorkspaces::as_ref(ctx).team_context_for_operation(ctx)), harness: self.ambient_agent_model.as_ref(ctx).selected_harness(), name: name.clone(), owner: owner.clone(), }; self.set_menu_visibility(false, ctx); self.delete_confirmation_dialog.update(ctx, |dialog, ctx| { dialog.show(pending_deletion, ctx); }); ... }对话框视图位于 app/src/terminal/view/ambient_agent/delete_auth_secret_confirmation_dialog.rs规格要求的内容TECH.md 第 5 点标题Delete secret正文Are you sure you want to delete {SECRET_NAME}? This action cannot be undone. Any agents or environments referencing this secret will no longer have access to it.按钮Cancel与破坏性样式Delete支持 dismiss-to-cancel居中浮层对话框与Cancel/Confirm事件由 handle_delete_confirmation_event 处理Cancel只隐藏对话框、不触碰 pending 状态、不发起删除Confirm才把捕获的载荷交给start_secret_delete。由于删除载荷在打开模态框时就被捕获即使模态框打开期间选择器状态发生变化也不会删错目标。该对话框复用了仓库中既有的DialogDangerPrimaryTheme破坏性确认模式与 app/src/settings_view/delete_environment_confirmation_dialog.rs 和 app/src/settings_view/mcp_servers/destructive_mcp_confirmation_dialog.rs 保持一致。八、事件回流成功/失败后的状态收尾选择器订阅了HarnessAvailabilityModelauth_secret_selector.rs对两个新事件分别处理。8.1 成功AuthSecretDeletedhandle_secret_deleted 依次完成清理 pending 状态pending_deletes.remove((harness, name, owner))清理持久化偏好当CloudAgentSettings.auth_secret_preference恰好指向被删名称时调用persist_auth_secret_preference(..., None, ...)重置——这一步不依赖当前活跃 harness即使用户在服务端响应前已经切换了活跃 harness也会清理CloudAgentSettings.last_selected_auth_secret[harness.config_name()]对应 TECH.md 的Always remove要求与 PRODUCT behavior 10清理内存选择仅当被删密钥正是当前选中项时set_harness_auth_secret_name(None, ctx)使芯片回退到无密钥/继承标签刷新菜单与按钮toast 反馈只有当removed_pending为 true即这次删除由本选择器发起才弹出成功 toastAPI key {name} deleted.通过ToastStack::add_ephemeral_toast(DismissibleToast::success(...), window_id, ctx)呈现——避免其它窗口/表面发起的删除在本窗口重复弹 toast仅当被删 harness 仍是活跃 harness 时ctx.notify()触发可见刷新。8.2 失败AuthSecretDeletionFailedhandle_secret_deletion_failed 则同样先移除匹配的 pending 目标仅在被删 harness 活跃时刷新菜单密钥仍保留在列表中PRODUCT behavior 11仅对本选择器发起的失败弹Failed to delete API key {name}: {error}错误 toastDismissibleToast::error错误文本取自服务端/用户可读错误用户随后可重试删除。两个 toast 均为add_ephemeral_toast的临时提示无自定义动作按钮PRODUCT behavior 15。九、相邻表面一致性其它 selector 不能继续展示已删密钥TECH.md 第 7 点要求AuthSecretFtuxDropdown、orchestration pickers 与 model selector 订阅者对新事件保持编译兼容并对已挂载的密钥列表刷新。其理由是如果模型已经发出AuthSecretDeleted其它已挂载的选择器就不应再把已删密钥作为可选项目展示PRODUCT behavior 12。失败事件可被这些订阅者忽略成功事件负责刷新。这里可以观察到一条贯穿全链路的一致性准则所有本地 UI 状态菜单行、按钮标签、内存选择、持久化偏好、其它选择器列表都以共享模型的删除结果为准而非在发起请求时乐观修改——这正是服务端确认前不呈现删除成功PRODUCT behavior 8、14的实现保证。即使列表已过期、服务端报告密钥不存在只要服务端没有明确返回删除成功就按失败处理并以服务端错误文本提示PRODUCT behavior 13。十、测试与验证策略TECH.md 明确本次实现不保留纯辅助性的单元测试但给出了未来高价值 seam 出现时可补充的覆盖方向缓存更新行为对应 PRODUCT behaviors 8/10/11成功删除只移除匹配的 auth secret 并发出AuthSecretDeleted失败删除保持列表不变并发出AuthSecretDeletionFailedteam 条目以SecretOwner::Team删除、用户条目以SecretOwner::CurrentUser删除选择器与确认路由PRODUCT behaviors 4/5/7/9/10点击行仍选中密钥点击删除 affordance 只打开确认框、不立即删除取消/关闭不启动删除确认后进入 pending 流程对 pending 密钥的重复确认派发被忽略删除当前选中密钥会清空内存选择与持久化设置菜单渲染若引入可复用右侧动作 API无右侧动作时右侧图标保持装饰性有右侧动作时与行动作独立派发手动验证清单打开含多个密钥的 selector 菜单确认仅已有密钥行有右对齐X、New行与 sidecar 行无X点击X弹出指定文案的破坏性确认框取消/关闭后行保持不变确认删除未选中密钥后该行消失并出现成功 toast确认删除当前选中密钥后芯片回退到无密钥/继承标签制造删除失败/离线状态后出现失败 toast 且行保留。实现阶段的聚焦检查命令TECH.md 第 5 点cargo check -p warp cargo fmt -- --check规格同时注明本实现 pass 按请求跳过测试执行。十一、并行化建议与风险缓解TECH.md 明确不建议并行化工作量小且高度耦合一个 UI 视图、一个共享模型、一个可选的小型菜单扩展拆分反而会增加 action/event 命名的合并冲突风险。规格列出的风险与缓解措施在当前代码中均有对应落点风险缓解实现点击X误触发行选择右侧动作独立派发with_right_side_icon_action与行with_on_select_action分离删除错误 owner 作用域AuthSecretEntry携带SecretOwnerfetch/create 时由secret_owner_from_space转换点击时不猜测删除后遗留过期选中状态同时清理AmbientAgentViewModel内存选择与CloudAgentSettings.last_selected_auth_secret且持久化清理不依赖活跃 harness菜单 API 改动过宽MenuItemFields右侧动作 opt-in未设置 action 的菜单右侧图标保持装饰性与原有行为不变十二、关键源码路径速查功能规格specs/cloud-mode-auth-secret-deletion/PRODUCT.md、specs/cloud-mode-auth-secret-deletion/TECH.md缓存条目与 owner 转换app/src/ai/harness_availability.rs、app/src/ai/harness_availability.rs删除方法与事件app/src/ai/harness_availability.rs、app/src/ai/harness_availability.rs选择器动作与 pending 去重app/src/terminal/view/ambient_agent/auth_secret_selector.rs、app/src/terminal/view/ambient_agent/auth_secret_selector.rs菜单行构建与右侧 Xapp/src/terminal/view/ambient_agent/auth_secret_selector.rs事件回流处理app/src/terminal/view/ambient_agent/auth_secret_selector.rs确认对话框视图app/src/terminal/view/ambient_agent/delete_auth_secret_confirmation_dialog.rs菜单右侧动作 APIapp/src/menu.rs服务端删除能力crates/managed_secrets/src/manager.rs、app/src/server/server_api/managed_secrets.rs结语从产品规格到技术规格再到落地代码从 auth secret selector 删除密钥功能展示了一条完整的双规格驱动实现路径数据模型先补足owner元数据共享模型提供删除方法与事件选择器层用独立右侧动作和 pending 去重保证交互正确性确认对话框兜底防误删事件回流统一收尾内存状态、持久化偏好与 toast 反馈。对于希望在 Warp 代码库中继续深入该功能或扩展类似列表内直接删除交互的开发者上述源码路径与验证清单可作为直接的参考起点。赞分享桌面应用开发者工具人工智能AI 应用AI Agent代码智能体【免费下载链接】warpWarp is an agentic development environment, born out of the terminal.项目地址https://gitcode.com/GitHub_Trending/wa/warp点击查看免费下载相关推荐Warp 云模式认证密钥删除功能全解析从选择器 X 按钮到服务端删除的实现指南Warp 云模式认证密钥删除功能全解析从选择器 X 按钮到服务端删除的实现指南 导读 在 Warp 的云模式cloud mode环境中Agent 运行所桌面应用开发者工具人工智能AI 应用AI Agent代码智能体如何高效实现大数据删除Hudi删除功能的完整指南如何高效实现大数据删除Hudi删除功能的完整指南 在大数据处理领域高效的数据删除操作一直是一个挑战。Apache HudiHadoop Upserts D大数据数据湖数据工程Apache Druid 数据删除完整指南从软删除到 kill 任务永久清除Apache Druid 数据删除完整指南从软删除到 kill 任务永久清除 本指南以 docs/data management/delete.md http数据库OLAP大数据后端上一篇git-extras 的 git missing精准查看两个分支之间缺失提交的命令行指南下一篇魔兽争霸3终极优化指南6个简单配置让经典游戏在Windows 11焕发新生创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表