ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Warp 编排卡片内联创建 API Key:Orchestration Cards 的 Create-API-Key Flow 设计与实现解析

Warp 编排卡片内联创建 API Key:Orchestration Cards 的 Create-API-Key Flow 设计与实现解析 桌面应用开发者工具人工智能AI 应用AI Agent代码智能体【免费下载链接】warpWarp is an agentic development environment, born out of the terminal.项目地址https://gitcode.com/GitHub_Trending/wa/warp点击查看免费下载导读本文解析 Warp 终端中编排卡片Orchestration Card内联创建 API Key功能对应 specs 中的 QUALITY-702的完整设计与实现当用户在RunAgents确认卡片或计划卡片plan card的编排配置块中为 Claude Code、Codex 等非 Oz 环境下的子 Agent 选择认证密钥时如果当前 harness 尚未配置任何托管密钥用户可以直接在对话内打开工作区级模态框创建新密钥而无需离开对话、跳转云模式 FTUX。读完本文你将掌握AuthSecretSelection状态枚举的设计动机、picker 菜单内容与标签推导规则、Accept 门控与 tooltip 联动、一次性自动打开守卫auto-open one-shot guard的状态机以及 FTUX 视图如何从云模式解耦并复用于工作区模态框。1. 背景与问题编排卡片orchestration card用于在 Warp 中启动额外的子 Agentchild agents。当用户在非 Oz harness如 Claude Code、Codex下编排时卡片会展示一个API key选择器picker。在此之前该 picker 隐含一个前提当前 harness 下至少已存在一个托管密钥。当不存在任何托管密钥时下拉菜单实际上是空的卡片内没有任何路径可以创建密钥。用户只能离开当前对话找到云模式 FTUXfirst-time user experience首次使用引导界面创建密钥回到对话并重新触发卡片。更糟糕的是在这种空密钥状态下Accept 按钮仍会静默放行将继承自 worker 环境的凭据直接派发出去——这通常不是用户想要的且往往在下游执行中失败。这使得用户在新 harness 下的第一次编排尝试变成了一条死路。2. 方案总览本次变更详见产品文档 specs/QUALITY-702/PRODUCT.md 与技术文档 specs/QUALITY-702/TECH.md从三个层面解决问题扩展既有编排卡片的 auth-secret picker当用户没有当前 harness 的托管密钥时可直接在对话内创建一个无需离开。解耦云模式创建密钥视图将 create-key 视图与其对云模式状态AmbientAgentViewModel的紧耦合剥离重新托管到工作区级workspace-level的阻塞式模态框中。重塑卡片选择状态把卡片上OptionString 兄弟布尔值sibling bool的双字段编码收敛为三态实现中实际为四态枚举使 picker 标签、Accept 门控、持久化三者行为一致地匹配产品规格。两张编排卡片表面——RunAgents确认卡片和计划卡片的行内编排配置块——都获得了新的 picker 条目和新的动作变体create_new_auth_secret_requested该请求会被冒泡到工作区由工作区打开模态框。3. 核心状态建模AuthSecretSelection枚举3.1 旧双字段编码的问题在引入枚举之前OrchestrationEditState通过以下两个字段表达密钥选择auth_secret_name: OptionStringauth_secret_explicit_inherit: bool这套编码存在几个微妙的问题TECH.md 第 3 节明确指出None false与None true含义不同但都表现为没有名字线格式on-wire proto只携带名字无法表达显式继承这个意图picker 标签、Accept 门控、持久化逻辑各自都要对两个字段做特判极易出现三处判定不一致。3.2 新枚举实际实现为四态技术文档中最初设计为三态而当前仓库源码 app/src/ai/orchestration/config_state.rs 中的实际实现在此基础上增加了CreatingNew变体用于区分尚未选择与正在创建模态框已打开/// The users current selection in the auth secret picker. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AuthSecretSelection { /// No choice yet; re-seeded from persisted settings. Blocks Accept. Unset, /// User explicitly chose to inherit credentials from the worker env. Inherit, /// User picked a managed secret by name. Named(String), /// Creating a key (modal open). Blocks Accept and, unlike Unset, is /// not re-seeded from persisted settings. CreatingNew, }各变体语义如下变体含义picker 触发标签Accept 门控持久化Unset尚未做出任何选择支持创建的 harness 显示 New API key…否则显示继承文案禁用不持久化Inherit用户显式选择继承 worker 环境凭据Inherit key from environment启用清除持久化键Named(name)用户按名称选中某个托管密钥密钥名启用写入last_selected_auth_secretCreatingNew正在创建密钥模态框打开中 New API key…禁用不持久化且不会被持久化设置重新播种文档与实现的差异说明TECH.md 第 3 节描述的是三态设计Unset/Inherit/Named仓库源码在落地时补充了CreatingNew变体。它与Unset的关键区别在于Unset每次渲染都会被持久化设置重新播种re-seed而CreatingNew不会被重新播种——这样当用户正在创建密钥时后台刷新不会把一个陈旧的选中状态悄悄恢复回来config_state.rs。3.3 线格式映射与按名取数AuthSecretSelection::from_optional_name(OptionString)将线格式载荷映射进枚举Some(name)且非空白 →Named(name)None→Unset。由于线上格式与持久化设置只携带名字缺席永远表示尚未选择config_state.rs。OrchestrationEditState::auth_secret_name()返回Named载荷对Inherit/Unset/CreatingNew返回None这样只关心线上字段的派发代码无需匹配整个枚举。同时该函数会依据当前模式/harness 是否支持托管密钥做可见性门控防止用户在切换回 Local 或切换到无认证 harness 后残留的Named(_)泄漏进线上载荷config_state.rs。3.4 持久化边界只有Named(_)会通过CloudAgentSettings.last_selected_auth_secret持久化按harness.config_name()键控。Inherit与Unset属于每个会话、每个 harness 的 UI 状态不落盘。选中托管密钥会写入该键选择 Inherit 会清除该键通过 New API key…切到Unset/CreatingNew同样会清除避免取消模态框后残留陈旧名字。4. Picker 内容与触发标签推导4.1 菜单内容与顺序populate_auth_secret_picker_for_harness定义于 app/src/ai/blocklist/inline_action/orchestration_controls.rs会在 harness 或密钥列表变化时重建下拉菜单条目顺序如下Inherit key from environment—— 恒常存在点击时派发auth_secret_changed(None)空 row id 即为 Inherit 条目见api_key_snapshot的行映射逻辑。已加载的托管密钥—— 按服务器返回顺序排列若处于 Loading/Failed 状态则渲染单个禁用的占位条目OptionSourceStatus::Loading→ Loading…OptionSourceStatus::Failed→ 错误消息文本。分隔线 New API key…—— 仅当auth_secret_types_for_harness(...)非空即该 harness 支持至少一种托管密钥类型时出现点击派发create_new_auth_secret_requested()动作。此外该函数会通过HarnessAvailabilityModel::ensure_auth_secrets_fetched触发一次惰性抓取lazy fetch使后续帧中Loading…被真实条目替换。let name (!row.id.is_empty()).then_some(row.id); MenuItem::Item(MenuItemFields::new(row.label).with_on_select_action( DropdownAction::select_action_and_close(A::auth_secret_changed(name)), )) // ... if supports_create_new { items.push(MenuItem::Separator); items.push(MenuItem::Item( MenuItemFields::new(AUTH_SECRET_CREATE_NEW_LABEL).with_on_select_action( DropdownAction::select_action_and_close(A::create_new_auth_secret_requested()), ), )); }4.2 触发标签推导picker 的触发标签闭合态下拉框顶部显示的文本由auth_secret_trigger_label直接从AuthSecretSelection推导orchestration_controls.rsfn auth_secret_trigger_label(selection: AuthSecretSelection, supports_create_new: bool) - String { match selection { AuthSecretSelection::Named(name) name.clone(), AuthSecretSelection::Inherit AUTH_SECRET_INHERIT_LABEL.to_string(), AuthSecretSelection::CreatingNew AUTH_SECRET_CREATE_NEW_LABEL.to_string(), AuthSecretSelection::Unset if supports_create_new { AUTH_SECRET_CREATE_NEW_LABEL.to_string() } AuthSecretSelection::Unset AUTH_SECRET_INHERIT_LABEL.to_string(), } }Named(name)→ 密钥名Inherit→ Inherit key from environmentUnset且 harness 支持创建 → New API key…Unset且不支持创建 → Inherit key from environmentCreatingNew→ New API key…。标签始终使用下拉框的默认文本颜色不做置灰占位处理。TECH.md 记录了一个值得注意的踩坑早期迭代曾尝试覆盖触发颜色以弱化占位符但该覆盖路径会在下拉框自身派发的动作执行过程中再次进入下拉框视图触发 warpui 的 Circular view update循环视图更新守卫因此该覆写被移除TECH.md 第 4 节。5. 动作 trait 与 handler 接线5.1 新增 trait 方法OrchestrationControlAction由RunAgentsCardViewAction与OrchestrationConfigBlockAction共同实现新增一个工厂方法orchestration_controls.rs/// User picked the New API key… item; opens the workspace create modal. fn create_new_auth_secret_requested() - Self;两个实现者都添加了CreateNewAuthSecretRequested变体处理逻辑完全一致调用oc::apply_create_new_auth_secret_requested(...)将选择重置为CreatingNew并清除持久化的名字——这样取消模态框不会悄悄残留一个陈旧的已选名称解析当前活跃 harness派发WorkspaceAction::OpenCreateAuthSecretModal { harness }卡片刷新 Accept 门控并通知重绘。5.2 避免循环更新的设计约束apply_auth_secret_change与apply_create_new_auth_secret_requested刻意不会重新进入 picker 视图内部没有populate_*或sync_*调用。因为这些 helper 是从下拉框自身派发的动作内部被调用的若再次进入下拉框视图就会触发上文提到的循环更新守卫。下拉框会在菜单点击时自行更新其显示标签编排器的职责只是记录状态并持久化。5.3 新密钥被采纳apply_created_auth_secret_if_matchesorchestration_controls.rs在HarnessAvailabilityEvent::AuthSecretCreated事件到达时检查创建密钥的 harness 是否与卡片当前 harness 一致不一致 → 返回false不动作一致且当前选择已是同名Named→ 返回false幂等否则将选择置为Named(created_name)并调用persist_auth_secret_selection写盘返回true。这样新创建的密钥会被卡片立即采纳为当前选中项无需等待手动重新填充。6. 工作区级模态框6.1 动作与宿主WorkspaceAction::OpenCreateAuthSecretModal { harness }定义于 app/src/workspace/action.rs只由两个卡片动作 handler 派发。工作区视图app/src/workspace/view.rs 附近持有一个ModalViewStateModalAuthSecretFtuxView在动作到达时惰性构造lazily constructed模态框并以请求的 harness 参数化视图。模态框是工作区级别的打开期间会阻塞其余 UI。它内部托管与云模式 FTUX 相同的AuthSecretFtuxView组件用户可以在其中选择密钥类型当 harness 支持多于一种类型时输入密钥值与显示名称提交、取消Skip 在此模态框模式中被隐藏——picker 中已有的 Inherit key from environment 条目承担了同样的角色。6.2 生命周期事件处理工作区订阅视图的生命周期事件TECH.md 第 6 节事件工作区行为Created { harness, name }通过CloudAgentSettings.last_selected_auth_secret将该密钥持久化为该 harness 的当前选中项先写设置、后关模态框以保证随后的HarnessAvailabilityEvent::AuthSecretCreated事件能读到已落盘的值随后关闭模态框Cancelled/Skipped关闭模态框无副作用发起卡片的选中状态不变仍为Unset/CreatingNewFailed { error }保持模态框打开由视图自身渲染行内错误用户可修正后重试两张卡片视图则订阅HarnessAvailabilityEvent::AuthSecretCreated并调用oc::apply_created_auth_secret_if_matches(...)将新密钥采纳为卡片选中项同时 Accept 门控立即解除。7. 一次性自动打开守卫Auto-Open One-Shot Guard为了给首次体验提供引导又不至于烦人每张卡片持有一个has_auto_opened_create_modal: bool布尔守卫。maybe_auto_open_create_modal是唯一的检查汇聚点chokepoint按序执行以下判定TECH.md 第 7 节守卫已置位 → 直接返回卡片不在交互式确认状态已拒绝、已自动启动、正在 spawning、从历史恢复、动作已结束或正在异步执行→ 返回活跃 harness 没有 auth-secret picker例如 Oz→ 返回auth_secret_selection不是Unset→ 返回harness 的密钥列表不是Loaded(secrets)且secrets.is_empty()→ 返回。NotFetched、Loading、Failed一律视为尚不可判定——HarnessAvailabilityEvent::AuthSecretsLoaded订阅会在密钥真正到达后重新触发检查置位守卫并派发WorkspaceAction::OpenCreateAuthSecretModal { harness }。守卫的复位时机构造时置为falseupdate_request中当 harness、模型或执行模式经流式streaming变化时try_auto_launch_on_stream_complete中流完成快照是权威的最终状态需要重新评估ExecutionModeToggled与HarnessChanged动作 handler 中。maybe_auto_open_create_modal从上述相同路径以及AuthSecretsLoaded/AuthSecretsFetchFailed订阅 handler 中被调用。切换 harness 或切换 Local/Cloud 会复位一次性守卫让新 harness / 新模式获得自己的一次自动弹出机会而取消/跳过模态框不会在下次渲染或通知周期中再次弹出。8. Accept 门控与 Tooltipoc::accept_disabled_reason_with_auth(state.orch, ctx)扩展了既有的OrchestrationEditState::accept_disabled_reason当auth_secret_selection为Unset且 harness 暴露了 picker 时返回人类可读的禁用原因例如 Pick an API key or choose to inherit from the environment before accepting.。两张卡片视图都通过一个小的refresh_accept_button_state方法调用该 helper并据此设置 Accept 按钮的disabled与tooltip确认卡片Accept 是一个CompactibleSplitActionButton直接设置禁用与提示计划卡片使用同一个门控渲染行内校验错误而不是禁用按钮。refresh_accept_button_state在每个动作 handler 与每个触碰state.orch的模型订阅 handler 中被调用包括AuthSecretCreated、AuthSecretsLoaded、AuthSecretsFetchFailed分支。set_disabled/set_tooltip在值未变化时是廉价 no-op。CompactibleSplitActionButton::set_disabled/set_tooltip会同时委托到主按钮与菜单按钮使整个拆分按钮呈现为单一门控的交互控件。这两个能力的底座由 app/src/view_components/compactible_action_button.rs 的set_disabled/set_tooltip提供——它让既有的单状态按钮也能从父级门控重新推导自身状态。9. FTUX 视图解耦细节9.1 解耦前的问题此前云模式 FTUX 视图持有Rcdyn AmbientAgentViewModel在render/ 事件 handler 内部从模型读取选中 harness提交时的副作用也直接针对模型执行set_harness_auth_secret_name、mark_harness_auth_ftux_completed、last_selected_auth_secret写入以及云模式特有的set_harness Oz后置动作。这使得该视图无法脱离云模式复用。9.2 解耦后的形态现在 app/src/terminal/view/ambient_agent/auth_secret_ftux_view.rs 中构造时直接接收harness: Harness而非视图模型句柄提供set_harness(harness, ctx)setter由父级在云模式 harness 选择器变化时调用切换 harness 会清空进行中的创建状态并同步内嵌下拉框见 auth_secret_ftux_view.rs副作用不再在视图内执行改为发出生命周期事件auth_secret_ftux_view.rs#[derive(Debug, Clone)] pub enum AuthSecretFtuxViewEvent { /// User picked an existing secret from the in-view dropdown. SecretSelected { harness: Harness, name: String }, /// User created a new secret via the form. Created { harness: Harness, name: String }, /// User dismissed the form via Cancel. Cancelled, /// User skipped via the in-dropdown Skip item. Skipped { harness: Harness }, /// create_auth_secret failed. The view also shows a toast. Failed { error: String }, }新增with_skip_hidden(bool)开关供工作区模态框隐藏 Skip 按钮该上下文下 Inherit 位于 picker 上同文件还提供with_compact_mode切换到模态框的精简呈现不显示描述头、下拉框隐藏既有密钥与 Skip、自动进入首个密钥类型的创建表单、并在表单上方渲染 harness 选择器auth_secret_ftux_view.rs创建表单的提交流程由validated_form_snapshot统一校验名称 trim 后非空、必填字段 trim 后非空handle_continue构造ValidatedForm后调用HarnessAvailabilityModel::create_auth_secretauth_secret_ftux_view.rs视图只消费属于自己的AuthSecretCreated事件通过is_saving harness 匹配 pending_name 匹配三重过滤避免并发 FTUX 视图的成功事件误关当前模态框auth_secret_ftux_view.rs。9.3 宿主职责云模式由 app/src/terminal/input.rs 以云模式选中的 harness 构造 FTUX 视图/下拉框并订阅新的生命周期事件执行与原先内联完全相同的副作用持久化选中密钥、标记 FTUX 完成、写入last_selected_auth_secret等从而保持云模式 UX 端到端不变。工作区模态框订阅同一组事件执行模态框特有行为关闭 持久化。AuthSecretFtuxDropdown也做了同样的形状变更直接接收 harness、暴露set_harness、移除subscribe_to_model(AmbientAgentViewModel)依赖app/src/terminal/view/ambient_agent/mod.rs 更新了 re-exports。10. 云模式对等性ParityTECH.md 第 10 节明确了两条必须在编排卡片上镜像的云模式行为10.1 默认选择逻辑resolve_default_auth_secret_for_harness只提升promote已持久化的last_selected_auth_secret值绝不回退到第一个已加载密钥。这一点同时匹配 warp-server 的 webappHarnessAuthSecretSelectoruse-agent-form-state.ts与云模式的auth_secret_selector.rs::maybe_restore_auth_secret_from_settings。没有显式选择时picker 停留在 New API key…或在不支持托管类型的 harness 上停留在 Inherit 文案。10.2 持久化形态在任一卡片上选中托管密钥 → 写入同一份CloudAgentSettings.last_selected_auth_secret按harness.config_name()键控云模式下次启动时读取选择 Inherit → 清除该键切到Unset通过 New API key…→ 同样清除保证取消模态框不会残留陈旧名字。11. 关键文件清单复用的创建密钥视图已解耦app/src/terminal/view/ambient_agent/auth_secret_ftux_view.rs —— 构造时接收harness: Harness暴露set_harness以AuthSecretFtuxViewEvent::{Created, Cancelled, Skipped, Failed}事件替代直接模型修改提供with_skip_hidden开关含compact_mode精简呈现app/src/terminal/view/ambient_agent/auth_secret_ftux_dropdown.rs —— 同样的形状变更移除subscribe_to_model(AmbientAgentViewModel)依赖app/src/terminal/view/ambient_agent/mod.rs —— 更新 re-exports云模式重接线保留既有 UXapp/src/terminal/input.rs —— 以云模式选中 harness 构造 FTUX 视图/下拉框订阅生命周期事件并执行原内联副作用编排卡片表面app/src/ai/blocklist/inline_action/orchestration_controls.rs —— 共享 picker 逻辑AuthSecretSelection线程化、 New API key…菜单条目、apply_create_new_auth_secret_requested、apply_created_auth_secret_if_matches、OrchestrationControlAction::create_new_auth_secret_requested变体app/src/ai/blocklist/inline_action/run_agents_card_view.rs —— 确认卡片实现新 trait 变体、接线工作区模态框派发、订阅HarnessAvailabilityEvent::AuthSecretCreated、持有一次性自动打开守卫app/src/ai/document/orchestration_config_block.rs —— 计划卡片行内配置块picker、动作 handler、AuthSecretCreated采纳逻辑与确认卡片一致工作区模态框宿主app/src/workspace/action.rs —— 新增WorkspaceAction::OpenCreateAuthSecretModal { harness }app/src/workspace/view.rs —— 持有ModalViewStateModalAuthSecretFtuxView响应新动作打开模态框订阅生命周期事件以关闭并持久化按钮门控管线app/src/view_components/compactible_action_button.rs ——set_disabled/set_tooltipapp/src/view_components/compactible_split_action_button.rs —— 将set_disabled/set_tooltip委托给主按钮与菜单按钮12. 验证方式自动化仓库技术文档记录的自动化验证命令TECH.md 第 11 节cargo check -p warp cargo fmt cargo clippy --workspace --all-targets --all-features --tests -- -D warnings手动验证要点产品文档 specs/QUALITY-702/PRODUCT.md 第 7 节给出了完整的手动测试矩阵关键场景包括清空所有托管 Claude Code 密钥后编排 Cloud Claude Code确认模态框自动打开一次取消后不再弹出点击 picker 的 New API key…可重新打开模态框中创建密钥确认 picker 自动选中新密钥且 Accept 启用取消模态框确认 picker 停在 New API key…、Accept 保持禁用并带 hover tooltip显式选择 Inherit key from environment确认 Accept 启用卡片从 Claude Code 切到 Codex无 Codex 密钥确认模态框为 Codex 自动打开一次卡片 Cloud ↔ Local 切换确认自动打开为新模式重新武装恢复包含已展示编排卡片的历史对话确认不弹出模态框云模式 FTUX 全流程确认端到端行为不变。13. 后续事项TECH.md 第 12 节记录了三条后续方向考虑把工作区持有的模态框抽取为小型可复用宿主目前内联在Workspace上当出现第二个消费方时再引入抽象待编排 picker 视觉定型后为 New API key…条目增加轻量视觉处理如前置加号图标长期看云模式 FTUX 视图的 Skipped 路径可整体移除——工作区模态框已隐藏 Skip编排 picker 也直接暴露 InheritSkip 已无实际消费场景。赞分享桌面应用开发者工具人工智能AI 应用AI Agent代码智能体【免费下载链接】warpWarp is an agentic development environment, born out of the terminal.项目地址https://gitcode.com/GitHub_Trending/wa/warp点击查看免费下载相关推荐Warp TUI 的 /api-keys 内联菜单API Key 管理、密钥掩码与 Grok 连接流程深度解析Warp TUI 的 /api keys 内联菜单API Key 管理、密钥掩码与 Grok 连接流程深度解析 本文基于 Warp 开源仓库中的 specs/桌面应用开发者工具人工智能AI 应用AI Agent代码智能体SpinalHDL社区生态如何参与贡献与获取技术支持SpinalHDL社区生态如何参与贡献与获取技术支持 SpinalHDL作为基于Scala的硬件描述语言HDL凭借其强大的表达能力和灵活的设计流程吸引编程语言硬件开发嵌入式Warp TUI 的 /api-keys 内联菜单一站式 AI 供应商凭据管理CODE-1930 设计实现解析Warp TUI 的 /api keys 内联菜单一站式 AI 供应商凭据管理CODE 1930 设计实现解析 本文以 specs/CODE 1930/P桌面应用开发者工具人工智能AI 应用AI Agent代码智能体上一篇ncmdump 完整教程NCM 转 MP3 免费批量拖拽整库 500 首歌一次转完下一篇Upscayl免费AI图像放大指南:4倍出图与批量实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表