跳到正文
Hacker News· cs1996·· 7 天前AI 评分34

Mainbrella Build:基于 Cloudflare Workers AI 的 Lovable 式应用构建器,支持 GLM-5.3、Kimi K2.7 Code 与 GLM-5.3 Flash

Lovable Style Builder via CF Inference GLM-5.3, Kimi K2.7 Code and GLM-5.3 Flash

AI 导读

Mainbrella 推出 Build 应用构建器,采用类似 Lovable 的对话式工作流,通过 Cloudflare Workers AI 调用 GLM-5.3、Kimi K2.7 Code 和 GLM-5.3 Flash 三个模型,Kimi 推理设置固定,GLM 支持 high 与 max effort。

正文 · AI 翻译

← 工程博客

2026年10月10日 · Andrew Arrow

我们构建了一个应用构建器,其工作流程与 Lovable 类似:描述你想要的内容,查看可用的预览,并持续与智能体对话来修改它。Mainbrella Build 通过 Cloudflare Workers AI 运行 GLM-5.3、Kimi K2.7 Code 和 GLM-5.3 Flash。我们希望从第一次构建起就有 Git 历史记录,而不要求新用户注册 GitHub 账号,因此我们将源代码和 Git 包存储在自己的 R2 存储桶中。

Cloudflare 将推理部分描述为“在 GPU 上运行无服务器推理”。我们向模型发送请求;Cloudflare 运营着响应请求的 GPU 基础设施。围绕该推理调用,我们构建了编辑源代码、运行编译器、从中断的尝试中恢复以及在构建机器消失后保留结果的工具。

从提示词到经过检查的预览

假设你想要一个小型费用跟踪器。在 Build 中描述它,回答两个关于应用类型和视觉风格的可选问题,并在智能体编写 React 和 TypeScript 时跟进对话。要求添加另一个视图、CSV 导出或不同的布局,它会继续在同一个应用上工作。

后端通过 env.AI.run(model, payload)(Cloudflare 的 Workers AI 绑定)调用模型。请求包含对话历史、工具定义以及所选的推理设置。模型可以请求诸如读取文件、替换文件、运行构建命令或生成图像等操作。Mainbrella 验证并执行这些请求,将结果返回给模型,并继续循环。Cloudflare Workflows 协调构建步骤;Linux 客户机运行项目的工具链。

The Mainbrella build workflow sends prompts to Cloudflare Workers AI and receives model replies and tool requests. It sends commands to a temporary Linux guest and receives results. The workflow saves source and native Git bundles in R2, with version metadata in D1. The guest has no direct connection to the bucket.

这里有一个容易犯的错误:将模型的“完成”视为应用可用的证据。我们的平台在模型完成后运行自己的检查。它安装依赖项,然后直接调用 TypeScript 编译器和 Vite:

# Inside the app's Linux workspace
./node_modules/.bin/tsc --noEmit
./node_modules/.bin/vite build

实际的检查将这些命令连接起来,因此 TypeScript 失败会停止编译。直接调用工具很重要:应用生成的 package.json 无法重新定义 npm run build 来打印“success”并通过检查。编译器错误会返回对话中进行另一次修复尝试。一旦检查通过,我们提供编译后的 dist 目录用于预览。你看到的是构建的输出,而不是依赖可能容忍错误的开发服务器。构建工作流实现了这一边界。

干净的编译并不能证明费用跟踪器能正确计算所有内容。你仍然需要尝试你关心的行为。但它确实让“构建成功”具有了独立于模型所说内容的含义。

模型菜单目前通过 Cloudflare Workers AI 提供 GLM-5.3、Kimi K2.7 Code 和 GLM-5.3 Flash。我们公开它们支持的推理选项;Kimi 的推理是固定的,而 GLM 选项提供高和最高努力。如果应用需要原创图像,智能体可以使用 FLUX。这些图像成为本地资源,在预览中提供并包含在源代码导出中。一个普通的费用表格不会仅仅因为图像生成可用就需要图片。

