Composio CLI `composio run` 端到端测试深度解析:退出码转发与运行日志持久性验证

发布时间:2026/11/13 6:52:11

Composio CLI `composio run` 端到端测试深度解析:退出码转发与运行日志持久性验证 Composio CLIcomposio run端到端测试深度解析退出码转发与运行日志持久性验证【免费下载链接】composioComposio powers 1000 toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composiocomposio run是 Composio CLI 中用于执行内联 TypeScript/JavaScript 代码或脚本文件、并在其中注入execute、search、proxy、experimental_subAgent等工具助手helpers的核心命令。本文以仓库中该命令的端到端测试套件 ts/e2e-tests/cli/run/README.md 为骨架结合其实现源码 run.cmd.ts 与单元测试 run.cmd.test.ts系统讲解为什么composio run需要专门的 e2e 测试、它验证哪些关键行为、底层退出码与日志文件的机制原理以及如何在本仓库中实际运行这套测试。为什么composio run需要端到端测试一个单元测试无法覆盖的退出码盲区composio run的职责是把用户脚本作为子进程拉起并把子进程的退出状态原样转发给调用者。这个转发路径是进程级process-level的命令处理器handler只是设置process.exitCode然后返回一个成功的 Effect真正的退出动作发生在 CLI 的 teardown 阶段——teardown 把该退出码交给runMain而runMain仅在退出码非零时才强制退出进程。单元测试运行在进程内in-process只能观察到process.exitCode这个变量被设置成了什么却永远无法确认这个值是否真的到达了操作系统层。这意味着存在一个真实的回归风险一次 Effect 升级或 teardown 逻辑的改动可能让所有失败的脚本都变成 exit 0而整套单元测试依然全部通过——没有任何一个 in-process 断言能捕获这种故障。e2e 测试则是直接针对真实编译后的 CLI 二进制执行composio run process.exit(7)然后用wait/$?级别的断言去检查进程的真实退出码。这是唯一能在失败脚本被静默当成成功这一回归出现时发出警报的防线。运行日志同样面临进程结束后存活问题composio run会在 stderr 上打印一行RUN_LOG_FILEpath向调用者宣告本次运行日志文件的路径。这条路径只有在文件比打印它的进程活得更久时才有意义。历史上这里就回归过一次当时运行日志存放在一个作用域临时目录scoped temp directory里目录随进程结束被清理日志文件随之消失RUN_LOG_FILE路径形同虚设。这个文件在进程退出后是否仍然存在的属性同样无法在进程内被检查必须靠 e2e 测试在 CLI 进程退出之后再去探测文件系统。测试套件总览整个测试套件位于 ts/e2e-tests/cli/run/e2e.test.ts对应的清单文档 README.md 给出了完整的测试矩阵测试描述Exit forwarding退出码转发composio run process.exit(7)以退出码 7 结束Successful run成功运行内联脚本以 0 退出且其 stdout 能到达调用者Missing script缺少脚本不带代码也不带--file时非零退出并给出明确提示Run log durability日志持久性进程退出后宣告的RUN_LOG_FILE路径仍然存在运行前置要求Requirements无。该套件不需要任何环境变量也不需要网络访问——它不依赖 API Key、不调用远端服务纯粹验证 CLI 本地的进程行为。隔离工具Isolation ToolDocker并且 CLI 必须是从当前 monorepo 源码构建的版本。端到端测试基础设施e2e-tests/utils这套测试构建在仓库共享的 e2e 基础设施之上位于 ts/e2e-tests/_utils/README.md。核心是e2e()入口函数它从调用方位置自动推断工作目录与套件名称使用bun:test作为测试框架并在隔离的 Docker 容器中执行命令。本套件用到的关键 APIrunCmd(command: string): PromiseE2ETestResult在 Docker 容器中运行任意命令返回{ exitCode, stdout, stderr }sanitizeOutput()去除 ANSI 转义码、统一换行符并裁剪空白使输出断言稳定可比TIMEOUTS.FIXTURE120 秒用于beforeAll钩子中执行真实 CLI 命令的超时上限versions: { cli: [current] }声明测试当前版本的 CLIcurrent解析为ts/packages/cli/package.json中的版本号也可通过COMPOSIO_E2E_CLI_VERSION环境变量覆盖。套件的 package.json 依赖e2e-tests/utilsworkspace 内部包并声明了test:e2e:cli脚本bun test e2e.test.ts。四项测试的代码级详解测试主体在beforeAll中一次性执行四个命令并缓存结果均在TIMEOUTS.FIXTURE超时保护下随后用describe/it分组断言。1. 失败脚本的退出码转发failingScriptResult await runCmd(composio run process.exit(7)); it(forwards the script exit status to the caller, () { expect(failingScriptResult.exitCode).toBe(7); });这是整个套件最关键的一个断言脚本主动以7退出CLI 进程本身也必须以7退出。如果前面提到的teardown 改动导致失败脚本变成 exit 0的回归发生这条用例会立刻红灯。2. 成功脚本退出码与 stdout 转发const STDOUT_MARKER composio-run-e2e-stdout-marker; successfulScriptResult await runCmd(composio run console.log(${STDOUT_MARKER})); it(exits successfully, () { expect(successfulScriptResult.exitCode).toBe(0); }); it(forwards script stdout to the caller, () { expect(sanitizeOutput(successfulScriptResult.stdout)).toContain(STDOUT_MARKER); });成功路径除了验证退出码为 0还验证了子进程的 stdout 被原样透传——调用者能收到脚本打印的内容这是composio run作为脚本运行器的基本契约。3. 缺少脚本时的失败提示missingScriptResult await runCmd(composio run); it(exits non-zero, () { expect(missingScriptResult.exitCode).not.toBe(0); }); it(explains what is missing on stderr, () { expect(sanitizeOutput(missingScriptResult.stderr)).toContain( Provide inline code or use --file to run a script file. ); }); it(writes no data to stdout, () { expect(sanitizeOutput(missingScriptResult.stdout)).toBe(); });裸执行composio run既不提供内联代码也不提供--file时CLI 必须非零退出、在 stderr 上说明缺什么、并且不向 stdout 写任何数据。对应到源码这条错误信息正是MissingRunSourceError携带的MISSING_RUN_SOURCE_MESSAGE见 run.cmd.tsProvide inline code or use --file to run a script file. composio run console.log(1) composio run --file ./script.ts源码注释明确说明这是普通的用法错误usage mistake而不是内部不变量被破坏因此实现为带一行消息的类型化失败Data.TaggedError让调用者看到的是修复指引而非堆栈。并且这个检查发生在任何准备工作之前handler 的第一件事就是检查Option.isNone(file) !args[0]见 run.cmd.ts所以裸composio run既不会创建 run-artifacts 目录也不会宣告一个永远不会被使用的日志文件。4. RUN_LOG_FILE 路径的持久性探针这一组测试用一个复合 shell 命令实现进程外探测先运行一次成功的脚本把 CLI 自身的 stderr 重定向到run-stderr.txt文件避免其混入输出然后在 CLI 进程退出后解析出RUN_LOG_FILE宣告的路径再检查该文件是否存在const runLogProbeCommand [ composio run console.log(run-log-probe) 2 run-stderr.txt, run_status$?, run_log_path$(grep ^RUN_LOG_FILE run-stderr.txt | head -n 1 | cut -d -f2-), echo RUN_STATUS$run_status, if [ -n $run_log_path ]; then echo RUN_LOG_ANNOUNCEDyes; else echo RUN_LOG_ANNOUNCEDno; fi, if [ -f $run_log_path ]; then echo RUN_LOG_EXISTSyes; else echo RUN_LOG_EXISTSno; fi, ].join(; );探针结果以KEYvalue形式输出到 stdout随后在测试侧断言it(is announced, () { expect(runLogResult.exitCode).toBe(0); expect(sanitizeOutput(runLogResult.stdout)).toContain(RUN_STATUS0); expect(sanitizeOutput(runLogResult.stdout)).toContain(RUN_LOG_ANNOUNCEDyes); }); it(still exists after the CLI process exits, () { expect(sanitizeOutput(runLogResult.stdout)).toContain(RUN_LOG_EXISTSyes); });注意这里的时序关键点RUN_LOG_EXISTSyes的检查发生在composio run这个 CLI 进程已经退出之后由外层 shell 完成——这正是日志文件必须比打印它的进程活得更久这一契约的直接验证。也正是这一点让这个探针天然无法被放进单元测试。底层实现原理退出码与日志文件的生命周期退出码如何从子进程走到操作系统从 run.cmd.ts 的实现可以看到完整链路命令处理器通过ChildProcess.make拉起子进程process.execPath --preload preloadPath ...并await child.exitCode得到脚本退出码退出码被赋值给process.exitCode exitCodehandler 返回一个成功的 Effect让 Effect 运行时不做任何强制退出CLI teardown 读取process.exitCode并交给runMainrunMain只在非零时 force-exit。这个总是返回成功 Effect 依赖 teardown 转发退出码的设计正是 README 所述盲区的根源只要某次重构把 teardown 中的runMain行为改掉例如无论什么退出码都直接正常结束所有失败脚本都会静默变成 exit 0。e2e 测试用真实的$?值来做最后一道防线。顺带一提源码还处理了一个相关细节当用户按 Ctrl-C 中断时handler 把process.exitCode设为130即 128SIGINT 的惯例值防止 wrapper 脚本和set -e把一次被取消的运行误读为成功。RUN_LOG_FILE 为什么能活过进程RUN_LOG_FILE路径由createRunHelpersPreloadFile生成见 run.cmd.ts其中体现了两种临时目录的刻意区分preload 目录使用makeTempDirectoryScoped带前缀composio-run-创建作用域化运行结束时自动删除——globals.mjs预加载文件放在这里属于临时中间产物run 输出目录运行日志与较大的工具输出需要向调用者宣告并存活因此放在非作用域的独立临时目录makeTempDirectory前缀composio-run-artifacts-日志文件路径为runOutputDir/run.log。当检测到当前处于 consumer 项目时runOutputDir还会被替换为 CLI 会话工件目录resolveCliSessionArtifacts让日志落在项目会话的正式工件目录中。命令执行前的ui.error(\RUN_LOG_FILE${preload.runLogFilePath})见 run.cmd.ts就是把这行路径打到 stderr 上供外部工具或 e2e 探针捕获。这段代码的注释也直接呼应了 README 中记载的历史回归曾有一次日志被放进作用域临时目录导致RUN_LOG_FILE路径在进程结束后失效。配套单元测试的边界仓库中的单元测试 run.cmd.test.ts 通过替换ChildProcessSpawner为 stub 句柄STUB_CHILD_PID、挂起的exitCode来测试命令构造、信号转发、wrapInlineCodeForRun包装逻辑等 in-process 行为并断言MissingRunSourceError的错误消息对应缺失脚本场景。它与 e2e 测试形成互补单元测试验证处理器把 exitCode 设置对了e2e 测试验证这个 exitCode 真的到达了操作系统。二者缺一不可。如何运行这套测试在仓库根目录或ts/e2e-tests/cli/run目录执行pnpm test:e2e:cli该命令会执行bun test e2e.test.ts。运行前提本机安装Docker测试基础设施在隔离容器中运行命令见 ts/e2e-tests/_utils/README.mdCLI 镜像需从当前 monorepo 源码构建无需任何环境变量或网络访问套件完全离线可跑。如需调整被测 CLI 版本可通过COMPOSIO_E2E_CLI_VERSION环境变量覆盖默认current解析自ts/packages/cli/package.json。延伸阅读命令实现与全部 flag 定义ts/packages/cli/src/commands/run.cmd.ts含--file/-f、--dry-run、--debug、--logs-off、--skip-connection-check、--skip-tool-params-check、--skip-checks及注入的execute/search/proxy/experimental_subAgent/z等 helpers 说明单元测试进程内行为验证ts/packages/cli/test/src/commands/run.cmd.test.tse2e 基础设施与e2e()/runCmd/sanitizeOutput/TIMEOUTSAPIts/e2e-tests/_utils/README.md本套件清单文档ts/e2e-tests/cli/run/README.md【免费下载链接】composioComposio powers 1000 toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/13 6:47:26

