拓冰建站拓冰建站
首页 / 资讯中心 / 正文

识别 AI Coding Agent:解析 Cypress `@packages/agent-info` 环境指纹检测的实现与扩展

识别 AI Coding Agent解析 Cypresspackages/agent-info环境指纹检测的实现与扩展【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypresspackages/agent-info是 Cypress 仓库中一个刻意保持小而纯的内部包它只回答一个问题当前这个 Cypress 进程是否由 AI 编程代理AI coding agent调用以及具体是哪一个。它通过比对进程环境块中的特征变量如CLAUDECODE、GEMINI_CLI、CURSOR_AGENT返回一个来自封闭集合的固定名称claude、codex、cursor等。本文以 AGENTS.md 为主线结合 lib/index.ts、测试用例与 CLI 消费端完整讲解其设计动机、源码结构、检测规则、调用方式以及如何新增一个 Agent的操作指南供需要在自有工具链中实现或扩展 Agent 指纹识别的开发者参考。为什么要单独做一个Agent 识别包随着 Claude Code、Codex、Cursor 等 AI 编程代理被越来越多人用来直接驱动命令行工具Cypress 这类 CLI 工具需要回答两个问题当前进程是不是某个 Agent 派生的子进程如果是它来自哪个 AgentAgent 自身会通过环境变量暴露身份——CLAUDECODE、GEMINI_CLI、CURSOR_AGENT、AI_AGENT等等。agent-info做的事情就是对环境做指纹比对把环境对象与一张标记变量表detection table做匹配输出一个固定名称。关键约束在于输出值会被上报离开用户机器CLI 会将其作为遥测数据的一部分发送所以返回值必须来自封闭集合而不是环境里任意字符串的原样透传——这正是本包存在的核心价值。AGENTS.md 的第一段即明确了这一点It fingerprints the environment block against a table of marker variables and returns a fixed name from a closed set.设计基石纯函数、零依赖的 TypeScript该包被刻意设计为纯净、无运行时依赖的 TypeScript不碰fs、不碰http无任何运行时依赖package.json的dependencies为空仅含 devDependencies不读取除被传入的环境对象之外的任何东西。正因为如此Node 侧的各个包以及cypressCLI 才能放心地把它打进 bundle而不会引入新的运行时依赖。从 package.json 可以看到包入口为dist/index.js构建产物仅含dist目录类型声明由dist/index.d.ts提供。整体架构一个文件承载全部逻辑AGENTS.md 的 Architecture 一节指出整个包就是 lib/index.ts 一个文件它包含三大部分组成部分作用AgentName联合类型可返回的封闭名称集合AGENTS检测表名称 → 一组环境检查条件的映射detectAgent()/isAgent()对外入口扫描环境并给出结论配套的还有 lib/spec/index.spec.ts 中的 Vitest 测试以及面向使用者的 README.md其同时被 CLAUDE.md 以AGENTS.md方式引用仓库内约定以 AGENTS 文档为单一事实来源。AgentName封闭的名称集合源码第 1–14 行定义了AgentNameexport type AgentName | auggie | claude | codex | cursor | devin | gemini | goose | junie | kiro | opencode | other | pi | replitdetectAgent()只可能返回这些名称之一或undefined。其中other是兜底值当通用变量AI_AGENT存在但表内无法识别时返回代表确实有 Agent但无法命名。AGENTS检测表字符串标记与函数式检查EnvCheck支持两种检查方式源码第 16–48 行裸环境变量名字符串只要该变量存在且有值即为命中如[claude, [CLAUDECODE, CLAUDE_CODE]]envMatcher(key, regex)函数变量存在不足以说明问题、还需校验取值格式时使用例如pi需要检查PATH中是否含\.pi[\\/]agent模式、devin需要检查EDITOR是否以(^|[\\/])devin(\.exe)?$结尾、kiro需要在无 TTY 前提下检查TERM_PROGRAM是否匹配/kiro/。整张表含所有分支变量如下Agent环境标记 / 检查条件claudeCLAUDECODE、CLAUDE_CODEreplitREPL_IDgeminiGEMINI_CLIcodexCODEX_SANDBOX、CODEX_THREAD_IDopencodeOPENCODEpienvMatcher(PATH, /\.pi[\\/]agent/)auggieAUGMENT_AGENTgooseGOOSE_PROVIDERjunieJUNIE_DATA、JUNIE_SHIM_PATHdevinenvMatcher(EDITOR, /(^\|[\\/])devin(\.exe)?$/)cursorCURSOR_AGENTkiroenvMatcher(TERM_PROGRAM, /kiro/, { noTTY: true })表序是有意设计的IDE如 Cursor排在最后以确保运行在 IDE 内部的 Agent 被识别为 Agent 而非 IDE——因为同一环境里可能同时出现CURSOR_AGENT与CLAUDECODE先命中claude行便立即返回这由测试reports the agent rather than the IDE hosting it明确验证。detectAgent()与isAgent()两个入口export const detectAgent (env: NodeJS.ProcessEnv process.env): AgentName | undefined { for (const [name, checks] of AGENTS) { for (const check of checks) { if (typeof check string ? env[check] : check(env)) { return name } } } return env.AI_AGENT ? fromAiAgent(env.AI_AGENT) : undefined } export const isAgent (env: NodeJS.ProcessEnv process.env): boolean { return !!detectAgent(env) }逻辑非常直接先顺序遍历AGENTS表每个名称下依次尝试各组检查字符串标记做存在性判断函数标记执行匹配全部未命中后再看通用的AI_AGENT变量。两入口均接受可选参数env默认为process.env。fromAiAgent()把自由文本收窄成固定名AI_AGENT是自由格式且常带版本号——例如 Claude Code 会设置形如claude-code_2-1-221_agent的值。源码第 55–61 行先转小写再用正则^${name}($|[^a-z0-9])从已知名称中寻找前缀命中const fromAiAgent (value: string): AgentName { const normalized value.toLowerCase() // The name has to end where it ends, so a short one like pi cannot claim an // unrelated pipecat. return KNOWN_NAMES.find((name) new RegExp(^${name}($|[^a-z0-9])).test(normalized)) ?? other }这个边界正则防止了短名称误伤pi不会认领不相关的pipecatpipecat中pi之后是p属于[a-z0-9]不满足$|[^a-z0-9]。该行为由测试reports a value that only begins with a known name as other覆盖。API 使用方式README.md 给出两个入口的完整说明可直接在 Node/TS 环境中使用import { detectAgent } from packages/agent-info detectAgent() // 在 Claude Code 下运行 → claude detectAgent({ GEMINI_CLI: 1 }) // gemini detectAgent({}) // undefined未检测到任何 AgentdetectAgent(env?: NodeJS.ProcessEnv): AgentName | undefined检查传入环境默认process.env返回命中的AgentName无任何 Agent 时返回undefinedisAgent(env?: NodeJS.ProcessEnv): boolean仅返回布尔值便于快速分支判断import { isAgent } from packages/agent-info if (isAgent()) { // 运行在某个 Agent 之下无论是否被具名识别 }由于包自身是纯净的你甚至可以传入任意手工构造的环境对象做单元测试或离线演练而完全不需要真的启动一个 Agent。CLI 中的实际消费点遥测事件里的agent字段agent-info的真实消费者是cypressCLI。在 cli/package.json 中它以 workspace 依赖packages/agent-info: 0.0.0-development被引用检测结果在 cli/lib/tap/events.ts 中进入上报事件 payloadconst payload { command: trace.command, flags: trace.flags.slice(0, MAX_REPORTED_FLAGS), agent: detectAgent(), sessionId: identity?.sessionId ?? undefined, userId: identity?.userId ?? undefined, exitCode, errorCode: trace.errorCode, durationMs: Date.now() - trace.startedAt, }随后该 payload 通过 fetch POST 发送。这解释了 AGENTS.md 反复强调的一条铁律只有固定名称允许离开机器Only fixed names leave the machine。AI_AGENT被收窄成已知名或other后才可能被上报绝不会把未经审查的环境字符串原样透传出去——否则一次简单的环境探测就可能泄露用户环境里的敏感值。扩展指南如何新增一个 AgentAGENTS.md 明确给出新增 Agent 的标准流程三处改动全部位于 lib/index.ts 及其 spec 中第 1 步把名称加进AgentName联合类型保持字母序export type AgentName | auggie // ... 在对应字母位置插入新名称保持字母序 | myagent第 2 步在AGENTS表中新增一行const AGENTS: readonly (readonly [AgentName, readonly EnvCheck[]])[] [ // ... [myagent, [MY_AGENT_VAR]], ]选择判据若某个变量存在即足以证明例如该变量仅由对应 Agent 设置直接用裸变量名若变量在许多场景都会出现、仅靠存在性不足为凭则用envMatcher(key, regex)校验取值。例如pi之所以用envMatcher(PATH, /\.pi[\\/]agent/)正是因为普通用户机器上也可能出现含.pi的路径kiro用envMatcher(TERM_PROGRAM, /kiro/, { noTTY: true })则是因为需要叠加 TTY 门控。第 3 步在 lib/spec/index.spec.ts 的detects %s表格里新增一行it.each([ // ... [myagent, { MY_AGENT_VAR: 1 }], ])(detects %s, (name, env) { expect(detectAgent(env)).toBe(name) expect(isAgent(env)).toBe(true) })每次检测逻辑的调整都伴随 spec 更新保证表格与联合类型、测试三者同步演化。工程细节与设计陷阱GotchasAGENTS.md 记录了若干经过真实权衡得出的工程约定直接决定了实现的形态1. 表序即优先级IDE 必须排在最后IDE 环境变量如CURSOR_AGENT会先于 IDE 内部的 Agent 变量被扫描的风险因此实现把 IDE 相关的行放在表尾。意图是在 IDE 内运行的 Agent应被上报为 Agent 本身而非承载它的 IDE。测试detectAgent({ CURSOR_AGENT: 1, CLAUDECODE: 1 })期望返回claude正是这一约定的回归保障。2. 宁可用锚定的具体标记也不用子串匹配对路径类变量做正则时若匹配可发生在任意位置则可能误伤——某位用户的主目录恰好以某个 Agent 命名就会被错判。因此必须锚定在路径分隔符和字符串结尾上pi的正则用\.pi[\\/]agent要求.pi后紧跟分隔符与agentdevin的正则(^|[\\/])devin(\.exe)?$要求匹配到行尾。测试does not match devin in the path of an unrelated editor验证了/home/devin/.local/bin/vim不会被误判为 devin。同时两个正则都使用[\\/]兼容 Windows 反斜杠路径对应 Windows 风格 PATH/EDITOR 的两个测试用例。3. TTY 门控检测默认偏向人类某些环境变量同时被IDE 的集成终端和它的 CLI Agent设置。此时若该进程的 stdin 或 stdout 连着一个 TTY基本可以断定有人在终端前操作而不是 Agent 派生的子进程。因此envMatcher(..., { noTTY: true })会在任一端口为 TTY 时跳过该检查——两端都要查因为重定向其一仍会留下另一个典型如cypress run | tee log.txtstdout 被管道占用的场景。其背后的原则是把人类误标成 Agent比漏掉一个 Agent 更糟糕。测试套件通过操作process.stdout/process.stdin的isTTY描述符来验证TERM_PROGRAM: kiro在挂上 stdout TTY 或仅 stdin 为 TTY输出被管道化时都不再命中但同时给出CLAUDECODE: 1时仍会识别为claude因为显式的强标记不受 TTY 门控影响。4. 空值语义变量存在但为空 未设置源码第 30 行return value ? regex.test(value) : false字符串分支也只对 truthy 值命中。测试ignores an env var that is set but empty{ CLAUDECODE: }确认空字符串不会触发检测——这是对环境变量被设置成空串是常见 shell 状态的防御。构建、测试与运行环境约束在仓库根目录下执行以下命令可构建与测试该包# 用 tsc 编译 TypeScript 到 dist/ yarn workspace packages/agent-info build # 运行测试Vitest yarn workspace packages/agent-info test补充说明build脚本实际为yarn clean tsc见 package.jsondist/由tsc生成禁止手改AGENTS.md 明确提示。该包没有独立的lint外的运行时脚本依赖日常开发还可使用watchtsc --watch增量编译。运行环境约束由于该包被cypressCLI 消费而 CLI 运行在用户自己的 Node上因此兼容范围受 cli/package.json 中engines.node约束当前为^22.0.0 || ^24.0.0 || 26.0.0——这是一个比开发或打包 Node 版本更低的下限要求意味着代码里不能使用超出该下限的 Node 新特性。这与包的零运行时依赖 保守语法取向一致确保它无论被加载到哪一档受支持的 Node 都能正常工作。测试覆盖一览lib/spec/index.spec.ts 用 Vitest 系统化地验证了本包的全部行为契约测试主题覆盖点空环境detectAgent({})为undefined、isAgent({})为falsedetects %s参数化表格14 组 Agent × 环境变量组合全部命中Windows 风格路径piPATH含反斜杠.pi\agent与devindevin.exe空字符串变量存在但为空不触发检测优先级同时出现CURSOR_AGENT与CLAUDECODE时返回claudeAgent 胜过 IDE误判防御/home/devin/.local/bin/vim不命中devinTTY 门控stdout 为 TTY 时不识别kiro仅 stdin 为 TTY输出被管道化时同样不识别显式标记CLAUDECODE不受影响AI_AGENT带版本号值收窄为已知名大小写不敏感未知值返回other而非原样pipecat不因前缀pi误判表内显式变量优先于AI_AGENT默认参数未传env时读取process.envvi.stubEnv模拟这些测试同时充当检测表语义的可执行文档——新增或调整 Agent 检测规则时先改 spec、再改实现是仓库推荐的开发顺序。小结packages/agent-info以约 77 行源码回答了一个看似简单实则布满陷阱的问题进程是否由 AI 编程代理启动、由谁启动。它的方法论可以提炼为可复用的四条经验输出必须来自封闭集合以保护隐私上报识别依据应优先使用锚定的具体标记而非宽泛子串表序即优先级具体 Agent 优先于承载它的 IDE默认偏向人类TTY 门控只在无交互终端时放行。配合一次调用即完成的环境指纹比对与高度参数化的测试表这个模式非常适合移植到任何需要区分人与 Agent、并安全上报 Agent 身份的 CLI 或开发工具中。若需在 Cypress 中扩展对某个新 Agent 的支持只需按上文三处改动的流程在 lib/index.ts 与 index.spec.ts 中同步更新联合类型、检测表和测试行即可。【免费下载链接】cypressFast, easy and reliable testing for anything that runs in a browser.项目地址: https://gitcode.com/GitHub_Trending/cy/cypress创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门