Composio CLI 命令设计规范:输出流契约、交互策略与退出码的完整工程指南
2026/9/10 12:43:15 网站建设 项目流程

Composio CLI 命令设计规范:输出流契约、交互策略与退出码的完整工程指南

【免费下载链接】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

本篇指南基于 Composio 仓库中.agents/skills/cli-command/references/design.md编写,系统讲解@composio/cli命令面的设计准则:stdout/stderr 双流契约、ui.output()的机器输出边界、TTY 感知的交互策略、环境变量配置体系与退出码语义。读完本文,你将掌握在 Composio 中新增或审查 CLI 命令时应当遵循的完整设计规范,并能对照 ts/packages/cli 的源码理解每条规则背后的实现原理。

一、设计原则:把"人看的"和"机器读的"彻底分开

Composio CLI 的第一条设计原则,是 Unix 传统中"数据与装饰分离"的严格化:stdout 只承载数据,stderr 只承载人类可读的装饰信息。这是整个命令面设计的基石,其余规则几乎都由此派生。

具体落到实现上,规范要求:

  • ui.output()是唯一的机器数据出口——只有脚本需要捕获的值(API Key、版本号、工具列表等)才通过它写入 stdout;
  • 保持 quiet / piped 模式干净——当 stdout 被重定向到管道或文件时,终端上不能出现任何多余字符污染数据;
  • 优先使用 flags 而非模糊的位置参数——命令的可读性与自文档性优先;
  • 统一使用约定俗成的 flags——当命令形态需要时,一致地使用--json--dry-run--force--no-input--no-browser,避免每个命令各自发明一套;
  • 绝不通过 flags 接收 secrets——API Key 等敏感信息不能出现在命令行参数中(进程列表可见),只能走交互输入或环境变量。

在 ts/packages/cli/AGENTS.md 的 "Output Conventions" 一节中,这一原则被进一步细化为三条独立契约:canPromptstdin.isTTY && stderr.isTTY)、机器输出(!stdout.isTTY)与canDecoratestderr.isTTY)。三者的关键约束是:管道(piping)永远不能改变提示或认证行为——composio login | tee应当与人工登录完全等价;同样,重定向 stdin 或 stderr 也绝不能导致数据泄漏到可见的 stdout 终端上。

二、帮助与错误:把失败也当作产品体验的一部分

规范对帮助文本与错误处理提出了明确的要求:

帮助文本就是用户体验(Help text is user experience)。每个命令的帮助应当以简洁的描述开头,并紧跟常见用法示例,让用户在不读源码的情况下就能完成 90% 的操作。

预期错误要给出下一步行动。当用户输错了 flag、缺少参数或鉴权失败时,错误信息不仅要说明"发生了什么",还要告诉用户"下一个命令或修复方法是什么"。例如在 cli-main.ts 中,当出现ValidationError时,CLI 会解析出"Received unknown argument"之类的具体错误,并追加诸如Tip: --xxx requires a value, e.g. --xxx "value"的修复提示,同时打印对应命令的帮助文本。

意外错误要保留调试细节。未预期的异常不应当被吞掉或只显示一行笼统信息,而是通过仓库自有的effect-errors/机制(source-mapped 堆栈、Effect span 时间线、格式化输出)呈现,方便上报与定位。在 cli-main.ts 中可以看到,最终兜底的Effect.catchAll会调用captureErrors/prettyPrintFromCapturedErrors输出错误详情,并设置退出码 1。

三、交互性:只在人类在场时提问

Composio CLI 的交互策略是"严格 TTY 感知"的:

  • 统一使用@clack/prompts,且必须经过现有的 CLI UI 抽象层——即TerminalUI服务(src/services/terminal-ui.ts),而不是在命令里直接调用 Clack;
  • 仅当 stdin 是 TTY 时才发起提示——自动化环境(CI、管道、脚本)没有键盘,也不该被挂起;
  • 非交互模式应当以可操作的错误信息失败,而不是挂起等待——--no-input或非 TTY 场景下,命令要么使用默认值继续,要么明确报错并给出修复路径。

在 terminal-ui.ts 中,TerminalCapabilitiescanPrompt定义为stdinIsTTY && stderrIsTTY:stdin 必须能接收输入,同时 stderr 必须能显示 Clack 提示框。值得注意的是,stdout 是否 TTY 完全不参与提示决策——这保证了composio login | tee场景下提示与认证流程不变。当提示不可用时,ui.confirm直接返回defaultValue(默认true),ui.select返回第一个选项的值,保证脚本流程不被阻塞。

四、配置与状态:环境变量、用户态与项目作用域三层体系

规范明确了 CLI 的配置读取与状态存放规则,这是最容易踩坑的部分:

4.1 环境变量:COMPOSIO_前缀与豁免例外

运行时配置通过effect/Config从环境变量读取,相关代码在 src/services/config.ts 与 src/cli-config.ts:

  • 常规键采用大写蛇形命名(upper snake case)并以COMPOSIO_为前缀,读取时前缀会被剥离。例如设置COMPOSIO_USER_API_KEY=xxx,代码中通过Config.string('USER_API_KEY')读取;
  • 例外情况DEBUG_OVERRIDE_*FORCE_*两类变量原样读取、不做前缀映射(见 src/constants.ts 中的APP_ENV_CONFIG_KEY_PREFIXDEBUG_OVERRIDE_ENV_CONFIG_KEY_PREFIX,以及 config.ts 的extendConfigProvider实现)。