重庆高压电工证考证多久拿证?实操考试心里没底怕挂科

重庆高压电工证考证多久拿证?实操考试心里没底怕挂科 你是不是也这样,听说要考高压电工证,心里就开始打鼓,怕实操考试过不了?特别是像你这样的新手,对流程、考试内容、多久拿证这些都一知半解,搞不好还可能走弯路。别急,今天就从…

2026/11/13 6:47:26

兵王电工证丢了怎么补办?全国通用证书丢了别慌

兵王电工证丢了怎么补办?全国通用证书丢了别慌 怕考不过白交培训费?其实很多人在备考【兵王电工证】时最大的担心不是考试难度,而是报名后流程复杂、材料不全、甚至证书丢了怎么办。特别是这个证书【全国通用】,一旦遗失,不仅影响工作,还可能耽误项目进…

2026/11/13 6:47:26

小撬棍电工证上岗必备怎么考?三门峡从业者避坑指南

小撬棍电工证上岗必备怎么考?三门峡从业者避坑指南 你是不是也遇到过这种情况: 不知道去哪报名怕被中介坑 ?尤其是像小撬棍电工证这种和日常工作息息相关,却又让人摸不着头绪的上岗必备证件。今天就从三门峡市政公用工程从业者的角度,讲讲怎么稳妥拿到…

