Composio CLI Local Tools:本地工具包架构与版本演进全解析
【免费下载链接】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
<output文章>
关于 @composio/cli-local-tools
@composio/cli-local-tools是 Composio 仓库中一个专门承载「本地工具与本地工具包声明」的 TypeScript 包,位于 ts/packages/cli-local-tools。它的定位与云端托管的工具不同:它把运行在用户自己机器上(macOS、Linux、Windows)的 CLI 程序、原生二进制、MCP Server 和动态库统一声明为可被 AI Agent 调用的工具,并把这些本地工具接入 Composio CLI 的 Tool Router 搜索与执行会话。
从包描述Local tool and toolkit declarations for the Composio CLI.(见 package.json)可以看出,这个包是 CLI 侧本地工具的声明层 + 执行层:它既负责描述"这个工具长什么样、支持哪些平台、如何被调用",也负责真正把命令 spawn 出去、把 MCP Server 连起来、甚至通过 Bun FFI 加载原生动态库。
本文将以该包的 CHANGELOG.md 为主线,结合仓库源码,梳理这个包从 0.0.2 到 0.1.0 的演进脉络,深入解析其核心架构、四种执行模型、CLI 子命令与本地元数据机制。
一、版本演进总览
@composio/cli-local-tools目前的版本历史非常短,只有 5 个版本(0.0.2 → 0.1.0),但每个版本都承载着明确的架构决策。整体时间线如下:
| 版本 | 变更类型 | 核心内容 |
|---|---|---|
| 0.0.2 | Minor | 首次落地本地工具基础框架,接入 Tool Router,新增 Beeper iMessage、Chrome DevTools、Peekaboo 三个本地工具包 |
| 0.0.3 | Patch | 仅依赖升级:@composio/core@0.9.1 |
| 0.0.4 | Patch | 仅依赖升级:@composio/core@0.10.0 |
| 0.0.5 | Patch | 仅依赖升级:@composio/core@0.11.0 |
| 0.1.0 | Minor | 破坏性变更:全面迁移为 ESM-only,移除 CommonJS 入口与.cjs产物,要求 Node.js ≥ 22.22.3;依赖升级至@composio/core@0.12.0 |
从源码结构看,该包的正式功能面在 0.0.2 一次成型,后续 0.0.3~0.0.5 是跟随@composio/core的依赖演进,而 0.1.0 则是一次影响所有消费者的打包与运行时策略调整。下面逐一拆解。
二、0.1.0:ESM-only 破坏性迁移
0.1.0 是当前最新版本,也是唯一一次包含Minor Changes的破坏性变更:
Drop CommonJS entrypoints and publish the TypeScript SDK packages as ESM-only packages. This is a breaking change within the existing 0.x release line: consumers must use Node.js 22.22.3 or newer. CommonJS callers can only rely on Node's native
require(esm)interop, and the SDK no longer ships custom CommonJS compatibility machinery or.cjsartifacts.
这段描述包含三个关键决策:
- ESM-only:包不再提供 CommonJS 入口,发布产物只含 ESM 模块。
- Node 版本门槛:消费者必须使用 Node.js22.22.3 或更新版本,才能获得完整的 ESM 支持与原生
require(esm)互操作。 - 移除 CJS 兼容机制:不再内置自定义的 CommonJS 兼容层,也不再发布
.cjs产物。CommonJS 调用方只能依赖 Node 原生的require(esm)互操作能力。
这一决策在 package.json 中有完整的落地证据:
{ "name": "@composio/cli-local-tools", "version": "0.1.0", "type": "module", "main": "dist/index.mjs", "module": "dist/index.mjs", "types": "dist/index.d.mts", "exports": { ".": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" } }, "files": ["dist", "local-tools-binaries"] }可以看到:
"type": "module"明确整个包按 ESM 语义解析;- 入口统一为
dist/index.mjs(main与module指向同一文件),类型声明为.d.mts; files字段把dist与local-tools-binaries一起发布——后者就是随包分发的本地工具二进制目录。
对消费方的影响
对于通过包管理器安装该依赖的使用者,0.1.0 之后需要注意:
- 若你的项目是 ESM(
"type": "module"或使用.mjs),直接import即可; - 若你的项目是 CommonJS,不能再用
require('@composio/cli-local-tools')直接加载,必须依赖 Node 22.22.3+ 的原生require(esm)互操作,并且需要注意 ESM 的顶层语义(如异步加载、无__dirname等)对调用方式的影响; - 从源码看,包内部大量使用了
node:前缀的 ESM 导入(node:fs/promises、node:child_process、node:path等,见 src/runtime.ts),并对 MCP SDK 使用动态import(),这些都依赖完整的 ESM 运行时。
同时,0.1.0 还同步更新了 8 组依赖提交(552859a、a0bef5d、23f9053、dfd7a08、507318d、025a657、6a4cb54、4b76dbf、cbbad15),将@composio/core从 0.11.0 提升到0.12.0——这是它直接依赖的唯一运行时核心包(另一个核心依赖是@modelcontextprotocol/sdk,用于 MCP 类本地工具的调用)。
三、0.0.2:本地工具框架的诞生
0.0.2 是这个包的「奠基版本」,一条提交79ac220同时带入了四大能力:
- Beeper iMessage 本地工具包:可重建的 sidecar 二进制来自 ComposioHQ 的 platform-imessage 子模块,并封装了更高层的包装器,用于紧凑的会话发现、联系人感知的会话搜索、发送校验以及主实例反应(reaction)准备。
- Chrome DevTools 本地工具:基于官方
chrome-devtools-mcp包及其有状态的chrome-devtoolsCLI 守护进程的一等公民支持。 - 本地工具基础包脚手架:接入 Tool Router 的搜索/执行会话,并对外暴露
composio local-tools list|doctor|configure|meta四个子命令,分别用于发现、就绪检查、设置提示与本地元数据状态。 - Peekaboo macOS 本地工具:内置 darwin-arm64 的 Peekaboo CLI 二进制。
这四条能力共同勾勒出这个包的完整功能面。下面结合源码逐一深入。
3.1 内置的三个本地工具包
在 src/registry.ts 中,内置工具包被声明为一个明确的数组:
export const localToolkitDeclarations: ReadonlyArray<LocalToolkitDeclaration> = [ beeperImessageToolkit, chromeDevtoolsToolkit, peekabooToolkit, ];对应实现位于 src/toolkits/beeper-imessage.ts、src/toolkits/chrome-devtools.ts 与 src/toolkits/peekaboo.ts,每个文件都配有同名的.test.ts测试。
以 Beeper iMessage 为例,beeper-imessage.ts 中可以看到典型的本地工具声明结构:
- 内置二进制标识
beeper-imessage-cli,版本锁定为0.21.0; - 命令执行超时
COMMAND_TIMEOUT_MS = 120_000,发送校验轮询间隔500ms、超时8s,会话扫描默认最多翻页10页、上限50页; - 使用 zod 定义输入参数:
dataDir(iMessage CLI 状态目录,默认临时目录)、useSecondaryInstance(默认使用次要 Messages.app 实例;反应等 UI 相关工具默认切回主实例,因为需要可见的会话视图)、verbose(是否开启详细日志); - 输出统一封装为
cliOutput结构:ok、commandName、callId、durationMs、result、stdout、stderr。
这些细节说明本地工具并非简单"包一层命令",而是带有完整的输入校验、超时控制、输出规整与平台差异化行为。
3.2 平台声明与匹配
本地工具天然与操作系统强绑定,因此包内定义了细粒度的平台类型 src/platform.ts:
export type LocalCliPlatform = | 'all' | 'darwin' | 'linux' | 'win32' | 'darwin-arm64' | 'darwin-x64' | 'linux-arm64' | 'linux-x64' | 'win32-arm64' | 'win32-x64';detectCliPlatform()根据process.platform与process.arch探测当前平台(例如 darwin + arm64 →darwin-arm64),supportsCliPlatform()则判断某个工具的platforms声明是否覆盖当前平台——规则是:声明包含all、包含精确平台,或包含当前平台的家族平台(如darwin可覆盖darwin-arm64)。
例如 Peekaboo 的声明只面向darwin-arm64,因此在其他平台上运行composio local-tools doctor时,该工具会被标记为unsupported。
3.3 四种执行模型
LocalToolDeclaration中最重要的字段是execution,它决定了工具运行时如何被真正执行。根据 src/types.ts,共支持四种kind:
| kind | 执行方式 | 典型场景 |
|---|---|---|
command | 通过spawn启动本地命令 | CLI 程序、sidecar 二进制 |
native | 在进程内执行 JS 函数 | 需要精细控制或直接操作 Node API 的工具 |
mcp | 通过 MCP SDK 连接 stdio MCP Server | 封装任意 MCP Server 的能力 |
ffi | 通过 Bundlopen加载动态库 | 直接调用.dylib/.so/.dll |
command:子进程执行
src/runtime.ts 中的runLocalCommand是 command 模型的核心实现:
- 使用
node:child_process的spawn启动进程,捕获 stdout/stderr; - 支持
stdin注入、cwd、环境变量合并({ ...process.env, ...invocation.env }); - 支持
timeoutMs超时,超时后发送SIGTERM; - 退出码非 0 时抛出包含 exitCode/signal/stderr 的详细错误;
parseJson为 true 时尝试把 stdout 按 JSON 解析,失败则回退为原始文本。
命令的解析支持静态值与函数值两种形式(LocalCommandValue),并且可以引用内置二进制(LocalBundledBinaryRef)——此时会先解析随包分发的二进制路径,若不存在再回退到fallbackCommand(如 PATH 中的命令)。
mcp:MCP Server 桥接
runLocalMcpTool动态导入@modelcontextprotocol/sdk的Client与StdioClientTransport,用spawn拉起server.command(如chrome-devtools守护进程),然后:
- 若声明了
toolName,调用client.callTool({ name, arguments })并返回结构化结果; - 若未声明
toolName,则返回listTools()的结果——此时该本地工具本身就是一个"MCP 工具发现器"。
finally块中总会尝试client.close(),保证 MCP 连接被及时释放。
ffi:Bun 动态库调用
runLocalFfiTool使用bun:ffi的dlopen加载动态库:
- 库路径可以是绝对路径,也可以是
LocalBundledBinaryRef(先解析随包二进制,再回退到fallbackCommand); - 符号声明(
LocalFfiSymbolDeclaration)把LocalFfiType(char/i8/u32/i64/f32/ptr/cstring/void等 17 种)映射为 Bun 的FFIType; - 重要限制:该执行模型要求运行在 Bun 运行时中,源码会显式检查
globalThis.Bun,否则抛错 "Local FFI execution requires the Bun runtime used by the packaged Composio CLI."。
native:进程内函数
LocalNativeExecution直接提供execute(input, context)函数,并可选携带readiness前置命令供composio local-tools doctor做就绪检查。它是四种模型里最灵活、也最接近普通 SDK 工具的一种。
3.4 slug 归一化与 Tool Router 接入
本地工具接入 Tool Router 的核心机制在 src/registry.ts:
- 所有本地工具统一使用
LOCAL_前缀,slug 通过normalizeLocalToolSlug归一化:LOCAL_${TOOLKIT}_${TOOL},其中非字母数字字符替换为下划线、整体转大写; createLocalToolRouterExperimentalPayload把声明的本地工具包转换为 Tool Router 的 experimental payload 结构(custom_toolkits),其中每个工具都携带slug、name、description、input_schema与可选的output_schema;resolveLocalTool支持三种 slug 匹配方式(完整归一化 slug、纯工具 slug、toolkit_tool组合),并返回supported布尔值与最终 slug;executeLocalToolBySlug提供按 slug 直接执行的入口,执行前会用inputParams.safeParse做参数校验,失败时给出字段级错误信息。
同时,工具/工具包的描述会被自动追加平台说明:
Local CLI platforms: darwin-arm64.
这种"描述注入平台信息"的做法,让 Agent 在收到工具描述时就能立刻判断当前平台是否可用。
四、composio local-tools四个子命令背后的机制
0.0.2 的 CHANGELOG 明确提到了composio local-tools list|doctor|configure|meta四个子命令。虽然命令本身在 CLI 包中实现,但本包为它们提供了全部底层能力:
4.1 list:发现本地工具
list依赖 src/registry.ts 的声明查询能力:
getAllLocalToolkitSlugs()返回全部内置工具包 slug;getLocalToolkitDeclarations({ currentPlatform, toolkits })按平台过滤工具包,并进一步过滤每个工具包内不支持当前平台的工具,最后丢弃"没有任何可用工具"的空工具包;isLocalToolkitSlug/isLocalToolSlug用于判断某个 slug 是否属于本地工具。
也就是说,list展示的内容是平台感知的——在 Linux 上你不会看到只为 macOS 设计的 Peekaboo。
4.2 doctor:就绪状态检查
doctor对应 src/readiness.ts 中的checkLocalToolkitsReadiness,它会为每个工具生成一份LocalToolReadiness报告,状态共六种:
| 状态 | 含义 |
|---|---|
ready | 依赖就绪,可直接执行 |
unsupported | 当前平台不支持 |
disabled | 被~/composio/local_tools.json元数据禁用 |
missing | 依赖缺失(命令不在 PATH、内置二进制缺失等) |
not_implemented | 尚未实现 |
unknown | 无法静态判定(native 包装器需运行时自检) |
就绪检查的核心是findExecutableOnPath,它会:
- 对绝对路径/含分隔符的命令直接检查可执行性;
- 对普通命令名遍历
PATH目录逐一探测; - 在 Windows 上还会按
PATHEXT(.EXE;.CMD;.BAT;.COM)补全扩展名; - 对内置二进制引用,通过
resolveBundledBinary检查随包二进制是否存在。
工具包层面的状态则由所有工具状态按优先级聚合(ready<unknown<not_implemented<missing<disabled<unsupported),取最严重者。报告同时携带messages与hints,例如命令缺失时会提示:
Set
LOCAL_BEEPER_IMESSAGE_XXX.installation.commandorbeeper-imessage.installation.commandin ~/composio/local_tools.json to override the binary.
这直接衔接了configure子命令的作用。
4.3 configure / meta:本地元数据状态
这两个子命令围绕 src/meta.ts 实现。元数据文件默认位于~/composio/local_tools.json(可通过COMPOSIO_LOCAL_TOOLS_PATH环境变量或显式 path 覆盖),结构如下:
{ "version": 1, "updatedAt": "2026-09-11T00:00:00.000Z", "tools": { "LOCAL_BEEPER_IMESSAGE_XXX": { "disabled": false, "installation": { "command": "/path/to/imessage-cli", "version": "0.21.0" }, "authenticated": true, "updatedAt": "2026-09-11T00:00:00.000Z" } }, "toolkits": { "peekaboo": { "disabled": false } } }每个条目(LocalToolMetaEntry)支持的关键字段:
| 字段 | 作用 |
|---|---|
disabled | 禁用该工具/工具包,doctor 会标记为disabled |
installation.command | 覆盖 CLI 类本地工具实际使用的命令/二进制路径 |
installation.path/installation.version | 记录二进制路径与版本 |
authenticated/auth | 记录认证状态与认证数据(type、account、env、data) |
notes/metadata | 自由文本说明与扩展元数据 |
实现细节上值得注意:
- 工具条目按
finalSlug.toUpperCase()存储与读取,工具包条目按slug.toLowerCase()存储与读取,读写两侧保持一致的归一化策略; - 文件不存在时返回空的元数据对象而非报错(
ENOENT分支); - 每次写入都会自动把
version固定为LOCAL_TOOLS_META_VERSION = 1并刷新updatedAt; commandOverride的优先级在 runtime.ts 中体现:工具级installation.command优先于工具包级,最终覆盖声明的默认命令。
这个文件是configure(写入)与meta(读取)两个子命令的共享存储,也是本地工具"可配置、可禁用、可审计"的落点。
五、内置二进制与安全解压
5.1 随包二进制解析
本地工具往往依赖平台二进制,本包通过 src/bundled-binaries.ts 管理这些「内置二进制」:
- 默认包根目录为
local-tools-binaries,对应的仓库目录是 local-tools-binaries(内含 beeper-imessage、composio-native-ui、peekaboo 三个子目录,各带 LICENSE 与 NOTICE); - 解析时依次探测三个候选位置:打包 CLI JS 旁的 sidecar 目录、作为普通依赖安装时的包根目录、以及独立 Bun 可执行文件的同级目录;
- 支持
COMPOSIO_LOCAL_TOOLS_BIN_DIR环境变量直接指定; - 每个二进制目标声明(
LocalBundledBinaryTarget)包含platforms(哪些平台可用)、path(相对包根路径)与executable(是否需要标记可执行位); - 执行前会调用
ensureBundledBinaryExecutable确保二进制具备执行权限。
从仓库布局看,local-tools-binaries目录中的二进制通过构建脚本生成:package.json 提供了build:beeper-imessage、build:peekaboo、build:composio-native-ui与汇总的build:local-tool-binaries四个脚本。其中 composio-native-ui 的原生部分是 Swift 实现(见 native/composio-native-ui 的Package.swift与main.swift)。
5.2 ZIP 安全解压
由于 sidecar 二进制通常以压缩包形式分发,包内专门实现了 src/extract-zip-safely.ts,配套测试在 src/extract-zip-safely.test.ts,测试夹具包含benign.zip、symlink-absolute.zip、symlink-relative.zip三个样本(见 src/zip-fixtures)。
从测试夹具命名可以推断,安全解压的核心关注点是符号链接攻击:恶意 ZIP 可能通过绝对路径符号链接或相对路径符号链接把文件写到解压目录之外(经典的 Zip Slip 变体)。这与 test/managed-block-fixtures 等安装类测试的防御思路一脉相承,体现了 Composio 在本地工具分发链路中对安全性的重视。
六、测试、构建与质量保障
包内测试使用 Vitest(vitest.config.ts),package.json中定义了:
{ "scripts": { "build": "tsdown", "test": "vitest run", "typecheck": "tsc --noEmit -p ./tsconfig.src.json" } }测试覆盖的关键面包括:
- 三个工具包的声明与行为测试(
beeper-imessage.test.ts、chrome-devtools.test.ts、peekaboo.test.ts); - 内置二进制解析测试(bundled-binaries.test.ts);
- 注册表 slug 归一化与解析测试(registry.test.ts);
- ZIP 安全解压测试(extract-zip-safely.test.ts);
- Swift 系统补丁脚本测试(scripts/swift-system-patches.test.ts)。
依赖方面,运行时依赖仅三个:@composio/core(核心 SDK)、@modelcontextprotocol/sdk(MCP 桥接)、extract-zip(安全解压)、zod(输入 schema);chrome-devtools-mcp出现在 devDependencies 中(固定 1.8.0),说明它主要在构建/测试期使用。
七、从 CHANGELOG 看包的演进规律
把这五个版本的 CHANGELOG 放在一起,可以读出清晰的演进节奏:
功能一次性成型:0.0.2 完成了从"零"到"完整框架"的跨越——声明体系(types.ts)、平台探测(platform.ts)、执行运行时(runtime.ts)、注册表(registry.ts)、就绪检查(readiness.ts)、元数据(meta.ts)、内置二进制(bundled-binaries.ts)与三个具体工具包同时落地,说明这个包在设计阶段就有完整的架构规划。
跟随核心包演进:0.0.3 ~ 0.0.5 三次 Patch 都是纯粹的依赖升级(
@composio/core0.9.1 → 0.10.0 → 0.11.0),本地工具声明层保持稳定——这也侧面说明LocalToolDeclaration等接口在设计上足够前瞻,未随核心包频繁变更。一次有准备的破坏性变更:0.1.0 的 ESM-only 迁移发生在 0.x 版本线内,属于"破坏性但被明确文档化"的变更。它同时给出迁移路径(Node 22.22.3+ 原生
require(esm)),并同步把@composio/core升到 0.12.0,属于一次"打包策略 + 运行时门槛"的整体升级。
对于正在使用 Composio CLI 本地工具的开发者,0.1.0 意味着:升级后请确保运行环境满足 Node ≥ 22.22.3,且项目入口遵循 ESM 语义;对于只通过composioCLI 命令(local-tools list|doctor|configure|meta)使用本地工具的用户,这些变更由 CLI 打包产物消化,通常无感。
八、总结
@composio/cli-local-tools是 Composio CLI 本地能力的中枢包。它用一套统一的声明模型(工具包 → 工具 → 执行模型)把四种迥异的本地执行方式——子进程命令、进程内原生函数、MCP Server 桥接、Bun FFI 动态库——收敛为 Agent 可直接调用的LOCAL_*工具,并通过local_tools.json元数据与doctor就绪检查让本地工具的安装、配置、诊断全程可控。
其版本演进(0.0.2 奠基 → 依赖跟进 → 0.1.0 ESM-only)则展示了 monorepo 中工具包典型的生命周期:先确立稳定抽象,再随核心依赖稳步迭代,最后在合适的时机完成一次影响面清晰、迁移路径明确的破坏性升级。想要深入了解实现细节的读者,可以直接从 src/index.ts 的导出清单出发,沿types → platform → meta → runtime → bundled-binaries → readiness → registry的顺序阅读源码。
</output文章>
【免费下载链接】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),仅供参考