QM 通过普通凭证使用 Composio:技能、SDK 供应与权限边界全解析
2026/9/22 11:38:46 网站建设 项目流程

QM 通过普通凭证使用 Composio:技能、SDK 供应与权限边界全解析

【免费下载链接】qmMultiplayer agent harness for work.项目地址: https://gitcode.com/gh_mirrors/qm6/qm

QM 将 Composio 集成进自身沙箱计算机,通过一个名为composio的技能加官方 SDK 完成第三方应用(如 Gmail、GitHub 等)的发现、授权与工具执行。本文以 docs/composio.md 为骨架,结合 skills-seed/composio/SKILL.md、src/api/routes/composio.ts、src/sandbox/connector-sdk.ts 与 test/composio-route.test.ts 等源码,讲清凭证如何注入、SDK 如何预置进沙箱、权限边界在哪里、回滚如何做,以及你部署和运维时需要注意的安全细节。

设计总览:一个技能 + 官方 SDK

QM 复用自己已有的凭证机制(keychain / 组织级 service credential),不单独发明一套 OAuth 代理。Composio 的接入由两部分构成:

  • composio技能(skills-seed/composio/SKILL.md):告诉 Agent 何时、如何用 SDK 发现应用、发起授权、选择连接并执行工具;
  • 官方 SDK 的预置 bundle:Sprites 与 Modal 沙箱在供给(provisioning)阶段获得一份固定的、已构建好的 SDK 包,避免在沙箱里现场npm install

核心执行路径是:技能发现并组合操作 → 沙箱内用官方 SDK 调 Composio 后端 API → 密钥只通过既有凭证通道送达命令环境,绝不出现在对话、脚本或仓库里。

凭证设置:普通 keychain 凭证,无 provider 标志

按 docs/composio.md 的 Setup 章节,接入只需两步:

  1. 注册一个普通个人 keychain 凭证,名为composio,环境变量键为COMPOSIO_API_KEY
  2. 或者使用已有的组织级 service credential,要求delivery: "env"且环境变量键同样为COMPOSIO_API_KEY

然后按常规授予凭证权限(credential grants)。不需要任何 provider 标志。关键约束:永远不要把密钥粘贴进对话、脚本或仓库

在源码层面,src/api/routes/composio.ts 的credential()函数严格实现了这一语义:

  • 请求者必须是已激活的 principal,否则 401/403;
  • 优先查找请求者个人keychain 中kind === "env"envKey === "COMPOSIO_API_KEY"、未过期的凭证;
  • 如果个人凭证多于一条,直接返回 409ambiguous_credential(fail-closed),而不会悄悄回退到公司级密钥——test/composio-route.test.ts 专门验证了这一行为;
  • 若无个人凭证,则走组织级 service credential,要求enabledhasSecretdelivery === "env"envKey === "COMPOSIO_API_KEY",且该凭证 slug 必须命中 ACL 中授予当前身份的service-cred读取权限;
  • 公司凭证未授权或未启用时返回 403,多个候选时返回 409。

测试 test/composio-route.test.ts 还验证了:未认证请求直接 401、无 grant 或未启用的公司凭证 403、合法授权后请求头x-api-key携带的是公司密钥。这条链路说明“密钥只送达被授权的位置”不是一句空话,而是由既有凭证机制强制执行的。

身份模型:userId 不是安全边界

Composio 的项目 API key 授予持有者该项目的全部权限,资源区域限制并不等同于按用户隔离。keychain 的 grants 只控制“谁能拿到这个 key”,并不缩小它在 Composio 内的权限范围。因此:

  • 调用方提供的userId只是一个账户选择标签,不是授权边界
  • 不要把同一个跨公司项目 key 分发给互相隔离的公司,并宣称它们的连接仍然互相隔离;
  • 本技能不解决共享宿主场景下的凭证隔离或供给问题。

为保持可追溯且避免撞车,QM 在 src/api/routes/composio.ts 中派生稳定身份:userId = "qm_" + sha256(org, principal)。同一人在不同组织、不同人在同一组织的 userId 均不同,且该值只由已认证的 actor 计算得出。

skills-seed/composio/SKILL.md 在访问模型一节也反复强调:技能的指令必须继续遵守用户既有的发送、起草与审批要求,不要用伪装调用绕过审批。从源码结构看,这意味着技能本身只是“提示层”,审批与策略仍由 QM 的 command policy 等既有机制裁决。

授权与连接:应用选择器、回调校验与审计

Web 聊天中的接入指令

当用户在 web 聊天中要求连接应用、浏览集成或重新打开设置时,技能要求 Agent 在自己的回复中以独立段落输出:

::connect-apps{}

