Sim 托管 CLI 注册表完全指南:如何为 Function Sandbox 安全地新增一个不可变 CLI
【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim
本文是一份面向 Sim 仓库贡献者的实战技术指南,围绕.agents/skills/add-managed-cli/SKILL.md中的工作流程展开,完整讲解如何为 Sim 的 Function Sandbox 添加或升级一个精选(curated)、不可变的托管 CLI——从上游版本核验、不可变 ID 设计、客户端安全元数据与服务器端安装配方编写,到供应链测试与真实沙箱冒烟验证。读完本文,你将掌握 Sim 托管 CLI 注册表(apps/sim/lib/execution/remote-sandbox/cli-tools.ts与cli-tools.server.ts)的完整实现原理,并能在不触碰 UI、API、数据库、解析器与云厂商(provider)代码的前提下,独立完成一次合规的 CLI 接入。
为什么需要一个"托管 CLI"注册表
Sim 的 Function Sandbox 允许工作区在沙箱里安装一组预置的命令行工具(例如gcloud、aws、kubectl、terraform、duckdb等),供 Agent 与工作流在沙箱环境中直接调用。但这些 CLI 并非通过任意命令或包名安装,而是必须经过一个精选注册表(curated registry):
- 系统级 Debian/APT 包已经覆盖了经过验证的 Debian/APT 坐标场景;
- 托管 CLI 则要求不可变产物(immutable artifacts)与可复现配方(reproducible recipes)。
注册表的核心原则是:持久化的是 ID,而不是任意的安装命令。apps/sim/lib/execution/remote-sandbox/cli-tools.ts开头的注释明确写道:
Stable identifiers for the curated CLI tools a workspace sandbox may install. The persisted value is an id, never an arbitrary install command.
也就是说,用户侧(客户端)只接触安全的目录元数据,服务器侧(server-only)才持有下载、校验、安装与验证的完整配方,二者通过导入边界严格隔离。
截至当前仓库,注册表共收录24 个托管 CLI(见 cli-tools.ts 中的SANDBOX_CLI_TOOL_IDS),分为 6 个类别:Cloud、Kubernetes、Infrastructure、Deployment、Data & storage、Security。每个沙箱最多可选10 个CLI(MAX_SANDBOX_CLI_TOOLS = 10,在 HTTP 边界与领域边界双重强制)。
动手前必读:五个活体源码
SKILL.md 强调:编辑之前先读这些活体源码,不要把技能文档里的当前条目复制进去(条目会随版本演进而变化)。这五个文件是理解注册表工作的钥匙:
| 文件 | 作用 |
|---|---|
| cli-tools.ts | 持久化 ID 与客户端安全元数据(catalog) |
| cli-tools.server.ts | 仅服务器端配方(recipe)与配方辅助函数 |
| cli-tools.test.ts | 目录与供应链不变量测试 |
| cli-tools-boundary.test.ts | 客户端/服务器导入边界测试 |
| sandbox-spec.ts | 内容寻址哈希(content-addressed hash)的输入 |
另外两点约束:
resolve.ts与e2b.ts只在改动供给机制(provisioning mechanics)时才需要阅读。一次常规的目录新增不需要修改 UI、API、数据库、解析器或 provider 代码——这些路径都会从注册表自动派生。- 常规托管 CLI 新增不得修改专用的 Function 基础镜像,也不得修改独立的 Mothership Shell 模板——托管配方只是叠加在 Function 基础镜像之上的一层。
MAX_SANDBOX_CLI_TOOLS(10)除非用户另行提出产品级限制变更,否则不要改动。
第 1 步:核验上游发布版本
在写任何代码之前,必须通过上游官方发布文档与官方产物,确认以下全部信息:
- 精确版本与稳定的 Linux x86-64 产物 URL。严禁使用
latest、可变重定向或无版本号的安装器; - 该精确产物的SHA-256。优先使用发布方签名的校验和;否则自行下载官方产物并独立计算;
- 归档(archive)布局与需要安装的精确可执行文件路径;
- 针对每一个对外公布的可执行文件的非交互、免凭据验证命令(通常是版本命令);
/opt/sim-cli下所需的 PATH 条目;- E2B 与 Daytona 兼容性。只有在同一个 Linux 配方对两者都成立时,才默认同时支持两者。
当用户未指定版本时,应从主要上游来源选择当前稳定版本,并明确声明所选的确切版本,不得静默选择预发布版(prerelease)或从不经核实的二手来源推断版本。
明确的拒绝清单:curl 管道到 shell 的安装器(curl-to-shell installers)、npm install、pip install、发行版软件包仓库、任意用户命令、非官方镜像产物,一律拒绝。并且绝不允许在镜像配方或构建日志中放置凭据、令牌、登录命令或账户配置——认证只允许发生在运行时(runtime-only)。
第 2 步:选择不可变 ID
ID 格式为:<tool>@<upstream-version>-r<recipe-revision>,例如kubectl@1.36.3-r1、aws-cli@2.36.15-r1。
规则要点:
- 新上游版本:追加一个以
-r1结尾的新 ID; - 同一上游版本的纯配方变更:追加
-r2、-r3……依次递增; - 绝不修改或删除已有 ID 或配方。已持久化的沙箱必须能继续解析到当初选定的字节与行为;
- 升级时:保留旧 ID 与旧配方,并将其元数据设为
selectable: false,只有最新版本保留公开标签可选。
在首次为一个工具家族发布升级前,需要验证:编辑沙箱时不会同时留下已退役与替换两个 ID 都被选中的状态。如果通用选择器与 API 校验尚未替换或拒绝冲突版本,应在通用注册表边界统一处理一次(配以聚焦的 UI 与契约测试),绝不针对单个 CLI 做特判,也绝不静默安装两个暴露同一可执行文件的版本。
配方的身份(ID、revision、SHA-256)会进入沙箱镜像哈希——这正是hashSandboxSpec在 sandbox-spec.ts 中所做的事。保留旧条目,才使这种身份是**可复现(reproducible)**的,而不仅仅是缓存失效(cache-busting)。
第 3 步:添加客户端安全元数据
在 cli-tools.ts 中完成以下操作:
- 将 ID 追加到
SANDBOX_CLI_TOOL_IDS,顺序与元数据注册表、配方注册表保持一致; - 在
SANDBOX_CLI_TOOLS中添加一个条目,其键(key)与id精确一致; - 提供唯一的、可选的
label,简洁的description,已有的category,以及在searchTerms中给出有用的可执行文件/厂商别名; - 只有当现有类别都不准确时才新增类别,且新类别必须至少有一个可选条目。
以kubectl为例,真实条目形如:
'kubectl@1.36.3-r1': { id: 'kubectl@1.36.3-r1', label: 'kubectl', description: 'Control Kubernetes clusters.', category: 'Kubernetes', searchTerms: ['kubectl', 'kubernetes', 'k8s'], },关键约束:该文件必须保持对客户端 bundle 安全。它不得包含产物 URL、校验和、安装命令、验证命令、PATH 配方、provider SDK,也不得从cli-tools.server.ts导入任何东西。cli-tools-boundary.test.ts会从客户端可达的模块图反向可达性搜索,确保cli-tools.server.ts永远不会被客户端模块图拉入。cli-tools.test.ts中还有一个专门的断言,直接读取cli-tools.ts源码文本,确认其中不出现installCommand、verificationCommands、pathEntries、SANDBOX_SYSTEM_PATH、sha256以及任何官方产物 host。
API 枚举与可搜索的分组选择器都由这个注册表派生(见 managed-cli-select.tsx),因此不要添加并行的选项数组或路由本地的 wire 类型。
第 4 步:添加仅服务器端配方
在 cli-tools.server.ts 中,优先使用最窄的辅助函数:
| 辅助函数 | 适用场景 |
|---|---|
defineBinaryRecipe | 单个下载的二进制文件(如kubectl、argocd、firebase、sops、minio-mc) |
defineTarGzipRecipe/defineZipRecipe | 包含二进制的归档(如doctl、gh、helm、terraform、stripe、duckdb) |
defineVerifiedRecipe | 厂商归档或安装器布局,需要显式命令(如gcloud、aws、az、pulumi、restic、mongosh) |
| 直接手写类型化条目 | 仅当辅助函数无法如实建模该发布形态时 |
defineVerifiedRecipe会把配方统一组装为一条确定性的安装命令,其结构(真实生成逻辑)为:
mkdir -p '<root>/bin' '<root>/extract' && curl -fsSL --retry 3 --retry-all-errors '<artifactUrl>' -o '<artifact>' && echo '<sha256> <artifact>' | sha256sum -c - && <installCommands...> && rm -f '<artifact>'其中root统一为/opt/sim-cli/<toolName>,bin为<root>/bin,临时产物放在/tmp/sim-cli-<toolName>-<artifactName>,安装完成后清理。cleanupCommand只允许出现rm -rf/rm -f且路径严格限定在/opt/sim-cli/<tool>/extract与/tmp/...——测试会对每个清理指令做正则断言。
配方契约要求提供以下每个字段:
- 精确的
version、artifactUrl、artifactName,以及小写 64 位十六进制sha256; - 每个安装的
executable都对应一个verificationCommands条目; - 确定性的解压/安装命令,写入
/opt/sim-cli;固定路径必须加引号,并清理临时产物; - 当可执行文件不在辅助函数默认
bin目录时,提供pathEntries; - 仅当与 E2B-and-Daytona 默认值不同时,才提供
supportedProviders(默认两者都支持); - 当
revision不等于1时提供之,且必须与 ID 后缀一致。
一些真实的配方形态示例:
二进制直达(kubectl):
'kubectl@1.36.3-r1': defineBinaryRecipe('kubectl@1.36.3-r1', { version: '1.36.3', artifactUrl: 'https://dl.k8s.io/release/v1.36.3/bin/linux/amd64/kubectl', artifactName: 'kubectl-v1.36.3-linux-amd64', sha256: 'ebbd080e7c2e275093b55915722043257eb24004363e20acb3c4d71919f88336', executables: ['kubectl'], verificationCommands: ['kubectl version --client'], binaryName: 'kubectl', }),tar.gz 归档(github-cli,注意归档内嵌套路径):
'github-cli@2.97.0-r1': defineTarGzipRecipe('github-cli@2.97.0-r1', { version: '2.97.0', artifactUrl: 'https://github.com/cli/cli/releases/download/v2.97.0/gh_2.97.0_linux_amd64.tar.gz', artifactName: 'gh_2.97.0_linux_amd64.tar.gz', sha256: 'a2c9b8497e1f85b1ad0dfcb78b5a622e098801b8e461e459e88e1ee12f018112', executables: ['gh'], verificationCommands: ['export GH_NO_UPDATE_NOTIFIER=1 GH_TELEMETRY=0', 'gh --version'], binaries: { gh: 'gh_2.97.0_linux_amd64/bin/gh' }, }),厂商安装器布局(aws-cli,需要显式 install 命令):
'aws-cli@2.36.15-r1': defineVerifiedRecipe('aws-cli@2.36.15-r1', { version: '2.36.15', artifactUrl: 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.36.15.zip', artifactName: 'awscli-exe-linux-x86_64-2.36.15.zip', sha256: '02a8eb2fe985be8ebcc284aaa5bae206ee8668872d6369e66a5c7d49d8671a08', executables: ['aws'], verificationCommands: ['aws --version'], installCommands: (paths) => [ `unzip -q '${paths.artifact}' -d '${paths.extract}'`, `'${paths.extract}/aws/install' --install-dir '${paths.root}/aws-cli' --bin-dir '${paths.bin}' --update`, ], }),关于验证命令:必须离线、免遥测
验证命令的目标是证明该命令能通过sandboxCliEnvironment被发现,而不是做认证或联系用户账户。因此许多配方都会在版本命令前导出环境变量以关闭遥测与更新检查:
gcloud --version、bq version、gsutil version(google-cloud-cli)export GH_NO_UPDATE_NOTIFIER=1 GH_TELEMETRY=0+gh --versionexport GLAB_CHECK_UPDATE=false GLAB_SEND_TELEMETRY=false+glab versionexport CHECKPOINT_DISABLE=1+terraform versionexport PULUMI_SKIP_UPDATE_CHECK=true+pulumi versionsops --disable-version-check --versionmongosh --build-info(构建信息而非联系网络的检查)
cli-tools.test.ts用一张表格逐一断言这些"离线、免遥测"的验证命令组合。配方命令以 root 身份运行,既发生在预构建镜像创建阶段,也发生在**运行时供给(runtime provisioning)**阶段。
新增产物 host 的供应链审查
如果产物托管方(host)是全新的,只能在 cli-tools.test.ts 的officialHosts白名单中添加确切的官方 hostname。当前白名单包含:awscli.amazonaws.com、dl.k8s.io、dl.min.io、downloads.mongodb.com、downloads.rclone.org、get.helm.sh、get.pulumi.com、github.com、gitlab.com、packages.microsoft.com、releases.hashicorp.com、storage.googleapis.com。测试同时断言:所有artifactUrl必须是https://、不得包含latest、必须包含版本号;安装命令不得出现curl ... | sh、npm install、pip install。请把白名单视为一次供应链审查,而不是让测试闭嘴的手段。
第 5 步:保持通用行为不被破坏
新增 CLI 后,需要确认以下通用路径仍然自洽:
sandboxCliToolRecipes:对传入的 ID 列表做 canonicalize(去重 + 排序)并解析出配方;sandboxCliEnvironment:把每个配方的pathEntries与SANDBOX_SYSTEM_PATH合并,构造 PATH 环境,传播给 Python 子进程、JavaScript 子进程与 Shell:
export const SANDBOX_SYSTEM_PATH = '/usr/local/sbin:/usr/local/bin:/usr/local/games:/usr/sbin:/usr/bin:/usr/games:/sbin:/bin:/root/.local/bin'- E2B 会把配方烘焙进自定义镜像;运行时策略(runtime-strategy)provider 则在 Function 超时内执行安装;
- 纯 CLI 沙箱(不选任何语言包)也必须保持可构建;
hashSandboxSpec在包含配方 ID、revision、校验和的同时,对空 CLI 列表保留旧版哈希(sandbox-spec.test.ts用固定摘要3cb73688...断言了这一点,确保存量沙箱镜像不被无谓重建);- 设置页选择器从客户端安全元数据派生分组与搜索别名。
不要在那些通用层里为某个 CLI 特判,除非注册表契约确实无法表达某个真实的 provider 需求。当多个 CLI 需要同一种新行为时,应泛化地扩展注册表契约。
第 6 步:为新增编写测试
当新条目引入了未被现有测试覆盖的行为时,扩展测试(主要位于 cli-tools.test.ts):
- 每一次升级都增加回归断言:旧 ID 与旧配方仍然可解析但不可选择,替换 ID 可选;
- 把重要的可执行文件别名加入表驱动的搜索断言(如
['google-cloud-cli@577.0.0-r1', ['bq', 'gcloud']]、['minio-mc@...', ['mc']]); - 为多可执行文件配方、自定义 PATH 或受限 provider 增加聚焦断言(如 supabase 的
supabase与supabase-go双可执行文件); - 仅在"安装 + 真实最小命令无法脱离认证被验证"时,才添加opt-in 凭据冒烟测试:凭据从测试专用环境变量读取、默认跳过、仅在运行时创建、并且总是拆除沙箱。
仓库中的真实冒烟测试范例是 managed-cli.smoke.test.ts:它由E2B_MANAGED_CLI_SMOKE=1触发(默认describe.skipIf跳过),要求设置E2B_API_KEY与E2B_FUNCTION_TEMPLATE_ID,对SANDBOX_SELECTABLE_CLI_TOOL_IDS中每个 ID 依次执行安装命令 → 清理命令 → 验证命令三个阶段,全部以 root 用户运行,安装超时 15 分钟、验证超时 2 分钟,afterEach中强制kill()沙箱。
绝不提交下载的产物或凭据。
必需验证:完整命令清单
在apps/sim目录下运行单元与组件测试:
bunx vitest run \ lib/execution/remote-sandbox/cli-tools.test.ts \ lib/execution/remote-sandbox/cli-tools-boundary.test.ts \ lib/execution/remote-sandbox/sandbox-spec.test.ts \ lib/execution/remote-sandbox/resolve.test.ts \ lib/api/contracts/sandboxes.test.ts \ 'app/workspace/[workspaceId]/settings/components/sandboxes/utils.test.ts' \ 'app/workspace/[workspaceId]/settings/components/sandboxes/components/sandbox-editor.test.tsx'在仓库根目录运行类型检查、API 校验契约检查、格式与差异检查:
bun run type-check bun run check:api-validation bunx biome check \ apps/sim/lib/execution/remote-sandbox/cli-tools.ts \ apps/sim/lib/execution/remote-sandbox/cli-tools.server.ts \ apps/sim/lib/execution/remote-sandbox/cli-tools.test.ts git diff --check对新增配方,还应在真实 E2B 或 Daytona 沙箱中实际执行它的安装命令与每一条验证命令(需要凭据与网络可用时)。若只运行了注册表/单元验证,必须如实说明缺口。
完成检查清单
- 官方不可变 Linux x86-64 产物与 SHA-256 已核验;
- 版本化 ID 已追加;旧 ID 与旧配方全部保留;
- 客户端元数据可搜索、已分类、唯一、且不含配方细节;
- 服务器端配方已钉死版本、经完整性校验、非交互、免凭据;
- 每个对外公布的可执行文件都有离线验证命令与 PATH 条目;
- provider 兼容性明确且准确;
- 目录、边界、哈希、解析器、类型、API 校验、格式与 diff 检查全部通过;
- 真实 provider 安装已测试,或如实披露缺失的实机验证。
小结
Sim 的托管 CLI 注册表是一套把"供应链安全"内建到数据模型里的系统:客户端只见安全的目录元数据,服务器端持有钉死版本、校验和与安装配方的不可变配方,二者以cli-tools-boundary.test.ts的导入边界与cli-tools.test.ts的不变量测试双重锁死;内容寻址的hashSandboxSpec把配方 ID、revision 与 SHA-256 纳入镜像身份,同时为旧沙箱保留兼容哈希。遵循本文的六步流程与验证清单,你就能在不触碰 UI、API 与 provider 实现的前提下,安全地扩充这个精选工具集,并确保每一次新增都可复现、可审计、可回滚。
【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考