2026/11/13 6:47:25

中山电工证培训费用2026河南最新政策下怎么走

中山电工证培训费用2026河南最新政策下怎么走 之前考的证在外省能不能转过来?这可能是很多在中山准备考电工证或者已经考了但异地打工的电工最关心的问题。2026年河南最新政策明确规定, 电工证全国通用…

2026/11/13 6:42:25

白云景泰电工证考场在哪考 跨省转证流程全解析

白云景泰电工证考场在哪考 跨省转证流程全解析 之前考的证在外省能不能转过来?这是很多在外地工作、持有特种作业证的朋友最关心的问题。尤其是像电工作业这种岗位,很多从业者是跨省流动的,证件如果不能顺利转过来,不仅影响工作,还可能带来安全风险和法…

2026/11/12 20:35:13

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/13 0:01:06

办上海高压电工证良心建议:别被中介坑了,自己看这篇就够了

办上海高压电工证良心建议:别被中介坑了,自己看这篇就够了 你是不是也像我一样,第一次听说“高压电工证”这个名词,就感觉一头雾水?其实很多人都跟我一样,不知道去哪报名、怕被中介坑,结果花了冤枉钱还考不上。今天我就从一个在商丘培训过的学员角度,…

2026/11/13 0:01:06

黎里考电工证在哪里值不值得考

黎里考电工证在哪里值不值得考 之前考的证在外省能不能转过来?这是很多在外地工作、准备回老家发展的朋友最关心的问题。特别是在黎里地区,很多从事电力相关工作的朋友,都面临这样一个难题: 外省的电工证能否在本地使用?值不值得考?…

2026/11/13 0:01:06

毕节哪里考电工证 怎么报名学历不高也能上手

毕节哪里考电工证 怎么报名学历不高也能上手 你是不是也和我一样,学历不高,想着考个电工证,但又担心自己条件不够,怕报不上名?其实不然,只要符合条件,学历不高也能考电工证。今天就来聊聊【毕节哪里考电工证】这个问题,还有怎么报名,让你一步到位不…

2026/11/12 22:10:13

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/12 9:51:42

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