web UI 只会在原地渲染一个可搜索的应用选择器。若要显示独立的 “Add to Slack” 操作,则输出::add-to-slack{};两者需要时分别以独立段落给出。渲染该部件不会授权任何服务、也不触发 SDK 调用——用户自行选择应用并完成 provider 的同意流程;未验证状态前不得宣称已连接。该指令仅用于 web,Slack 会话中应使用普通授权链接。

回调衔接由 plugins/web-ui/server/composio-return.ts 的composioCallbackUrl()处理:它校验 state 格式、returnTo 必须是站内路径(拒绝//、反斜杠、换行、跨源跳转),并把composioReturn状态参数写回目标 URL,避免把连接结果错误地落到任意站点。

后端 API:四个受保护路由

src/api/routes/composio.ts 暴露了四条路由,全部走既有认证:

方法与路径作用认证
GET /v1/composio/toolkits应用目录(按使用频率排序,支持游标分页)source
GET /v1/composio/connections当前 actor 的 ACTIVE 连接列表source
POST /v1/composio/authorize创建 tool router session 并发起授权,返回 redirect URLsource
GET /v1/composio/identity返回当前 actor 的稳定 Composio userIdeither

实现细节值得注意(均有对应测试佐证):

  • 目录清洗:src/api/routes/composio.ts 只回传slug/name/description/logoUrl,过滤掉NO_AUTHscheme 与非法 slug,绝不回传密钥或原始字段
  • 授权防重定向滥用:src/api/routes/composio.ts 校验 session 与账户 ID 格式,且 redirect URL 必须严格属于https://connect.composio.dev/link/lk_*https://app.composio.dev/link/lt_*,拒绝携带用户名密码、非 https 或任意路径的组合(test/composio-route.test.ts);
  • callback 白名单:仅允许 https,或本机回环地址的 http;拒绝javascript:等危险 scheme(test/composio-route.test.ts);
  • 忽略调用方自报身份:即使请求体携带user_id: "bob",后端也一律用已认证 actor 派生 userId 绑定 session,测试 test/composio-route.test.ts 明确断言这一点;
  • 连接列表只返回本人:src/api/routes/composio.ts 用派生 userId 过滤上游结果,仅保留 ACTIVE、未禁用、格式合法的账户,并剥离凭证字段;
  • 错误脱敏:上游错误一律折叠为 502 通用提示,不泄露密钥等敏感信息(test/composio-route.test.ts);
  • 审计authorize动作写入 audit(actioncomposio.authorize,scope 为请求者 personal scope)。

技能工作流:发现 → 授权 → 执行 → 校验

skills-seed/composio/SKILL.md 的 Workflow 章节给出了六步可组合操作:

  1. 发现client.toolkits.list({ limit: 50 })分页拉取应用;composio.tools.getRawComposioTools({ search: "the task", toolkits: [toolkit], limit: 25 })找原生工具,逐个阅读输入 schema;
  2. 取身份:先读GET /v1/composio/identity拿稳定 userId(与应用选择器一致),不得猜测或借用他人 ID;端点不可用时复用既定映射并在工作笔记中记录非密钥映射关系;用composio.connectedAccounts.list({ userIds, toolkitSlugs })列账户,只展示 ID/标签/状态,不展示含凭据的原始对象;账户模糊时询问用户而不是随手取第一个;
  3. 授权composio.sessions.create(userId, { manageConnections: false, sandbox: { enable: false } })后调用session.authorize(toolkit),把redirectUrl交给用户自行完成同意;事后用composio.connectedAccounts.get(accountId)复核,只有状态为 ACTIVE 才可声称已连接
  4. 执行composio.tools.execute(tool.slug, { userId, connectedAccountId, version: tool.version, arguments: args }),使用发现的 schema 与具体版本,保持用户请求的动作与账户显式可见;
  5. 校验结果:检查成功/错误字段;不要自动重试不确定的写操作;草稿保持草稿、邮件保持纯文本,发送/删除必须来自用户请求;
  6. 断开:仅在用户要求时用 provider 文档化的 revoke/delete 操作,确认结果后再声明已撤销。

两点硬性限制:自动文件传输已禁用,上传/下载只允许显式授权的文件(路径字符串不等于传输了字节);若项目启用了回调身份验证(callback identity verification),同意流程必须回到该项目已有的已认证验证器,本技能既不实现也不绕过它——不要用自报身份兑现session_uri、不要关闭验证、不要伪造回调成功,缺失配置时应请运维人员解决。

沙箱 SDK 供给:固定 bundle、SHA-256 校验、原子激活

核心镜像直接携带一份不含凭证的预构建 SDK 资产,SDK 调用只运行在受限的 scoped computer 中;其他沙箱后端则保留技能描述的按需安装方式(npm install --prefix .tools/composio --no-save --ignore-scripts @composio/core@0.18.1,并从该目录运行、导入@composio/core)。

构建

  • 源码开发时,npm startnpm run devnpm run worker与 dev-instance 启动器都会自动构建 bundle(package.json 中的prestart/predev/preworker/pretest均先跑build:connector-sdk);
  • 直接用 Node 启动核心时,需先手动执行npm run build:connector-sdk
  • 核心 Dockerfile 也会自动构建同一资产;发布的 CLI 部署的是这个核心镜像,CLI 自身不运行 connector SDK
  • 独立 lockfile 位于 deploy/connector-sdk/package.json,固定@composio/core@0.18.1与 esbuild 构建链;构建产物是可移植的 Node JavaScript + 第三方许可文本,输出到.generated/connector-sdk,不含原生模块与任何凭证。修改 lockfile 后必须重新构建

供给

src/sandbox/connector-sdk.ts 的installConnectorSdk()实现了严格的供给流程:

  1. 校验:bundle 的 SHA-256 必须匹配(sha256sum -c --status探测),不符直接抛错;
  2. 复用:若$HOME/.qm/composio/current/sdk.cjs已匹配则直接退出;否则优先复用镜像内烧录的/opt/qm/composio/sdk.cjs,其次复用$HOME/.qm/composio/<sha>/中已验证的旧版本;
  3. 传输:都不命中时,把 bundle 写入临时 staging 目录,校验通过后原子移动到<sha>目录并符号链接激活current
  4. 激活current通过符号链接指向具体 sha 目录,切换用临时 link +renameSync完成,保证只有校验完整、能被require(...).Composio正确加载的文件才会成为 active
  5. 并发与重试:并发供给可能重复传输字节,但只有校验通过的完整文件才会激活;中断的传输下次供给自动重试;
  6. 升级与回滚:home 恢复(restore)先于供给执行,因此恢复出的旧 bundle 会自动升级到新版本,旧版本仍留在<sha>目录供回滚;
  7. 无联网:沙箱内不发生 npm registry 连接或依赖解析,只使用预置文件。

技能侧的使用方式(skills-seed/composio/SKILL.md)是直接导入预置 bundle:

const { Composio } = await import(`${process.env.HOME}/.qm/composio/current/sdk.cjs`); const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY, allowTracking: false, disableVersionCheck: true, dangerouslyAllowAutoUploadDownloadFiles: false, }); const client = composio.getClient(); client.maxRetries = 0; client.timeout = 30_000; client.logLevel = "off";