对话还需要解释一次被中断的尝试。我们添加了一个操作日志,用于保留命令标识、输出以及与该操作关联的来源。编译器退出码、被截断的模型响应和断开的流是不同的结果。如果流在工具请求进行到一半时结束,我们不会执行不完整的参数。如果执行可能已经开始但没有保留结果,日志会记录这种不确定性。

没有 GitHub 注册的 Git 历史

一旦对话开始修改应用,我们就需要把代码检入 Git。“添加报表”是一次实验,而下一个请求可能是“把它放回去”。一堆被覆盖的文件无法让用户有效地检查那次实验,或恢复他们喜欢的版本。

我们不想让第一个应用想法变成注册 GitHub 并请求仓库权限。因此我们使用 Cloudflare R2 为仓库构建了自己的存储。原生 Git 在 Linux 机器中运行。R2 保存源对象、生成的资源和 Git 包;D1 保存版本记录和当前 head。仓库从一开始就属于 Mainbrella 应用,不需要 GitHub 连接。

源存储层按文件的 SHA-256 内容哈希寻址,位于以账户和应用为作用域的前缀下。清单将文件名映射到这些存储对象。这让仪表板无需运行访客即可读取文件,并让后续构建能在全新机器中重建源代码。我们费用跟踪器的预览可以过期,而其源代码仍可供浏览、检查或导出。

界面提供源代码 ZIP 和可下载的 Git 包:一个 Git 可以克隆到普通仓库中的文件。你可以在 Mainbrella 内检查版本、查看哪些文件发生了更改并恢复早期工作,或者把历史带走。

当后续请求过于雄心勃勃时,恢复尤其有用。假设版本 A 是你想要的小型跟踪器,而版本 B 添加了复杂的报表界面。恢复 A 会创建一个包含 A 文件的新提交,在历史中排在 B 之后。如果你后来决定 B 的报表值得保留,B 仍然在那里。

Commit A contains the simple tracker. Commit B adds reports. Restore creates commit C after B, copying A's source while retaining both earlier commits. The parent arrows follow A to B to C; a separate dashed arrow copies A's files into C.

让这段历史可移植带来了一项有趣的实现变更。最初的 Git 实现为每个新版本保存完整历史包。一段长对话会反复上传相同的祖先。新版本存储增量包:初始历史只存一次,然后存储每个后续提交所需的对象。未更改的保存会复用其父级的包。

下载不应需要启动付费机器。Worker 从保留的对象组装一个自包含的包。这里有一个微妙的 Git 约束:恢复旧文件可能导致某个对象在后续包中再次出现,并具有不同的增量表示。这些包无法通过简单拼接其对象部分来安全合并。我们为增量禁用了增量打包,保留普通的 zlib 压缩,因此导出可以组合完整的压缩对象,并重新计算包的对象数量和校验和。这牺牲了一些压缩效率,换来了无需运行沙箱的导出。

快照包含依赖锁文件和引用的生成图像,并排除 node_modules、dist 和环境文件。平台仅在存储对象存在后才发布新的历史 head。一旦你下载仓库,交接就是普通的 Git:

git clone -b main mainbrella-app.bundle expense-tracker
cd expense-tracker
git log --oneline
npm ci
npm run dev

你可以继续在自己的编辑器里工作,或者把应用托管到别处。保留的项目存储有其自身的资金和保留规则;把它与访客的生命周期分开,并不意味着可以永久无限存储。它的意思是,访客可以停止运行,而它不会成为你应用的唯一副本。

从仓库开始

你可能已经有代码了。在 mainbrella.com/try 粘贴一个 GitHub URL,并为 ChatGPT、Claude 或你的编码代理准备一个设置提示词。代理会检查项目,并提议运行时、工作目录、安装命令、启动命令和端口。在启动机器之前,你可以先审查配置。

仅凭分支名不足以描述你运行了什么。启动器会把仓库解析到某个具体提交,并在访客上检出该提交。如果安装失败,终端和失败命令的输出会保留下来,以便你修复那个检出。刷新页面不会自动重新运行失败的设置。启动器的恢复规则会在整个调试过程中保持相同的机器和命令标识。