这种"前缀剥离"设计的好处是:代码内部只关心短键名,而环境变量命名空间由COMPOSIO_统一隔离,避免与用户机器的其他环境变量冲突。

4.2 用户态:~/.composio/user-config.json

持久化的用户 / 认证状态存放在~/.composio/user-config.json(对应 constants.ts 中的USER_CONFIG_FILE_NAME,其值来自@composio/coreUSER_DATA_FILE_NAME)。

关键的合并规则是:ComposioUserContext服务会把环境变量叠加在存储文件之上,且环境变量优先——同一个键,只要环境变量存在,就以环境变量为准覆盖文件中的旧值。这样既支持纯环境变量驱动的无状态部署,也支持交互式登录后的持久化复用。用户态的实现位于 src/services/user-context.ts,API Key 还会优先写入系统 keyring(macOS Keychain / Linux Secret Service,经由@composio/cli-keyring),仅在 keyring 不可用时回退为明文存储。

4.3 项目作用域:项目本地.composio/目录

与全局用户态相对,项目级数据(project-scoped data)使用项目本地的.composio/目录(constants.ts 中PROJECT_COMPOSIO_DIR = '.composio'),其中包含project.json项目配置与.env项目级环境覆盖。这套"全局用户态 + 项目本地态"的分层,让同一个 CLI 可以在多个项目间切换而不互相污染。

五、退出码:只有 0 和 1,中断不算失败

退出码语义是脚本化集成的契约,规范给出的规则非常明确:

  • 成功退出0,失败退出非零(1
  • 中断(interrupt,如 Ctrl+C 触发的信号)不视为失败——被中断的命令不应被当作错误处理,也不应污染后续的自动化判断;
  • 不存在独立的"无效用法(invalid usage)"退出码——不要自行发明2之类的特殊码。

上述逻辑落在 src/cli-main.ts 的teardown中:当Exit是失败且Cause不仅仅是中断时,才返回1,否则返回0;同时尊重composio run这类代理进程通过process.exitCode上报的状态。整个 CLI 通过BunRuntime.runMain({ teardown })接入该清理逻辑。

这条规则的意义在于:任何依赖退出码做条件判断的脚本,都可以放心地依据0/ 非0二值语义编写,无需区分"用法错误"与"运行失败"。

六、源码佐证:从规范到实现的关键路径

为了便于在仓库中按图索骥,下表汇总了上述规范对应的核心实现文件:

规范主题核心实现
命令面与整体架构ts/packages/cli/AGENTS.md(含完整命令清单与分层结构)
输出流契约 / TerminalUIsrc/services/terminal-ui.ts(TerminalCapabilitiesui.output()stdoutIsTTY判断)
环境变量配置读取src/services/config.ts(ConfigProvider.fromEnv+ 前缀映射)、src/cli-config.ts(showBuiltIns: falseisCaseSensitive: true
前缀与常量定义src/constants.ts(COMPOSIO_/DEBUG_OVERRIDE_前缀、缓存文件名、项目目录)
退出码 / 中断语义src/cli-main.ts(teardownBunRuntime.runMain、错误兜底输出)
用户态合并src/services/user-context.ts(ComposioUserContext、keyring 回退)
引导装配src/bin.ts(Effect 层组合与根命令启动)

此外,规范指向两份辅助材料:命令的实现范式(Command.make+Effect.gen、Effect 平台边界、必跑的pnpm typecheckpnpm --filter @composio/cli test)见 .agents/skills/cli-command/references/implementation.md;而 Effect 与 Clack 的只读源码位于 ts/vendor 目录,供实现时对照查阅。

七、实战建议:新增命令时的自检清单

将本规范浓缩为一份可执行的检查清单,供在ts/packages/cli/src/commands/下新增命令或审查既有命令时逐条核对:

  1. 数据出口:该命令是否产生脚本应捕获的值?是 → 用ui.output(value)写 stdout,配合ui.log.*/ui.note()做 stderr 装饰;否 → 只做装饰,不写 stdout;
  2. 流契约:绝不向 stderr 写数据、向 stdout 写装饰;绝不根据 stdout 的 TTY 状态分支程序行为(如认证路径);
  3. flags 一致性:需要时使用--json/--dry-run/--force/--no-input/--no-browser,避免自定义别名;
  4. secrets:API Key 等敏感输入走环境变量或交互提示,绝不通过 flag 传递;
  5. 帮助文本:以简洁描述 + 常见示例开头;
  6. 错误处理:预期错误给出"发生了什么 + 下一步命令";意外错误保留调试细节;
  7. 交互:提示必须经由TerminalUI抽象;非 TTY 下不挂起,回退默认值或报可操作错误;
  8. 配置:运行时配置走effect/Config+COMPOSIO_前缀;用户态存~/.composio/user-config.json;项目态存项目本地.composio/
  9. 退出码:成功0、失败1,中断不算失败,不引入额外的"无效用法"码。

遵循这份规范,Composio CLI 的每个命令都能同时服务好两类读者:终端前的人类用户与管道另一端的脚本——这也是该设计文档作为cli-commandskill 核心参考(见 .agents/skills/cli-command/SKILL.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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询