十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

OpenHands Canvas Live ACP e2e:用真实 Agent-Server 容器验证 ACP 凭证的 LookupSecret 全链路

OpenHands Canvas Live ACP e2e:用真实 Agent-Server 容器验证 ACP 凭证的 LookupSecret 全链路 OpenHands Canvas Live ACP e2e用真实 Agent-Server 容器验证 ACP 凭证的 LookupSecret 全链路【免费下载链接】OpenHands OpenHands: AI-Driven Development项目地址: https://gitcode.com/GitHub_Trending/ope/OpenHands本篇基于 Live ACP-in-Docker e2e 文档讲解 OpenHands 前端仓库中一套真容器 真凭证 真 API 调用的端到端验证方案它通过 Canvas 自身的代码路径把 Codex / Claude Code / Gemini CLI 的凭证写入 agent-server 的 secret store再以LookupSecret形式引用最终断言拿到真实的 agent 回复。读完本文你能掌握这套 e2e 的完整运行步骤Docker 启动、脚本执行、环境参数、三个 provider 的凭证采集实现以及buildStartConversationRequest如何把每个保存的凭证发射为LookupSecret的源码级原理。这套 e2e 验证的是什么单元测试__tests__/api/agent-server-adapter.test.ts只断言请求的形状——即buildStartConversationRequest输出的 payload 结构是否符合预期而tests/e2e/live-acp/下的脚本断言的是真的能跑通针对一个真实运行的 agent-server 容器发起真实的 provider API 调用并检查 agent 的最终回复中包含约定的校验 token如ACPOK-CODEX。它验证的完整凭证链路是onboarding 等价步骤通过SecretsService.createSecret即应用 onboarding 中 Set up credentials 的同一个调用把宿主机上采集到的凭证 upsert 进 agent-server 的 secret store本地后端走PUT /api/settings/secrets构建启动请求调用 Canvas 自己的buildStartConversationRequest把每个已保存的凭证按名字引用为一个LookupSecretsecret 的值不出现在请求里服务端解析agent-server 在 ACP 进程冷启动spawn时通过GET /api/settings/secrets/name把值从 store 解析回来日志中可见 200 响应完成如 codex 的auth.json物化Materialised ACP file-secret CODEX_AUTH_JSON - …/acp/codex/auth.json或 Gemini 的 ADC 物化GOOGLE_APPLICATION_CREDENTIALS_JSON - …/acp/gemini-cli/gcloud-credentials.json。版本要求需要agent-server:1.25.0-python或更新software-agent-sdk#3510。ACP 凭证以 loopbackLookupSecret的形式传递只有 #3510 之后才在事件循环之外解析它们旧镜像会在第一轮对话时死锁。运行脚本推荐镜像为1.28.0-pythonv1.28.0 新增了 Canvas 使用的client_toolsAPIacp-docker-e2e.mts 头部注释明确要求该版本。该目录位于tests/下被 Vitest 排除不属于npm test——因为它需要一个正在运行的容器和宿主机上的真实凭证只能手动执行。运行步骤1. 启动 agent-server 容器# 1. Agent-server 容器。v1.28.0 新增 Canvas 使用的 client_tools API。 # 挂载 Python 目录保证迁移前的会话状态仍可加载。 docker run -d --name oh-acp -p 8010:8000 \ -v oh-acp-data:/workspace \ -v $(pwd)/tools:/canvas-tools:ro -e OH_EXTRA_PYTHON_PATH/canvas-tools \ ghcr.io/openhands/agent-server:1.28.0-python几个挂载参数的含义-p 8010:8000容器内 agent-server 监听 8000映射到宿主机 8010与 e2e 脚本默认ACP_E2E_BASE_URLhttp://localhost:8010对应-v oh-acp-data:/workspace命名卷挂载到/workspace作为所有会话working_dir的根每个会话使用base/id_hex独立目录以便 agent-server 为每个会话初始化独立的 git repo worktree-v $(pwd)/tools:/canvas-tools:roOH_EXTRA_PYTHON_PATH/canvas-tools把仓库的 tools/ 目录含 canvas_ui_tool.py只读挂入容器并注入 Python 路径供 Canvas 的canvas_ui_controlclient tool 使用。2. 运行 e2e 脚本# 运行全部 provider或指定子集 npx vite-node -c tests/e2e/live-acp/vite-node.config.mts \ tests/e2e/live-acp/acp-docker-e2e.mts -- codex claude geminiacp-docker-e2e.mts 是请求构建器路径脚本验证通过后还会配套运行应用编排器路径脚本 acp-docker-app-e2e.mts一次一个 provider因为 settings 在 agent-server 上是全局的新进程可避免SettingsService缓存在 provider 之间串扰npx vite-node -c tests/e2e/live-acp/vite-node.config.mts \ tests/e2e/live-acp/acp-docker-app-e2e.mts -- codex脚本通过vite-node.config.mts在完整 Vite/React-Router 管线之外运行该最小配置只做了两件事——把#/*别名指向src/*应用平时靠 tsconfig-paths 解析vite-node 不加载它以及把openhands/typescript-client设为 SSR inline使其 ESM 解析方式与 Vitest 一致。3. 清理容器持有真实凭证docker rm -f oh-acp宿主机凭证采集三个 provider 的 collector凭证从宿主机读取全程不会打印。采集逻辑集中在共享的 harness.mts 中harness.mts#L107-L156任何 provider 的凭证缺失时该 provider 被跳过跳过不算失败Provideracp_server默认模型可覆盖凭证来源容器 secret 名Codexcodexgpt-5.5/mediumACP_E2E_CODEX_MODEL~/.codex/auth.jsonCODEX_AUTH_JSONClaude Codeclaude-codeclaude-opus-4-7ACP_E2E_CLAUDE_MODELmacOS 钥匙串security find-generic-password -s Claude Code-credentials -w取claudeAiOauth.accessTokenCLAUDE_CODE_OAUTH_TOKENGemini CLIgemini-cligemini-2.5-proACP_E2E_GEMINI_MODEL~/.config/gcloud/application_default_credentials.json gcloud 默认项目需先gcloud auth application-default loginGOOGLE_APPLICATION_CREDENTIALS_JSON、GOOGLE_CLOUD_PROJECT、GOOGLE_CLOUD_LOCATION、GOOGLE_GENAI_USE_VERTEXAItrue两个值得注意的实现细节均可在 harness.mts 中验证Claude 故意不设置ANTHROPIC_BASE_URL继承的环境 base URL 会破坏 OAuth token 的 bearer 认证harness.mts#L124-L128非 macOS 上 Claude collector 直接返回 null没有security命令时execFileSync抛错被 catch 掉runner 打印SKIP — credentials not present on host。另外 harness.mts 的registerDockerBackend()把应用的后端注册表指向该容器setRegisteredBackendssetActiveSelection效果等同于用户在 backend selector 中手动添加——下游的SecretsService、SettingsService以及buildStartConversationRequest发出的LookupSecret鉴权头全部经由这一选择解析宿主地址。源码纵深LookupSecret 是如何发射的SecretsService与 onboarding 相同的写入路径SecretsService.createSecret 对本地后端调用SettingsClient.upsertSecret({ name, value, description })即 agent-server 的PUT /api/settings/secrets按名字 upsert云后端则走saveCloudSecret。secret 名字有约束字母开头仅字母/数字/下划线1–64 字符。列表接口getSecrets()只返回名字和描述不返回值——值只存在于 agent-server 的 store 中。buildStartConversationRequest把名字变成 LookupSecret在 agent-server-adapter.ts 中LookupSecret的定义是interface LookupSecret { kind: LookupSecret; url: string; // 指向 secret store 的 loopback 地址 headers?: Recordstring, string; description?: string; }buildStartConversationRequestL1085接受customSecrets: Array{ name; description? }L1076并为每个名字生成一条{ kind: LookupSecret, url, ... }放入 payload 的secrets字段L1239-L1251。e2e 脚本正是利用这一点做不泄漏值的健全性检查把 payload 中所有secrets打印出kind并断言全部为LookupSecret——若不是直接判 FAILacp-docker-e2e.mts#L104-L121。ACP 相关的启动参数通过settings.agent_settings传入agent_kind: acp、acp_server即上表中的注册表键、acp_model可选acp_session_mode会话侧max_iterations: 8acp-docker-e2e.mts#L54-L82。轮询与断言harness.mts 提供两个共享 helperpollUntilTerminal(conversationId)每 2.5s 轮询GET /api/conversations/id直到execution_status进入终态集合{finished, error, stuck, stopped}或超时默认ACP_E2E_TIMEOUT_MS180000。注意idle刻意不在终态集合中——新建会话在 agent 启动前会报idle若把它当终态会在回复产生前就退出fetchFinalReply(conversationId)拉取GET /api/conversations/id/agent_final_response脚本断言其中包含对应 provider 的期望 tokenACPOK-CODEX/ACPOK-CLAUDE/ACPOK-GEMINI任务指令就是Reply with exactly: token。两条脚本路径覆盖的差异acp-docker-e2e.mts请求构建器路径acp-docker-app-e2e.mts应用编排器路径会话启动直接调buildStartConversationRequest内联agent_settingsacp_server/acp_model显式传入buildStartConversationRequestWithEncryptedSettingsL1290基础 settings 从后端重新拉取Agent 选择请求内联指定先经buildAcpAgentSettingsDiff(acpServer, { model })构造 diffPATCH /api/settingsagent_settings_diff模拟应用的 choose-agent 步骤额外证明LookupSecret从 store 解析、SDK 的acp_file_secrets物化端到端生效保存的凭证往返经过后端 store且编排器为每个已保存的 secret 名字发射正确的LookupSecretacp-docker-app-e2e.mts#L78-L102Provider 数量可一次跑多个-- codex claude gemini每个进程一个 providerharness.mts的注释点明了共享策略provider 计划模型、凭证采集器与 HTTP/轮询 helper 由两个脚本共用——改模型默认值或凭证参数时只改 harness不要分别改脚本避免两侧漂移。环境参数Knobs一览环境变量默认值说明ACP_E2E_BASE_URLhttp://localhost:8010agent-server 容器地址ACP_E2E_CODEX_MODEL/ACP_E2E_CLAUDE_MODEL/ACP_E2E_GEMINI_MODELgpt-5.5/medium/claude-opus-4-7/gemini-2.5-pro各 provider 的 ACP 模型需是账户/Vertex 项目支持的模型ACP_E2E_GEMINI_SESSION_MODE不设置SDK 用 provider 注册表默认设为default可绕过 gemini-cli ≥0.43 在 headless init 时set_session_mode(yolo)报错的 SDK 阻塞点GOOGLE_CLOUD_PROJECT/GOOGLE_CLOUD_LOCATION从 gcloud 读取 /us-central1Gemini Vertex 的项目与区域ACP_E2E_TIMEOUT_MS180000单轮会话轮询超时harness 中定义ACP_E2E_WORKING_DIR_BASE/workspace/acp-e2e请求构建器脚本的会话工作目录根已验证结果与 Gemini 的三个前置条件文档记录了 2026-06-07 针对ghcr.io/openhands/agent-server:1.25.0-python首个包含 software-agent-sdk#3510 的发布在全新卷上的复验每个凭证都从 secret store 种子注入、无残留状态日志确认 agent-server 在 ACP 冷启动期间解析了 loopbackLookupSecretGET /api/settings/secrets/name返回 200无死锁、无 Failed to start ACP server: timed out——这正是 #3510 修复的问题。三个 provider 的真实回复Provider结果日志证据Codex真实回复ACPOK-CODEX两个脚本Materialised ACP file-secret CODEX_AUTH_JSON - …/acp/codex/auth.jsoncodex-acp 0.15.0Authenticating with ACP method: chatgptClaude Code真实回复ACPOK-CLAUDE两个脚本claude-agent-acp 0.30.0CLAUDE_CODE_OAUTH_TOKENenv 路径未设ANTHROPIC_BASE_URLGemini CLI真实回复ACPOK-GEMINI¹Materialised ACP file-secret GOOGLE_APPLICATION_CREDENTIALS_JSON - …/acp/gemini-cli/gcloud-credentials.jsongemini-cli 0.45.1Authenticating with ACP method: vertex-ai在gemini-2.5-pro上发生真实 Vertex 推理¹Gemini 前置条件三者缺一不可新鲜的宿主机 ADC——需重新执行gcloud auth application-default login过期的 ADC 会以invalid_rapt失败这是凭证问题而非 Canvas 问题非 flash 的gemini-2.5-pro模型——gemini-cli 0.45.x 会在生成时把任何*-flashid 重新解析为当前默认 flash在不提供该模型的 project 上会 404software-agent-sdk#3532这也是 Canvas 预置gemini-5.5-pro之外的gemini-2.5-pro的原因ACP_E2E_GEMINI_SESSION_MODEdefault——绕过 gemini-cli ≥0.43 的set_session_mode(yolo)headless-init 阻塞点。acp-docker-e2e.mts内置了对第三种情形的提示若 gemini 在无 session mode 覆盖时报error脚本会提示这大概率是 SDK/gemini-cli 的set_session_mode(yolo)阻塞而非凭证问题请用ACP_E2E_GEMINI_SESSION_MODEdefault重跑以确认凭证链路端到端acp-docker-e2e.mts#L137-L147。小结这套 e2e 的设计要点走 Canvas 自己的代码凭证写入用 onboarding 相同的SecretsService.createSecret请求构建用应用相同的buildStartConversationRequest/buildStartConversationRequestWithEncryptedSettings——单元测试无法覆盖的它真的能用检查由它补齐凭证永不过客户端值只存进 agent-server 的 secret store请求里只有名字和 loopbackLookupSecret地址脚本侧只打印kind做健全性检查单点维护模型默认值与凭证采集器统一收敛在 harness.mts两条脚本路径共用可复现的版本前提agent-server ≥1.25.0#3510 的 off-loop 解析且推荐 1.28.0client_toolsAPI镜像、挂载与清理命令见上文运行步骤。相关文件索引README、harness.mts、acp-docker-e2e.mts、acp-docker-app-e2e.mts、vite-node.config.mts、agent-server-adapter.ts、secrets-service.ts、acp-providers.ts、单元测试对照。【免费下载链接】OpenHands OpenHands: AI-Driven Development项目地址: https://gitcode.com/GitHub_Trending/ope/OpenHands创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表