私有仓库使用 Mainbrella Import,这是一个单独的 GitHub App,旨在对你选择的仓库进行只读访问。导入你的项目不需要向其推送的权限。访问检查使用你的 GitHub 访问权限与 Import 应用访问权限的交集,而不是信任浏览器提交的安装 ID。

私有导入实现通过后端获取 Git 对象,并将它们传输到访客。访客在选定的提交处重建一个浅检出;它接收源代码,但不会接收 GitHub 访问令牌。你可以在那里进行本地编辑和提交,但推送需要单独的具备写入能力的连接。

有一个区别很容易被忽略:连接 Import 会让 Mainbrella 访问该仓库。它不会让 ChatGPT 或 Claude 获得访问权限。准备设置的代理仍然需要自己的授权方式来检查源代码,例如你现有的检出。

让服务持续运行

临时机器适合试用仓库或编译原型。但它不适合托管朋友明天要使用的 API。我们引入了两种生命周期,并在仪表盘中分别展示:

生命周期是什么让它保持运行?
临时一个临时会话,受空闲和硬性截止时间限制。
生产可续期的计算预留、可用资金以及你的支出额度。

生产并不意味着预留一个无限期的租约,然后指望计费能跟上。账户以五分钟为增量预留计算资源,并且只有在获得下一次预留后才会延长运行时的截止时间。即使账户协调器停止发送心跳,运行时也会强制执行截止时间。无需让仪表盘保持打开;资金和每月支出上限决定了服务可以运行多久。

还有两种不同的故障需要处理。如果应用进程退出,shell 监督程序会尝试重启启动命令,延迟会逐渐增加,最多 60 秒。如果运行时本身停止,账户可以使用新的预留、固定的镜像和保存的启动命令尝试恢复。逻辑服务标识会在该运行时恢复后继续存在。这些机制在生产生命周期变更中。

恢复从镜像和启动命令开始。未保存的本地文件和内存不会回来。对于我们的支出跟踪器来说,需要留存下来的记录应放在外部持久存储中;仅写入客户机的 SQLite 文件在恢复时会丢失。这个第一版没有持久磁盘、副本故障转移、HTTP 健康监控或正常运行时间 SLA。机器就绪也不代表你的应用正在响应 HTTP 请求。这些是当前生产设计的局限,其实际部署还需要验证。

更改应用时保留地址

开发预览提供一个临时的、隔离的 URL,无需 DNS 设置。拥有其地址即可访问,且授权会过期。这在构建期间很有用。被收藏的应用需要一个附加到项目的地址。

项目添加一个稳定的公共端点。你在该端点后面发布一个运行中的机器和应用端口,然后在想让不同应用提供服务时更改绑定。项目的地址保持不变。项目端点不会启动机器或延长其租约,发布替换代次是一项显式操作。

One project address branches to two possible backends. The dashed previous binding leads to machine A on port 3000; the solid current binding leads to machine B on port 8080. The visitor's bookmark and DNS stay at the same project address.

项目网关的传输策略与开发预览不同。公共应用可以使用自己的 Authorization 头、cookie、流式响应和 WebSocket。网关会移除平台凭据,并阻止响应 cookie 声明共享父域。如果你的应用提供私有信息,它仍然需要自己的身份验证。

自定义域名是该同一端点的别名。管理流程会检查一个账户和项目特定的 TXT 记录以证明所有权,然后在提供主机名服务之前检查路由 DNS 和 HTTPS 证书就绪状态。在项目元数据中输入域名并不会发布它。默认端点支持和自定义域名支持是部署能力,仅配置支持并不能证明已完成的实时 DNS 和 TLS 验证。

作为第一次尝试,构建一个小应用并下载其仓库。或者启动一个 GitHub 项目,你一直想检查它。当你决定让某个东西持续运行时,把它的持久数据放在客户机之外,使启动可复现,并通过项目端点发布它。有用的测试是,你能否在不丢失工作成果或你给别人的链接的情况下更改机器。

来源:Hacker News · mainbrella.com