- 人工智能
- AI Agent
- Agent 框架
- 后端
- 工具调用
【免费下载链接】eve
The Open Framework for Building Agents
eve/extensions/git是 eve(The Open Framework for Building Agents)官方自带的 GitHub 扩展,它向 Agent 注入 GitHub 操作指令与pr(Pull Request)技能,并可在配置github后通过 Connect 代理为 Agent 提供带签名提交能力的认证gh工具。本文以 packages/eve-git/README.md 为骨架,结合 packages/eve-git 包源码与测试,讲解两种挂载方式、凭证租约与防火墙交换机制、fail-closed 的输入校验、gh-signed-commit签名提交原理,以及验证命令,帮助你为 Agent 搭建安全且可复现的 GitHub 提交流程。
扩展定位:GitHub 事项的单一责任模块
eve/extensions/git负责一切与 GitHub 相关的能力:它贡献 GitHub 指令(instructions)和pr技能,并在配置github后贡献一个认证的gh工具(支持签名提交)。与eve/extensions/code(编码与沙箱工具)明确分工——packages/eve-code/README.md 明确指出 "GitHub instructions, theprskill, and the authenticatedghtool live ineve/extensions/git";@eve/code中的github配置项已被标记为 deprecated,GitHub 相关能力统一收敛到本扩展。
随 eve 包发布,私有 workspace 包为源
该扩展随eve主包一起分发。位于 packages/eve-git 的私有 workspace 包@eve/git(见 packages/eve-git/package.json)是它的源头:eve 的构建过程把extension/目录复制进packages/eve/src/extensions/git/extension,编译进 eve 包,并发布以下入口点:
eve/extensions/git:扩展本身(挂载入口)eve/extensions/git/tools:gh工具,供直接挂载该工具的场景使用eve/extensions/git/sandbox:GitHub shell 执行、签名提交与指导类辅助函数
从源码结构看,@eve/git依赖eve(workspace 依赖)与zod,因此它不对外独立发布,而是作为 eve 包内建扩展存在;eve侧的同名目录 packages/eve/src/extensions/git 即为构建产物,其中 packages/eve/src/extensions/git/tools.ts 直接复用extension/lib/gh-tool.ts的defineGhTool绑定挂载配置。
两种运行模式概览
| 模式 | 配置 | 凭证来源 | 工具贡献 |
|---|---|---|---|
| Shell 凭证模式 | git({}) | Agent shell 中已有的 GitHub 凭证 | 无工具,走gh/gitCLI |
| Connect 代理模式 | git({ github: { connector, org, broker } }) | Connect 按仓库签发、防火墙交换 | git__gh工具 + 签名提交流程 |
这两种模式由扩展配置动态切换,影响指令文案、pr技能内容与工具注册,详见下文。
挂载方式一:Shell 凭证模式git({})
在 Agent 项目中创建agent/extensions/git.ts:
// agent/extensions/git.ts import git from "eve/extensions/git"; export default git({});git({})假定 Agent 的 shell 已经配置好 GitHub 凭证。此时扩展:
- 注入指令,让 Agent 在
bash中使用gh和gitCLI 完成 GitHub 操作; - 提供
git__pr技能:提交、推送,并用gh pr create --draft打开草稿 Pull Request; - 不贡献任何工具。
这一模式适合沙箱进程自身持有凭证的场景(如预置了 PAT 的沙箱)。指令文案由 packages/eve-git/extension/lib/github-guidance.ts 中的SHELL_INSTRUCTIONS定义,核心约束是:使用 shell 中已配置的凭证、绝不把凭证写进 URL 或命令参数、访问被拒时如实报告并停止。
挂载方式二:Connect 代理模式git({ github })
若要通过 Connect 代理 GitHub 访问,则为扩展配置github,它需要connector、org以及一个必需的broker(sandbox, rules)回调:
// agent/extensions/git.ts import git from "eve/extensions/git"; export default git({ github: { connector: "github/acme-bot", // Connect 连接器标识 org: "acme", // 限定组织 broker: async (sandbox, rules) => { // 将 rules 合入你自己的网络策略;rules 为 null 时移除租约 }, }, });配置 schema 定义在 packages/eve-git/extension/extension.ts:github为可选对象,三个字段均非空:
connector: z.string().min(1):Connect 连接器标识,用于向 Connect 请求令牌;org: z.string().min(1):GitHub 组织名,令牌的服务器端授权范围被限定在该组织;broker: fn<GitHubLeaseBroker>():回调函数,签名见 packages/eve-git/extension/lib/github-shell.ts 中的GitHubConfig((sandbox, rules: Record<string, GitHubLeaseRule[]> | null) => Promise<void>)。
配置后,扩展在session.started事件中动态贡献两个能力(实现见 packages/eve-git/extension/tools/gh.ts 与 packages/eve-git/extension/skills/pr.ts):
git__gh工具:每次命令为恰好一个该组织内的仓库申请令牌;git__pr技能:对要求验证签名(verified signatures)的仓库切换到gh-signed-commit流程。
对应地,指令文案切换为CONNECT_INSTRUCTIONS:告知 Agent GitHub 凭证对普通bash不可用,所有认证 GitHub 操作必须走gh工具。
broker 回调的职责
broker在每次命令执行时被调用两次:
- 执行前:收到包含 GitHub header-transform 规则的
rules对象,需将其安装到沙箱网络策略(例如setNetworkPolicy)中,同时保留消费者自己的其他网络规则; - 执行后:收到
null,用于移除刚才的租约(释放凭证交换规则)。
测试 packages/eve-git/test/integration/github-shell.test.ts 中的CONFIG.broker给出了最小实现范式:rules非空时把规则映射为allow策略,rules为null时恢复allow-all。若不实现 broker 的合并逻辑,覆盖凭证交换的规则会覆盖(甚至清空)消费者原有网络策略。
核心安全机制一:占位令牌 + 防火墙交换的凭证租约
Connect 代理模式最重要的设计是真实凭证永不进入沙箱进程。其执行流程(见 packages/eve-git/extension/lib/github-shell.ts 的executeGitHubShell):
- 校验输入:
validateGitHubShellInput在沙箱访问与令牌签发之前完成 fail-closed 校验(详见下一节); - 解析目标仓库:通过
assertCommandTargets确认命令目标(--repo/-R参数、git remote get-url origin等)必须属于声明仓库,repositoryNames进一步校验仓库必须属于配置的org; - 申请令牌:以
ConnectTokenParams(authorizationDetails: [{ type: "github_app_installation", org, repositories }]、subject: { type: "app" })调用getToken(connector, params),向 Connect 申请仅覆盖该仓库的 app-subject 令牌; - 构造占位凭证:生成随机
placeholder = eve-github-<uuid>与其 Basic 编码的placeholderAuthorization,沙箱进程只拿到占位GH_TOKEN和占位 Authorization,真实令牌只存在于防火墙规则中; - 安装租约并执行:
withGitHubCredentialLease以会话 ID 为键将同一会话内的命令串行化(leaseQueuesmap),随后调用broker(sandbox, rules)安装两条 header-transform 规则:- 对
api.github.com:匹配authorization为^(?:token|Bearer) <placeholder>$的请求,变换为Bearer <真实token>; - 对
github.com:匹配authorization为占位 Basic 值的请求,变换为真实 Basic 凭证;
- 对
- 执行并清理:
sandbox.run运行命令,finally中调用broker(sandbox, null)移除租约,并删除临时GH_CONFIG_DIR;输出经sanitizeOutput把真实 token / authorization 全部替换为[redacted],超过MAX_OUTPUT_BYTES = 100_000字节时截断尾部。
测试对上述机制做了逐项断言(packages/eve-git/test/integration/github-shell.test.ts):
- 沙箱进程环境变量中不包含真实令牌(
assert.doesNotMatch(JSON.stringify(runs[4]?.env), /secret-token/)),而防火墙策略policies[0]中包含它; GH_TOKEN形如eve-github-前缀的占位值;- 命令
git push被重写为显式 HTTPS URL(-c protocol.allow=never -c protocol.https.allow=always push https://github.com/...git),并注入GIT_CONFIG_KEY_0/GIT_CONFIG_VALUE_0的http.https://github.com/.extraheader与core.hooksPath=/dev/null; - 输出中的令牌被替换为
[redacted]。
命令执行的参数白名单
validateGitHubShellInput对可执行程序做出严格限制:
gh:必须有子命令,完整 gh CLI 面可用(issue、PR、review、Actions、CI、auth、alias、extension、API 请求等),但gh的别名与扩展命令可能执行本地代码——仍被沙箱隔离;git:仅允许fetch、ls-remote、pull、push四个远端命令(ALLOWED_GIT_REMOTE_COMMANDS),且必须使用经过校验的originremote;克隆请使用gh repo clone;gh-signed-commit:签名提交专用,必须显式给出--repo;- 其余程序一律拒绝。
本地 Git 操作(status、diff、add、commit、rebase)不需要 GitHub 凭证,作为普通bash命令使用。
核心安全机制二:fail-closed 的 gh 工具输入校验
git__gh工具的输入 schema 定义在 packages/eve-git/extension/lib/github-shell-schema.ts:
| 字段 | 约束 |
|---|---|
command | 非空、trim 后 1–20,000 字符 |
permissions | 恰好 1 个元素;provider必须为"github",repositories恰好 1 个owner/name,access必须为"write" |
description | 非空、1–500 字符,描述命令预期的 GitHub 侧结果 |
workingDirectory | 可选,1–1,000 字符,默认/workspace |
只支持 argv 语法,拒绝 shell 元字符
parseCommand(packages/eve-git/extension/lib/github-shell.ts)手写实现了一个极简 argv 解析器:支持单引号、双引号与反斜杠转义,但拒绝管道|、重定向>、分号;、&、<、$、反引号、()、{}等一切 shell 运算符,双引号内的$与反引号直接报错。测试"parses simple argv quoting without enabling a shell language"验证了gh pr comment 1 --body "it's ready"被正确解析,而gh pr view 1 | cat、gh pr view $(printenv)、gh pr view 1 > out、gh pr view 1; env全部抛错。
声明仓库强制校验
assertCommandTargets对三类命令的目标做一致性校验,目标不在声明仓库内时在签发令牌之前即拒绝:
git:要求argv[2] === "origin",并通过git remote get-url --push origin解析远端,仅接受规范的https://github.com/owner/name.git或git@github.com:owner/name.git形式;gh-signed-commit:要求显式--repo owner/name;gh:解析所有--repo、-R、--repo=参数,以及gh repo clone/view/fork的仓库位置参数。
canonicalRepository用^[A-Za-z0-9-]+\/[A-Za-z0-9._-]+$校验仓库格式;repositoryNames校验仓库的 owner 必须等于配置的org(大小写不敏感),否则拒绝并提示 "outside "。测试"rejects explicit command targets outside the declared repository before minting"遍历 7 种越权命令,断言均以/not in the declared repositories/拒绝且tokenRequested === false。
权限与工作目录约束
githubShellApproval是工具级的审批钩子:输入合法时返回"not-applicable"(免审批),不合法时返回{ type: "denied", reason }——permissions为空/多个、provider非 github、access非"write"都会导致 denied;resolveWorkingDirectory要求工作目录解析后必须落在沙箱 workspace 之内,../outside这类逃逸路径被拒绝;validateRepositoryRoot(packages/eve-git/extension/lib/repository-root.ts)对git/gh-signed-commit额外要求工作目录必须是 git work tree 的根(通过git rev-parse --show-toplevel校验),并使用 NUL 分隔的realpath -e -z输出消除换行歧义。
签名提交:gh-signed-commit的工作原理
对要求 verified signatures 的仓库,普通沙箱提交会被拒绝。pr技能要求先显式暂存变更,再通过gh工具执行:
gh-signed-commit --repo owner/name --branch <branch> --base <base> -m "<headline>" [-b "<body>"]gh-signed-commit是一个随installCodeTooling安装到受信路径/usr/local/lib/eve-code/gh-signed-commit的 Node 脚本,源码内嵌于 packages/eve-git/extension/lib/signed-commit.ts(当前版本GH_SIGNED_COMMIT_VERSION = "3")。它的执行逻辑:
- 安全前置检查:
ensureNoUnstagedTrackedChanges拒绝存在未暂存已跟踪改动的工作区——避免覆盖用户未授权的内容,也不允许 stash/revert; - 分支准备:若
--branch分支不存在,以--base(缺省为仓库默认分支)创建refs/heads/<branch>; - 收集暂存变更:
git diff --cached --no-renames --name-status -z HEAD解析暂存区,A/M/T状态读取git show :<path>的 base64 内容作为 additions,D作为 deletions,U(未合并)直接报错; - GraphQL 提交:调用 GitHub GraphQL
createCommitOnBranchmutation(expectedHeadOid防竞态),由 GitHub 服务器侧生成带验证签名的提交; - 同步本地:
git fetch origin <branch>后git reset --soft FETCH_HEAD把本地 checkout 同步到已签名提交,并输出新提交 oid。
脚本要求GH_TOKEN存在——这正是 Connect 代理模式下防火墙交换进占位环境的凭证;因此gh-signed-commit必须经由gh工具调用(工具的凭证租约提供该环境),而不能在普通 bash 中运行。提交成功后,技能指引 Agent 将 PR 正文写入文件并创建草稿:
gh pr create --draft --title "<title>" --body-file <path> --base <base> --head <branch>pr技能与 GitHub 指令:随配置动态切换
git__pr技能与git__github指令都是动态定义(defineDynamic),在session.started事件中按extension.config.github !== undefined决定内容(packages/eve-git/extension/skills/pr.ts、packages/eve-git/extension/instructions/github.ts):
| 能力 | Shell 模式 | Connect 模式 |
|---|---|---|
| 指令 | 使用bash中gh/gitCLI,shell 凭证认证 | 凭证对 bash 不可用,一切认证操作走gh工具 |
pr技能 | 普通commit+push,然后gh pr create --draft | 先gh-signed-commit --repo ...,再创建草稿 PR |
| 技能描述 | 无 "signed commit" 字样 | 含 "signed commit" 字样 |
集成测试 packages/eve-git/test/integration/github-guidance.test.ts 分别以extension({})与extension({ github: {...} })驱动两种模式,断言工具为null或含execute、指令文案互斥、技能 markdown 是否包含gh-signed-commit,印证了动态切换行为。
pr技能本身(packages/eve-git/extension/lib/github-guidance.ts 的prSkillMarkdown)还包含发布规范:发布前先 fetch 当前 base 并 rebase、冲突/陈旧引用/意外路径/无关回退必须停止、标题必须具体命名行为或问题(禁止prepare changes占位符)、公开元数据中不得出现密钥/私有上下文/凭证/请求者身份。PR 描述推荐按以下形状撰写(仓库有强制模板时优先模板):
[One-line summary of what changed and why] **Why** [Observed problem, evidence, and impact] **Approach** Because [cause], changing [mechanism] should [expected outcome]. **Validation** [Focused checks and results] **Relevant context** [Only the implementation or>// agent/sandbox.ts import { defineSandbox } from "eve/sandbox"; import { VercelSandbox } from "eve/sandbox/vercel"; import { installCodeTooling } from "eve/extensions/code/sandbox"; export const environment = VercelSandbox.environment({ prepare: async (sandbox) => { await installCodeTooling(sandbox, { vercel: true }); }, }); export default defineSandbox(() => environment.open());installCodeTooling(packages/eve-code/extension/lib/sandbox.ts)会安装gh(必要时通过 apt-get 安装)、gh/vc包装器、gh-signed-commit与 TypeScript 诊断 worker;其中gh-signed-commit与工具执行路径共用同一GH_SIGNED_COMMIT_SOURCE/VERSION,并通过CODE_TOOLING_REVALIDATION_KEY(eve-code-tooling:3:<version>:<typescript-version>)驱动沙箱模板重建。在 Connect 模式下,这是git__pr技能切到gh-signed-commit的前提条件。
验证与测试
@eve/git是私有包,其验证命令在 packages/eve-git/package.json 中定义:
pnpm --filter @eve/git typecheck pnpm --filter @eve/git test pnpm --filter @eve/git test:scenario测试分三个层级(均位于test/,不进入扩展分发产物):
- unit(test/unit/gh-schema.test.ts):用
z.toJSONSchema断言输入 schema 序列化为 draft-7 后permissions是长度为 1 的同构数组(无 root union),并验证恰好一个权限/一个仓库的约束; - integration(test/integration):
github-guidance.test.ts验证两种配置模式下的指令/技能/工具差异;github-shell.test.ts用 fake sandbox 全面覆盖 argv 解析、令牌签发参数、占位凭证、防火墙策略、输出脱敏、工作目录逃逸拒绝、越权仓库拒绝等安全路径; - scenario(test/scenario/manifest.test.ts):真实构建扩展包并在临时消费者应用中挂载,断言编译后 manifest 包含动态技能
git__pr、动态指令git__github、动态工具git__gh,且不包含静态工具(工具只随会话动态解析);test/scenario/tool-input-schemas.test.ts 则保证所有工具输入 schema 为 object 根(避免 root union 导致模型请求被拒)。
修改extension/源码后需重新构建 eve(pnpm --filter eve build)以同步packages/eve/src/extensions/git/extension;包内依赖说明详见 packages/eve-git/AGENTS.md。
总结:两种模式如何选型
- Shell 凭证模式(
git({})):沙箱本身已配置 GitHub 凭证、无需最小化凭证暴露的场景。实现最简,但凭证会整体暴露给沙箱进程,且无按仓库的细粒度授权。 - Connect 代理模式(
git({ github })):需要"最小权限、每次命令仅一个仓库、真实凭证不落沙箱"的场景。配合installCodeTooling与gh-signed-commit,还能覆盖要求验证签名提交的仓库。代价是必须实现broker回调,并在沙箱 provider 上维护网络策略(如setNetworkPolicy)。
两种模式共享同一套安全价值观:命令只允许 argv 语法、目标仓库必须显式声明且归属配置组织、权限必须 write、输出脱敏、凭证租约按会话串行。这套机制让 Agent 的 GitHub 操作既可审计又可在失败时安全关闭,是 eve 框架中"Agent 访问外部服务"的参考实现。
- 人工智能
- AI Agent
- Agent 框架
- 后端
- 工具调用
【免费下载链接】eve
The Open Framework for Building Agents
相关推荐
eve Agent 的行为契约:深入剖析 eve Chat Template 的 agent/instructions.md
eve Agent 的行为契约:深入剖析 eve Chat Template 的 agent/instructions.md 本篇以 eve 开源仓库中 eve
人工智能AI AgentAgent 框架后端工具调用TypeGraphQL 安装与 TypeScript 环境配置指南:依赖安装、tsconfig 与前端 Shim 全解
TypeGraphQL 安装与 TypeScript 环境配置指南:依赖安装、tsconfig 与前端 Shim 全解 本篇指南聚焦于 type graphql
人工智能AI AgentAgent 框架后端工具调用Uni-MoE模型卡片v1.5:更新日志与性能提升对比
Uni MoE模型卡片v1.5:更新日志与性能提升对比 你是否在处理多模态数据时遇到模型效率低下的问题?是否在平衡性能与资源消耗时倍感困扰?Uni MoE v1
人工智能AI AgentAgent 框架后端工具调用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考