Composio CLIversion命令端到端测试全解析stdout/stderr 契约与--check更新状态机【免费下载链接】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 CLI 的composio version命令看似简单实则承载着一项重要工程约束装饰输出stderr与数据输出stdout严格分离。本文以仓库中 ts/e2e-tests/cli/version/README.md 描述的端到端测试套件为主线深入源码剖析版本号如何解析、为何管道重定向时 stderr 必须为空以及composio version --check如何输出确定性 JSON 状态。读完本文你将理解该命令的完整行为契约并掌握其背后的实现原理与测试验证方法。一、测试套件定位为什么专门为一个 version 命令写 e2e 测试在大多数 CLI 项目中--version只是一个打印字符串的简单分支。但 Composio 的 CLI 遵循装饰stderr与数据stdout分离的设计原则任何被管道、重定向或下游脚本消费的输出都必须是纯净、无装饰、机器可读的。version命令恰好是验证这一契约的最小载体——它输出数据量极少任何装饰泄漏都极易被发现。这套测试的目的在 README.md 中写得很明确composio version以退出码 0 正常退出stdout 内容与packages/cli/package.json中的版本号一致stderr 为空管道场景下无装饰泄漏stdout 重定向到文件时文件内捕获到干净的版本字符串composio version --check对已知更新与未知发布状态分别输出确定性 JSON。二、运行方式与隔离工具该套件不依赖任何环境变量README 明确注明 This suite does not require any environment variables因此可以无前置配置直接运行。运行命令在仓库根目录或套件目录下均可pnpm test:e2e:cli也可以进入套件目录单独执行cd ts/e2e-tests/cli/version pnpm test:e2e:cli对应的脚本定义在 ts/e2e-tests/cli/version/package.json 中bun test e2e.test.ts。隔离工具Docker。根据 ts/e2e-tests/cli/README.md 的说明每个测试套件都会在一个由Dockerfile.cli构建的 scratch Debian 容器中运行composio二进制该二进制在 Docker 镜像构建阶段通过bun build --compile编译产出自包含、无运行时依赖的可执行文件。测试通过runCmd在容器内执行 shell 命令并对退出码、stdout、stderr 进行断言。这是保证测试与宿主环境完全隔离、结果可复现的关键。三、测试覆盖矩阵六个维度的行为断言README.md 给出了清晰的测试矩阵实际断言分布在 ts/e2e-tests/cli/version/e2e.test.ts 中测试维度断言内容退出码composio version返回 0stdout输出与package.json的版本号完全一致stderr管道场景下为空无装饰泄漏文件重定向composio version out.txt把版本号完整写入文件更新可用version --check报告有更新的缓存稳定版本未知状态version --check不会把失败的刷新误报为已是最新测试以cli: [current]指定使用当前 monorepo 构建的 CLI 版本而非线上发布版确保测试的是仓库源码本身的产物。3.1 基础断言退出码、stdout、stderrbeforeAll阶段执行runCmd(composio version)随后三组断言逐一验证it(exits successfully, () { expect(versionResult.exitCode).toBe(0); }); it(stdout matches snapshot, () { expect(sanitizeOutput(versionResult.stdout)).toBe(expectedVersion); }); it(stderr matches snapshot, () { expect(versionResult.stderr).toBe(); });其中expectedVersion直接读取packages/cli/package.json的version字段String(cliPkg.version ?? ).trim()即测试的期望值来自仓库自身而非硬编码——版本升级后测试依然自洽。sanitizeOutput负责清洗可能的结尾换行等环境差异。3.2 重定向断言stdout 为空、文件里才有数据当 stdout 被重定向到文件时正确的行为是命令退出码仍为 0stdout 本身为空数据去了文件里stderr 也为空没有杂音泄漏到终端out.txt的内容与期望版本完全一致。这正是装饰与数据分离契约的落地验证任何把欢迎语、进度条、彩色文本写到 stdout 的实现都会在这里被测试捕获。四、源码纵深版本号从哪来理解测试的期望值需要看版本号的解析链路。测试读取的是packages/cli/package.json的version字段而源码中的 ts/packages/cli/src/effects/version.ts 提供了getVersionexport const getVersion Effect.flatMap( DEBUG_OVERRIDE_CONFIG.VERSION, Option.match({ onNone: () resolveRunningCliVersion(process.execPath, constants.APP_VERSION), onSome: version Effect.succeed(version), }) );即优先使用调试覆盖配置DEBUG_OVERRIDE_CONFIG.VERSION否则调用resolveRunningCliVersion解析正在运行的可执行文件自身的版本。关键在于 ts/packages/cli/src/constants.ts 中的APP_VERSIONexport const APP_VERSION typeof __COMPOSIO_CLI_RELEASE_VERSION__ undefined ? pkg.version : __COMPOSIO_CLI_RELEASE_VERSION__;源码注释明确了两点发布构建会用编译期的__COMPOSIO_CLI_RELEASE_VERSION__常量替换为 GitHub release 的确切版本号私有包的package.json版本仅是源码/开发环境的回退值绝不能用于驱动二进制发布版本选择。因此测试以package.json版本为期望值本质上是验证开发/本地构建语境下version输出与包元数据一致这一契约。测试通过resolveRunningCliVersion(process.execPath, ...)从实际运行的可执行文件读取版本保证断言对象是真实运行的二进制而非某个硬编码字符串。五、composio version --check确定性 JSON 与更新状态机5.1 命令实现ts/packages/cli/src/commands/version.cmd.ts 使用 Effect 的 CLI 框架定义命令与--check布尔标志const check Flag.boolean(check).pipe( Flag.withDefault(false), Flag.withDescription( Check for a newer stable release and print a machine-readable JSON status ({current, latestStable, updateAvailable, checkStatus, lastChecked}). Refreshes the release cache when it is older than 24 hours. ) );当--check被指定时处理逻辑为调用getUpdateStatus获取状态若updateAvailable latestStable打印提示Update available: current → latestStable — run composio upgrade带颜色装饰若checkStatus unknown打印警告Unable to determine the latest stable Composio CLI release.否则打印current is up to date.最后始终执行ui.output(JSON.stringify(status))把机器可读的 JSON 写到 stdout。注意这里的双层输出人类可读的提示走ui.logstderr 装饰通道确定性 JSON 走ui.outputstdout 数据通道——这正是前面装饰与数据分离在命令实现层面的体现。非--check分支则简单得多getVersion拿到版本号后同样同时调用ui.log.info(version)与ui.output(version)。5.2 更新状态机UpdateStatus状态机的核心类型定义在 ts/packages/cli/src/services/update-check.tsexport interface UpdateStatus { current: string; latestStable: string | null; updateAvailable: boolean; checkStatus: up-to-date | update-available | unknown; lastChecked: string | null; }生成逻辑getUpdateStatuslatestStable仅在缓存版本是合法 semver 且非预发布semver.prerelease(...) null时才有值updateAvailable要求latestStable严格大于currentsemver.gtcheckStatus三分支有更新 →update-available刷新失败refreshFailed或当前/最新版本无法解析 →unknown否则 →up-to-date。5.3 测试如何构造两种确定性场景e2e 测试通过预写缓存文件的方式在容器内人为构造确定性状态避免依赖网络场景一已知更新。测试先写入.composio/update-check.jsonmkdir -p .composio printf %s {lastChecked:2099-01-01T00:00:00.000Z,latestVersion:99.0.0} .composio/update-check.json HOME$PWD composio version --checkHOME$PWD让 CLI 在容器内当前目录下找缓存文件。随后断言 stdout 的 JSON 恰好为{ current: 当前版本, latestStable: 99.0.0, updateAvailable: true, checkStatus: update-available, lastChecked: 2099-01-01T00:00:00.000Z }场景二未知状态。测试改而写入.composio/update-check.json.attempt记录上次尝试刷新失败的独立文件注意后缀.attemptmkdir -p .composio printf %s {lastAttempted:2099-01-01T00:00:00.000Z} .composio/update-check.json.attempt HOME$PWD composio version --check此时没有成功缓存且最近一次尝试失败checkStatus应为unknown断言{ current: 当前版本, latestStable: null, updateAvailable: false, checkStatus: unknown, lastChecked: null }这两个场景共同验证了状态机的关键语义未知 ≠ 最新——刷新失败时必须诚实上报unknown绝不能把失败误报为 up to date。5.4 状态文件与刷新节流从源码看更新检查的后台机制checkForUpdate与--check共享同一套缓存逻辑状态文件默认位于~/.composio/update-check.jsondefaultStateFile拼接os.homedir与.composio/update-check.json刷新节流间隔为CHECK_INTERVAL_MS 24 * 60 * 60 * 100024 小时update-check.ts缓存命中判定距上次成功检查不足 24 小时则直接复用缓存失败重试判定若距上次尝试不足 24 小时且失败尝试晚于最后成功检查则跳过刷新并标记refreshFailed解析逻辑parseLatestVersionFromReleases只认composio/clisemver形式的 release tag、排除 prerelease/draft并且要求 release 包含当前平台的二进制资产composio-platform-arch.zip保证可用更新是真实可安装的请求使用AbortSignal.timeout(10_000)兜底任何 fetch/解析/写入错误都被吞掉Effect.ignore确保后台检查永远不阻塞主命令。六、设计启示可被脚本安全消费的 CLI 输出综合 README 与源码这套测试背后是一套可复用的 CLI 输出设计准则stdout 只承载数据。无论是version的纯版本号还是--check的 JSON凡是被管道/重定向消费的内容都保持零装饰。stderr 承载人类提示。更新提示、警告、颜色装饰一律走 stderrshowUpdateNotice还通过terminal.capabilities.canDecorate判断是否非 TTY进一步避免装饰泄漏。机器可读输出必须确定性。--check的 JSON 字段current、latestStable、updateAvailable、checkStatus、lastChecked结构稳定便于脚本解析与断言。诚实的状态语义。刷新失败显式报告unknown而不是默认已是最新避免误导用户错失升级。测试与实现同源。期望版本号来自packages/cli/package.json测试在 Docker 容器中运行当前构建产物既验证了行为契约又对版本迭代天然免疫。七、进一步探索命令实现ts/packages/cli/src/commands/version.cmd.ts版本解析ts/packages/cli/src/effects/version.ts更新状态机ts/packages/cli/src/services/update-check.ts版本常量与发布注入ts/packages/cli/src/constants.tse2e 测试源码ts/e2e-tests/cli/version/e2e.test.ts套件说明与运行方式ts/e2e-tests/cli/README.md如需在本地复现直接运行pnpm test:e2e:cli即可该套件不要求任何环境变量只需 Docker 可用。【免费下载链接】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),仅供参考