【免费下载链接】sandcastleOrchestrate sandboxed coding agents in TypeScript with sandcastle.run()项目地址https://gitcode.com/gh_mirrors/sandcastl/sandcastle点击查看免费下载导读Sandcastle 是一个用 TypeScript 编排沙箱化编码 Agent 的库sandcastle.run()在init脚手架与镜像定制这件事上它做出了一个明确的反向决策不提供任何用于程序化组合 Dockerfile / 管理基础镜像的抽象层如ISandboxEnvironment/IAgentHarness接口而是把.sandcastle/Dockerfile直接 scaffold 进用户项目并完全交给用户所有。读完本文你将掌握这个决策背后的四条理由与控制反转原则、sandcastle init实际生成 Dockerfile 的源码机制、内置镜像模板的结构与 UID/GID 构建参数以及在不借助抽象层的前提下自定义基础镜像的完整实操路径。决策核心不做镜像抽象层做可编辑的脚手架custom-base-image-abstraction.md记录的是 Sandcastle 项目早期对应 issue#283一次被否决的提案。提案设想为沙箱环境与 Agent 运行时各引入一层程序化接口ISandboxEnvironment/IAgentHarness以便在代码里组合 Dockerfile、统一管理基础镜像。最终结论是Sandcastle 不提供用于组合 Dockerfile 或程序化管理基础镜像的抽象层。取而代之的模型非常简单sandcastle init把一份完整、可运行、带注释的 Dockerfile 模板写进用户项目的.sandcastle/目录从那一刻起它就是用户自己的文件——用户可以直接改基础镜像、增删系统包、调整目录结构、替换ENTRYPOINT想怎么改就怎么改。Sandcastle 的职责止步于给出一个能工作的起点绝不越界去猜测用户的技术栈。四条理由为什么直接改 Dockerfile优于抽象层理由一Dockerfile 在 init 时被 scaffold之后完全归用户所有src/InitService.ts的scaffold()函数完整实现了这一模型InitService.ts在项目根目录创建.sandcastle/配置目录若已存在则直接报错防止覆盖用户的定制成果根据用户选择的 Agentclaude-code、pi、codex、cursor、opencode、copilot写入对应的 Dockerfile 模板根据用户选择的沙箱 providerDocker / Podman决定文件名是Dockerfile还是Containerfile同时生成.env.exampleAgent issue tracker 的 token 占位符、.gitignore和模板文件prompt.md、main.mts/main.ts。关键点在于init 只写入一次之后 Sandcastle 不再触碰、不覆盖、不同步这份 Dockerfile。任何镜像层面的需求换FROM基础镜像、预装语言运行时、写入 CA 证书、定制USER都通过直接编辑这份文件完成改动即刻生效于下一次sandcastle docker build-image。这正是文档所说的fully user-owned from that point。理由二抽象层为已经能做好的事引入了复杂度提案中的ISandboxEnvironment/IAgentHarness接口本质上是把 Dockerfile 的能力重新建模成一组编程接口。文档的判断是这层抽象为一件直接编辑 Dockerfile 就能完成的事增加了复杂度。复杂度并非只多不少。一旦引入抽象就需要定义基础镜像如何表示字符串对象解析后的指令树追加系统包与运行时如何表达方法链声明式配置与现有 Dockerfile 语法多阶段构建、ARG、COPY --from如何对齐抽象产生的配置如何再落回真实的 Dockerfile 文本。而从源码角度看Sandcastle 已经为镜像定制保留了最自然的入口镜像模板本身就是普通的 Dockerfile 文本存放在 InitService.ts 的CLAUDE_CODE_DOCKERFILE、PI_DOCKERFILE、CODEX_DOCKERFILE、CURSOR_DOCKERFILE、OPENCODE_DOCKERFILE、COPILOT_DOCKERFILE常量中并在 scaffold 时原样写入。用户编辑文件走的就是与模板作者相同的路径——不存在两套模型需要同步。理由三Docker 只是多个沙箱 provider 之一组合系统会过度耦合 init 层决策文档特别点出Sandcastle 不只支持 Docker还支持Daytona与E2B等其他沙箱 provider。从当前仓库 src/sandboxes/ 目录也能看到这一多 provider 格局docker.ts、podman.ts、daytona.ts、vercel.ts、no-sandbox.ts并列存在而 SandboxProvider.ts 用tag判别联合bind-mount/isolated/none统一了三种 provider 形态Bind-mountDocker、Podman把宿主机 worktree 目录挂载进容器Agent 直接写宿主文件系统IsolatedVercel、Daytona 类沙箱与宿主机隔离通过copyIn/copyFileOut传输文件NonenoSandbox()直接在宿主机上运行无容器隔离。如果 init 层内置一套Dockerfile 组合系统它天然假设了镜像构建是所有 provider 的公共能力——但 Vercel 的 Firecracker 微 VM、Daytona 的环境供给方式与 Docker 的docker build完全不同。文档的判断很直接把 Dockerfile 组合系统焊进 init 层等于把 init 层与 Docker 这一个 provider 紧紧绑死破坏 provider 的可插拔性。所以镜像定制被刻意保留为provider 相关的文件编辑而不是跨 provider 的统一抽象。理由四init 的职责是可工作的起点不是覆盖所有技术栈init脚手架的目标是让用户尽快跑起来模板 Dockerfile 预装了git、curl、jq和对应的 Agent CLI见下文模板解析足以支撑开箱即用而用户项目的真实技术栈Node 版本、Python 环境、数据库客户端、私有源……千差万别任何覆盖所有可能性的抽象尝试都会变成一份又厚又脆的配置面。因此文档给出的边界是init 只负责给出一份能工作的起点剩下的交给用户。源码级解析内置 Dockerfile 模板长什么样以CLAUDE_CODE_DOCKERFILEInitService.ts为例模板的结构完整展示了可编辑脚手架的设计FROM node:22-bookworm # Install system dependencies RUN apt-get update apt-get install -y \ git \ curl \ jq \ rm -rf /var/lib/apt/lists/* {{ISSUE_TRACKER_TOOLS}} # Build-args for UID/GID alignment: sandcastle docker build-image # defaults these to the host users UID/GID so image-built files # and bind-mounted files share an owner without runtime chown. ARG AGENT_UID1000 ARG AGENT_GID1000 # Rename the base images node user to agent and align UID/GID. RUN groupmod -o -g $AGENT_GID node usermod -o -u $AGENT_UID -g $AGENT_GID -d /home/agent -m -l agent node USER ${AGENT_UID}:${AGENT_GID} # Install Claude Code CLI RUN curl -fsSL https://claude.ai/install.sh | bash # Add Claude to PATH ENV PATH/home/agent/.local/bin:$PATH WORKDIR /home/agent # In worktree sandbox mode, Sandcastle bind-mounts the git worktree at ${SANDBOX_REPO_DIR} # and overrides the working directory to ${SANDBOX_REPO_DIR} at container start. # Structure your Dockerfile so that ${SANDBOX_REPO_DIR} can serve as the project root. ENTRYPOINT [sleep, infinity]几个值得注意的设计点{{ISSUE_TRACKER_TOOLS}}是模板占位符scaffold 时由 InitService.ts 的substituteTemplateArgs()替换为所选 issue tracker 的 CLI 安装步骤GitHub CLI 或 Beads 工具链。也就是说模板文件在 init 阶段被当场物化成最终文本用户拿到手的是完全展开、可逐行理解的 Dockerfile——没有任何运行时动态拼接。ARG AGENT_UID/AGENT_GID与 UID 对齐sandcastle docker build-image在 Linux/macOS 上默认把这两个构建参数设为宿主机用户的 UID/GIDprocess.getuid()/process.getgid()配合groupmod -o/usermod -o--non-unique把镜像内node用户改名为agent并对齐到宿主 UID/GID。这样镜像构建产物与 bind-mount 进来的 worktree 文件同属一个 owner避免运行时chownADR-0014docs/adr/0014-docker-uid-alignment-via-build-arg.md。USER指令使用数字形式${AGENT_UID}:${AGENT_GID}便于运行时 pre-flight 用docker image inspect --format {{.Config.User}}校验镜像内 UID 与实际执行 UID 是否一致docker.ts 中的containerUid/containerGid选项。ENTRYPOINT [sleep, infinity] 注释模板用睡眠进程保持容器存活Sandcastle 再通过exec()在容器内执行命令注释明确告知用户在 worktree 模式下SANDBOX_REPO_DIR会被 bind-mount 并作为工作目录提醒用户把你的 Dockerfile 结构设计得让该目录能充当项目根目录——这是模板把关键运行约束直接写进用户可见文件的做法而非藏在抽象层里。实战不借助抽象层自定义基础镜像的完整路径理解了文件归用户所有后自定义基础镜像就是纯文件编辑 重建镜像的流程运行npx ai-hero/sandcastle init生成.sandcastle/目录Docker 场景下包含Dockerfile选择 Podman 则生成Containerfile。所有交互提示都有对应的--flag--agent、--model、--sandbox、--template、--issue-tracker、--build-image等因此整个 init 可以在 CI 中非交互执行。编辑.sandcastle/Dockerfile更换FROM基础镜像、追加RUN apt-get install系统包、切换 Node 运行时版本、加入COPY证书/私有源配置等。注意保持模板末尾的ENTRYPOINT [sleep, infinity]与 UID/GID 对齐段除非你明确知道自己在做什么。重建镜像sandcastle docker build-imagePodman 对应sandcastle podman build-image。该命令从现有.sandcastle/目录重建镜像支持--image-name与--dockerfile参数指定自定义 Dockerfile 时构建上下文为当前工作目录。在 Linux/macOS 上它自动传入宿主机 UID/GID 作为AGENT_UID/AGENT_GID构建参数。运行验证npx tsx .sandcastle/main.ts或main.mts取决于项目package.json是否声明type: module按 blank 模板 的方式调用run()启动 Agent。若你手上有自己维护的镜像非模板构建无需重建即可通过docker({ containerUid: uid, containerGid: gid })声明镜像内已烘焙的 UID/GIDpre-flight 检查发现不匹配时会明确提示两种补救方案重建镜像或传containerUid对齐镜像见 docker.ts 与 ADR-0014。这套路径的关键优势在于每一步改动都是对一份普通 Dockerfile 的普通编辑没有需要学习的新 DSL、没有与底层文件保持同步的抽象配置、没有版本漂移问题。用户最终拥有的是一个完全可控、可读、可 grep、可 review 的文件。控制反转原则脚手架给默认值用户拥有结果决策文档最后提炼的原则是整个设计的锚点控制权向用户反转Control is inverted towards the user。Sandcastle 脚手架出一个合理的默认值用户拥有最终结果。这与 Sandcastle 的总体定位一脉相承prompt 体系README.md同样不施加任何关于工作流、任务管理或上下文来源的意见镜像定制则更进一步——连配置的载体都直接选用用户最熟悉的 Dockerfile 本身。放弃ISandboxEnvironment/IAgentHarness抽象层换来的是更低的认知负担、更强的可移植性不绑定 Docker和永不落后的定制能力Dockerfile 语法演进Sandcastle 无需跟随。边界说明该提案对应 issue#283在.out-of-scope/目录中归档为被否决的范围外提案custom-base-image-abstraction.md。同目录还归档了docker-provider-bespoke-options.md等相邻边界决策可一并阅读了解 Sandcastle 的取舍脉络。决策文档提及的 provider 集合Docker、Daytona、E2B是当时#283 时期的表述就当前仓库而言src/sandboxes/ 目录实际包含docker、podman、daytona、vercel、no-sandbox等 provider 实现README 中列出的内置选项为 Docker、Podman、Vercel 与 no-sandbox。无论 provider 集合如何演进镜像定制不进入抽象层、只通过用户拥有的 Dockerfile 完成这一决策边界始终保持不变。一个自然的推论如果你需要多个项目共享同一份定制镜像最佳做法也不是引入抽象层而是维护一份自己的基础镜像或构建脚本然后在各项目的.sandcastle/Dockerfile中把FROM指向它——控制权始终留在用户一侧。输出文章赞分享【免费下载链接】sandcastleOrchestrate sandboxed coding agents in TypeScript with sandcastle.run()项目地址https://gitcode.com/gh_mirrors/sandcastl/sandcastle点击查看免费下载相关推荐Prisma 1 是什么将数据库抽象为 GraphQL API 的数据库抽象层Prisma 1 是什么将数据库抽象为 GraphQL API 的数据库抽象层 Prisma 1 是一个 数据库抽象层database abstractio后端数据库GraphQLECharts多图表组合5种实战技巧解决90%复杂数据展示难题ECharts多图表组合5种实战技巧解决90%复杂数据展示难题 你是否曾面对这样的困境销售数据需要同时展示趋势、占比和分布但单一图表类型总是顾此失彼或者数据可视化图表库前端MoeKoe Music为什么这款二次元音乐播放器让95%用户放弃官方客户端MoeKoe Music为什么这款二次元音乐播放器让95%用户放弃官方客户端 MoeKoe Music作为一款基于Electron开发的跨平台音乐播放器以前端桌面应用音视频上一篇终极指南如何用Python自动化工具轻松搞定B站会员购抢票难题下一篇终极修复指南解决RetroArch UWP版Slang着色器失效问题从编译错误到完美渲染创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考