注意dangerouslyAllowAutoUploadDownloadFiles: false与自动文件传输禁用策略一致。供给端要求 Node22.22.3 或更新版本

权限策略与运维要点

  • 既有 command policy 不变:针对直接 provider URL 或特定 CLI 命令编写的规则不会自动覆盖 SDK 调用,运维人员必须审查策略覆盖范围;技能指令仍保留用户的发送、起草与审批要求;
  • pre-rollout 验证:上线前务必测试应用侧发起的同意流程与所请求的操作;
  • 供应商侧注意事项:Composio 负责 provider 的认证与 token 刷新,但部分应用仍需要 provider 侧管理员/客户配置;若项目启用了回调身份验证,则同意必须由既有验证器完成;
  • 不静默改动:添加这些技能不会改动任何现存 key、grant 或项目设置;自动 SDK 文件传输已禁用;独立的聊天机器人安装与确定性的后台 source 适配器保持不变。

回滚

  • 移除 seed不会删除已发布的技能,发布是独立动作;
  • 替换早期原型时,若安装了旧的integrations技能,需先将其归档;
  • 回滚本版本:恢复之前的 app/onboarding 技能 → 归档已发布的composio技能 → 停止使用它的工作流 →撤销单独启用的凭证 grants;若此前已把 key 分发给副本且必须让其失效,则轮换或吊销 provider key;未经授权不得删除 provider 连接。

小结

QM 把 Composio 接成了一条“普通凭证 + 沙箱内官方 SDK”的链路:凭证由既有 keychain/service-credential 机制严格管控(fail-closed、多凭证即 409),身份用qm_sha256(org, principal)稳定派生且不信任调用方自报 userId,SDK bundle 通过 SHA-256 校验与原子符号链接激活供给到沙箱,错误统一脱敏并写入审计。这条链路覆盖从应用发现、授权、执行到回滚的完整生命周期,其关键行为均有 test/composio-route.test.ts 与 src/sandbox/connector-sdk.ts 可查证,适合作为接入第三方应用连接能力时的参考实现。

【免费下载链接】qmMultiplayer agent harness for work.项目地址: https://gitcode.com/gh_mirrors/qm6/qm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询