关于 create-railcode-app
面向使用 Railcode 的 Agent,提供应用开发与维护指引,涵盖 CLI 更新、应用代际、连接器、迁移和破坏性变更,并要求操作前核对当前版本。
主要功能
- 01CLI 与技能更新检查
- 02识别应用代际
- 03连接器迁移指引
- 04排查破坏性变更
- 05反馈提交流程
适用场景
- 01创建 Railcode 应用
- 02更新现有应用的连接器
- 03诊断部署回归问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ railcode-hq/ railcode-skills --skill create-railcode-app安装后在 Claude Code 中输入/create-railcode-app确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/app-patterns.mdcli-workflow.mddeployment.mdmigration.mdv1-legacy.mdworker-sdk.md详细介绍
创建 Railcode 应用
首先更新
在回答 Railcode 相关问题或运行 railcode 命令之前,请先更新 Railcode
技能和 CLI,然后确认 npm 上发布的版本:
npx skills add Railcode-HQ/railcode-skills
npm install -g railcode@latest
railcode --version
npm view railcode version
如果技能有所变化,请从头重新阅读本文件。如果无法访问 npm,请明确说明,不要
声称这些指导是最新的。本版本依据 CLI 0.3.10 和
@railcode/sdk 0.5.1 编写(两者均已确认于 2026-10-28 发布)。
本文档以 0.3.0 为最低版本要求。 从该版本起,个人连接器已并入
railcode connector(参见重大变更)。较旧的二进制版本中不存在
connector link / add-mcp / share 子命令,而 railcode personal-connectors 命令仍然存在;如果该命令能正常运行,说明你使用的 CLI 不在本文档
适用范围内。请先重新安装,再按下文操作。
每用户连接器需要 CLI 0.3.9 和 @railcode/sdk 0.5.0。 较旧的 SDK 中,
ApiError.connectionRequired 为 undefined,且不存在 connector().connection();较旧的
CLI 中,railcode connector connect / status / link --per-user 都是未知命令。平台端
的功能已上线,因此使用 SDK 0.4.0 的应用仍会收到 409,只是必须手动解析
detail.code。建议升级。
CLI 只会在同一主版本内自动更新,而且仅在交互式终端中进行;Agent 驱动的会话
属于非交互式,因此请继续运行上面的显式命令
npm install -g railcode@latest,不要想当然地认为自己用的是最新版本。
反馈 Railcode 使用体验
完成有实质意义的 Railcode 工作后,如果你有具体的产品反馈,请使用隐藏命令
railcode agent-feedback,例如报告错误、使用阻碍、建议或特别出色的体验。每个不同的发现
提交一次。请描述任务、预期行为和实际行为,以及任何变通方法;报告应简洁,并省略
凭据、客户隐私数据、源文件和对话记录。
railcode agent-feedback --message "Deploy failed with an unclear error; expected the missing field to be named." \
--category friction --command "deploy" --context "Static app deploy; corrected the manifest to unblock."
如果报告已准备好,可使用 --file <path>;也可以通过标准输入传入文本,而不使用
--message。消息长度上限为 10,000 个字符;--context 为可选项,长度上限为
5,000 个字符。类别包括:bug、friction、suggestion、praise、other
(默认值)。可以使用 --agent 自行报告助手/工具名称(例如
Codex 或 Claude Code),并使用 --model 报告模型名称(如已知)。这些信息由你自行填写,可能不准确;未知时请省略,不要猜测。这些字段和 --command 的长度上限均为 200 个字符。
CLI 会附上其版本、操作系统平台、CPU 架构和 Node 版本。后端会在 PostHog 中将报告关联到已登录的用户和组织。此命令要求已有登录状态;在应用目录之外也能使用。Feedback accepted.
表示已尽力接收,并不代表确认已存储。如果提交失败,请继续原任务,不要反复重试,也不要仅为提交反馈而登录。
此命令有意不显示在 CLI 帮助中,并且需要 CLI 0.3.3 或更高版本。较旧的 CLI 可能会返回
Unknown command;此时应将其视为不可用。
重大变更
按时间倒序记录会导致现有应用或 Agent 无法继续正常运行的平台变更。当用户报告之前 可用的功能现在失效时,开始调试前请先阅读此处——平台自身的错误消息旨在提供修复指引, 下方的条目会告诉你该如何处理。每当发布某项现有应用无法在不做修改的情况下继续运行的变更时,都应在此添加条目。
公开应用 — CLI 0.3.10、@railcode/sdk 0.5.1(不构成破坏性变更,但有一条规则反转)
所有者或组织管理员现在可以将应用设为公开(railcode apps set-access <app> --public on,或通过实例提供的「访问权限」页面操作)。对于访客和 cron,ctx.user 现在都为 null,因此「调用者为 null 就表示调度器」不再成立:请使用 ctx.isGuest 或 ctx.trigger。在有人启用此功能前,先限制仅限成员访问的路由 — 参见
app-patterns.md。
每用户连接器 — CLI 0.3.9、@railcode/sdk 0.5.0(不构成破坏性变更,但有两条规则反转)
连接器现在可以设为每用户:只有一个名称,没有自己的凭据,且每位成员都在其中连接自己的帐户。connector("granola").call(…) 没有变化,但现在调用者尚未连接或尚未批准此应用时,它可能会返回 409 connection_required。此功能发布后,有两条陈述变成了有条件适用:
- “不要构建连接流程。”(下方的迁移表)。对于共享和自有连接器,这仍然成立。对于每用户连接器,应用确实会转发浏览器 — 转到平台的连接页面
err.connectionRequired.connectUrl(加上&return_to=),绝不会转到应用自己的 OAuth URL。 - “现在 cron 可以正常工作。”(worker-sdk.md → Connectors someone owns
personally)。这适用于共享和自有连接器。每用户连接器在 cron 下没有调用者,因此会返回
connector_needs_user。
现有功能不会受到影响:共享或自有的连接器行仍与之前完全一致。参见 worker-sdk.md → Per-user connectors 和 cli-workflow.md → Per-user connectors。
个人连接器已移除 — CLI 0.3.0、@railcode/sdk 0.4.0
personalConnections 和 personal_connectors: 清单键已移除。现在连接器只有一种:在 connectors: 下声明的带有所有者和访问模式的组织资源,无论它持有的是团队共享凭据,还是某个人自己的账户。共享前,所有者行的访问模式为 restricted(仅所有者、管理员及与其共享的人可访问)。
使用方式
- v1 应用或 v2 worker 调用
personalConnections.*时会收到 HTTP 410,而不是 404。响应正文会说明替代该工具包的连接器、列出调用者拥有的连接器,并附上可复制粘贴的替代调用。请阅读响应正文,答案就在其中。 - 如果清单仍包含
personal_connectors:,执行railcode deploy会失败,并提示 "personal_connectorswas removed — link the account as a connector … and name it underconnectors:"。应用仍会继续提供服务;只有部署会被拒绝。 - 托管 Agent 无需做任何处理。 平台已就地改写其清单、提示词和授权行。如果 Agent 仍有异常,那并非由此导致。
后续操作
-
执行
railcode connector list— 账户已经在列表中。凭据已迁移;无需任何人重新授权。 -
检查名称。 如果同一组织中的两个人持有同一提供商的账户,或者普通名称已被占用,该行名称会加上后缀 —
gmail可能现在是gmail-jp。请使用connector list中的名称,而不是旧工具包 ID。 -
替换调用。worker SDK 和 v1 浏览器 SDK 的接口相同:
旧调用 新调用 personalConnections.call("gmail", "send_email", args)connector("gmail-jp").call("send_email", args)personalConnections.tools("gmail")connector("gmail-jp").tools()personalConnections.list()serviceConnectors()personalConnections.connect("gmail")共享行或所有者行没有应用端的对应调用。 关联账户的操作已移出应用:用户运行 railcode connector link gmail,或从控制面板进行关联。请勿在应用中构建连接流程。例外情况是按用户配置的连接器:应用会将浏览器转发到平台的connectUrl— 详见上文条目和按用户配置的连接器。 -
在清单中将
personal_connectors:替换为connectors:,并指定行名称及其可调用的工具 — 例如connectors: { "gmail-jp": ["send_email"] },或者使用["*"]允许调用该行中的所有工具。然后重新部署。
v1 应用无需为此迁移到 apps v2。 connector() 已包含在从 /_api/sdk.js 提供的平台 SDK 中。但应用确实需要重新构建,因为调用位置已编译到应用包中 — 因此,只有仍持有应用源代码的人才能进行此操作。
项目与应用
Railcode 控制台现在使用 项目 一词,以前则使用 app:我的项目、新建项目、项目的设置页面。改用这个词,是因为部署的内容不一定是供人通过浏览器打开的东西——它可能是 Web 应用、Agent、API,也可能是完全没有 UI 的 worker。“项目”一词涵盖了所有这些情况。
项目和 app 是同一个对象。 只有控制台中的用词发生了变化。CLI(railcode apps、railcode init、railcode deploy)、railcode.json、API 和控制台 URL(/apps/<slug>)仍使用 app,本技能其余部分也一样。请将这两个词视为同义词:
- 用户说“我的项目”“项目设置”或“分享项目”时,指的就是 app。请使用
app命令和 app slug;没有railcode project命令。 - 与用户交流时,请沿用他们使用的词。从控制台来的用户会说“项目”;不要纠正他们。
- 在控制台的 新建项目 对话框中创建的项目,已在组织中存在,但尚未部署任何内容。请使用完全相同的 slug——新建文件夹时使用
railcode init <slug>,或在现有代码的railcode.json中将"app"设为相同的值——这样部署内容就会进入该项目,而不是新建第二个项目。 - 只有一处两者含义不同:在 Railcode 之外,“项目”也指源代码文件夹或仓库(
migrate-to-railcode技能就是这样使用的)。如果无法确定用户指的是哪一种,请先运行railcode apps list检查是否有匹配的名称,再询问。
这里所说的作为 Agent 的项目仍然是 app:由你编写、在 worker 中运行的代码。托管 Agent 则是另一回事:由 Railcode 代为运行的清单,在控制台的 Agents 下列出,使用 create-railcode-agent 技能构建。
首先:确认属于哪一代?
Railcode app 有两种形态,下面几乎每条规则都取决于你面对的是哪一种。请先确认这一点。
- 新 app 一律属于第 2 代(apps v2)。 没有选项、没有标志,也没有
railcode.json键。由服务器分配。 - 现有 app 可能属于第 1 代(v1)——旧版浏览器 SDK 形态。现有 app 已统一回填为第 1 代,并会保持不变,直到有人明确迁移它们。
railcode apps show <app> --json | grep generation # 1 = legacy, 2 = apps v2
纯文本输出不会显示所属代际;请使用 --json。
| 情况 | 操作 |
|---|---|
| 构建新 app | Apps v2。 继续阅读本文档。 |
修改 railcode.json 中有 "type" 和 "server" 的 app | Apps v2。 继续阅读本文档。 |
修改 index.html 中加载 /_api/sdk.js 的 app | 第 1 代。 阅读 v1 legacy——这里的大部分规则不适用。 |
| 修改 v1 app,而请求需要 v1 无法提供的功能——密钥、后端、cron 或必须生效的授权 | 迁移,并告知用户。 接下来阅读 When a v1 App Can't Do What's Asked。不要构建浏览器端变通方案。 |
| 用户想将 v1 app 重建为 v2 | 先阅读 Migration。迁移是单向的,并会有一段停机时间。 |
如果无法连接服务器进行检查,请根据源代码树判断:index.html 中有 /_api/sdk.js 表示 v1;railcode.json 中有 "server" 键表示 v2。
当 v1 应用无法满足需求时
v1 应用由一个页面和一个浏览器 SDK 组成。它没有 worker,因此没有密钥、没有 cron、没有服务端代码,也没有应用可以信任的调用方。当请求需要其中任何一项时,平台的答复就是“这需要 worker”——而 v1 应用必须迁移后才能获得 worker。“给这个应用添加 X”这句话,若 X 属于下表中的任何一项,就意味着需要迁移。
| 请求需要…… | v1 为什么做不到 | v2 中的方案 |
|---|---|---|
| 浏览器不应看到的凭据——AWS 密钥、签名密钥、第三方 API 密钥 | 没有密钥。页面能读取的任何内容——KV、设置集合、打包的常量——所有能打开应用的用户都能读取 | worker 中的 secrets.NAME |
| 连接器代理无法通过身份验证的 API——按请求签名(SigV4)、mTLS、自定义握手 | 连接器只支持 bearer/header/query/basic 身份验证,而且没有服务端位置可运行签名代码 | worker 通过 egress: 调用该 API |
| 定时运行的任务 | 没有 cron;除非页面处于打开状态,否则不会运行任何内容 | 使用 crons: 条目调用 worker 路由,或使用 Agent 自身的计划安排 AI 工作 |
| 必须始终成立的规则——“X 提交,Y 审批,X 不能审批自己的提交” | 页面端检查仅供参考;任何用户都可以通过开发者工具绕过 | 在 worker 中根据 ctx.user 进行检查 |
| 服务端工作——汇总多条记录、串联多个服务,或任何对标签页来说过于繁重或敏感的工作 | 没有任何内容会在服务端运行 | worker 路由 |
规则:迁移、告知用户,然后继续。 当请求符合表中的某一项时,交付内容应包括迁移和功能,而不是功能的 v1 近似实现。
- 不要实现权宜方案——把密钥放在 KV 中供页面签署请求、用轮询循环代替计划任务,或在标签页中进行审批检查。
- 提前用一段话说明,然后继续: 说明请求需要什么、v1 无法提供该能力、应用将迁移到 v2,以及采用哪种路径——使用新 slug(路径 A——可逆,优先选择)还是原地迁移(路径 B——不可逆,需要停机窗口)。不要暂停工作来询问是否迁移。
- 如实评估工作量。 所有页面端数据调用都要移到 worker 后面,但这只是将应用唯一的 SDK 封装改写为调用 worker 路由的
fetch(),而不是重写整个应用:视图、状态和业务逻辑都保留。railcode-examples中的apps/crm是一个大型 v1 应用,且只通过一个模块就完成了移植。 - 只有迁移关卡需要暂缓。 路径 B 中,
railcode migrate不可逆,而且线上应用会一直停机,直到下一次部署,因此应先完成并验证 v2 构建,再取得明确许可后运行它。路径 A 不会触及迁移关卡。
请阅读迁移了解操作流程。如果用户了解以上所有内容后仍希望使用 v1 版本,就按要求构建,并明确说明有哪些妥协。仅限于 v1 现有能力范围内的小改动——新增视图、修复问题、添加字段——无需执行这些步骤;请按照 v1 legacy 在原处完成。
清除任何 v1 SDK 记忆
如果你开始开发 v2 时,带有 apps v2 之前写下的记忆——Agent 记忆文件、
CLAUDE.md / AGENTS.md 笔记、保存的项目经验、规则文件或加星标的代码片段——
请假定其中描述的是 v1 浏览器 SDK,如今已不再正确。先将其清除,然后再编写任何
代码,并明确说出你正在这样做。如果这些笔记不归你编辑,请让用户运行以下操作:
删除所有关于 Railcode 浏览器 SDK 的已存笔记——
/_api/sdk.js、window全局变量 (me、roles、appUsers、designSystem、db、files、data、query、connector、serviceConnectors、llm、agents,以及该组中的其他变量)、 页面端授权和run_as: user。这些内容在 apps v2 中都不存在。
删除,不要追加——同时回忆 v1 和 v2 笔记会形成混合记忆,在 railcode dev 下运行正常,
却会在平台上失败。如果你仍在维护 v1 应用,请将每条笔记的范围改为以
v1 apps only (generation 1): 开头,而不是删除,并将
v1 legacy 作为实际信息来源。
将请求映射到 Railcode
选择架构前,请先使用此表。如果请求提到外部产品或数据源,在判断有哪些可用资源之前, 务必先检查数据连接/已保存查询以及连接器;发现命令见构建步骤 1。
| 用户的需求 | 使用此 Railcode 功能(均从 worker 调用) |
|---|---|
| “显示我们数据库中的公司指标/订单/客户” | 通过 query() 使用已保存查询(默认);仅在明确要求时使用数据连接 + data().runSQL() |
| “让用户使用自己的 Gmail、Slack 或其他账号” | 用户关联并拥有的连接器(railcode connector link gmail),在 connectors: 下命名。自有行在共享前均为 restricted——见连接器 |
| “所有使用此应用的人都应以自己的身份操作——查看自己的会议、收件箱、日历” | 使用按用户配置的连接器(railcode connector link granola --per-user):使用同一个名称,每位成员运行 railcode connector connect granola(或使用平台的连接页面)。清单和调用位置相同;worker 会通过将浏览器跳转至 connectUrl 来处理 ApiError.connectionRequired。仅限 OAuth 提供方;不支持 crons——见按用户配置的连接器 |
| “连接 Railcode 未内置支持的产品账号” | railcode connector add-mcp <name> <https-url>——将远程 MCP 服务作为你拥有的连接器 |
| “使用团队共享的 Stripe、CRM 或其他 SaaS 账号” | 使用组织管理的连接器(没有成员所有者;凭据由管理员保管),通过 connector().fetch() 或 connector().call() 调用。成员是否可以访问,由该行单独的 access_mode 决定 |
| “存储应用设置、草稿、审批或轻量级记录” | db——一个扁平存储;分区和访问策略由你的 worker 代码负责 |
| “上传、存储、下载或显示文件” | files(服务端平面:文件字节由你的 worker 持有) |
| “使用 AI 读取、提取、总结、转换或生成文件” | 托管 Agent——见Worker LLM 与托管 Agent |
| “在用户等待时总结或分类数据” | 在 worker 中使用 llm.generate() / llm.stream() |
| “按计划运行” | 添加 crons: 条目以调用你的某个 worker 路由——或使用 Agent 自带的计划 |
| “在后台运行、从 Slack 启动,或在标签页关闭后继续运行” | 托管 Agent——见Worker LLM 与托管 Agent |
| “发送由系统管理的事务性邮件” | 使用 email.send();如果邮件必须来自特定人员自己的账号,则使用 Gmail 连接器 |
| “调用任意网站/API” | 在 egress: 下声明主机,或使用连接器。默认允许列表仅包含数据平面 |
从示例开始
railcode init 是新应用的起点。默认的 hono+vite 模板会搭建入门用的 欢迎应用;hono+static 和 tanstack 会搭建一个小型 平台导览(身份信息、db 上的待办列表、文件和只读组织界面)。这两种模板都适合阅读,然后删除。
如果要构建超出导览范围的应用,请阅读 railcode-examples。其中的每个应用都是 第 2 代——frontend/ + server/index.ts,也就是你正在构建的结构——因此可以放心复制;每个示例都旨在讲解一个经验:
| 示例 | 阅读目的 |
|---|---|
apps/kanban | 最简单的工作器应用。共享存储,以及一条非对称规则(只有作者或管理员可以删除),规则写在调用方无法触及的地方。 |
apps/chat | 在工作器中运行 Agent 循环并向页面流式传输 ndjson;将每用户隔离重建为键加所有者检查(server/keys.ts);批量获取文件 URL,并为非 S3 存储提供回退方案。 |
apps/crm | 一个通过单个模块移植的大型 v1 应用,以及必须留在浏览器中的工具循环,因为其写入操作需要等待人工点击批准。 |
agents/pitch-deck | agents.start() + 轮询、刷新后重新连接的运行,以及无需桥接即可将 Agent 输出写入应用自身的存储。 |
agents/proposals | 为什么计划任务属于 AGENT,而不是应用:cron 没有调用方,因此从 cron 调用 agents.start() 会返回 409。 |
复制整个示例作为起点:
mkdir my-app && curl -fsSL \
https://github.com/Railcode-HQ/railcode-examples/archive/refs/heads/main.tar.gz \
| tar -xz --strip-components=3 -C my-app railcode-examples-main/apps/kanban
然后在 railcode.json 中将 app 设置为你的 slug。若要在不复制的情况下查看某个文件,请从 https://raw.githubusercontent.com/Railcode-HQ/railcode-examples/main/<path> 获取其原始内容。
构建流程(按顺序执行)
完成步骤 1–2 之前,不要开始编写应用代码。
1. 构建前先询问
先问用户几个简短问题,明确应用的范围——一次性把所有问题都问完。 只问会影响设计的问题,其余事项选择合理的默认值,并说明这些默认值。
所有问题都要用不了解 Railcode 内部机制的非技术用户能听懂的方式来提问:询问用户的意图,让回答决定所需的基础构件,不要直接说出它们的名称。“每位用户只能看到自己的记录,还是所有人都使用相同的数据?”——而不是“worker 应该如何划分扁平存储?”。*
提问前,先根据下方的限制检查用户的需求。如果需求涉及 Railcode 无法实现的功能,请先明确说明,并提出最接近的受支持方案。
必须发现外部数据源。 只要用户要求构建的应用要读取、写入、同步、搜索或操作某个具名产品(“X”)中的数据,就不要假设需要新建集成。先检查所有三个平面:
railcode db list # data connections
railcode query list # admin-published saved queries
railcode connector list # connectors you can reach: owned, shared, org-managed
railcode connector catalog # providers that could be linked, if none exists yet
如果发现可能适用的选项,先检查其实际接口,再据此进行设计(http 行使用 railcode connector docs <name>,mcp 行使用 railcode connector tools <name>)。绝不要臆造连接器名称、端点或工具标识。如果无法连接到该实例,请询问用户已配置的内容,并向他们展示这些命令;本地结果为空并不能证明 X 不受支持。
如果没有合适的选项,请说明缺口,并提供实际可行的后续选择,不要悄悄省略该集成:让管理员连接数据库并发布已保存查询;创建组织管理的连接器以共享凭据;将捆绑的提供商关联为连接器;或通过 URL 添加 X 的远程 MCP 服务(railcode connector add-mcp)。如果 X 既没有 API/数据库,也没有远程 MCP 服务,请说明 Railcode 无法直接访问它,并询问用户要使用哪个受支持的数据源。
至少涵盖以下内容:
- 功能与用户 — 应用要做什么,谁会使用?(这会影响访问策略,以及 worker 如何划分扁平存储)
- 数据 — 应用要存储或读取什么?按用户区分还是共享?外部数据库 → 使用管理员发布的已保存查询,除非用户明确要求直接执行 SQL。第三方 SaaS → 使用服务连接器。是否使用任何 LLM?如果涉及 AI,要明确其形态:是处理文件、运行代码,还是需要在请求结束后继续运行?只要有一项是,就使用托管 Agent。
- 技术栈 — 默认使用
hono+vite。小型应用可选hono+static;用户需要基于文件的路由和服务端函数时可选tanstack;用户要求使用 Next.js 或需要服务端渲染页面时可选next(CLI 0.3.8 或更高版本;请先阅读references/cli-workflow.md中的限制);完全不需要后端时可选static。 - 设计 — “我应该使用默认的 Railcode 设计系统,还是你有特定的设计方向?”
- 浏览器测试 — “在完成前,我是否应该在浏览器中测试所做的更改?”
2. 获取设计系统(如果用户需要)
railcode login # once, if not already logged in
railcode design-system
此命令会输出组织的设计系统指南(markdown)。如果返回为空或没有服务,请跳过此步骤,改用下方的视觉方向。
3. 构建应用
railcode init <app> [dir] [--template hono+vite|hono+static|tanstack|next|static]
cd <app>
npm install
railcode dev
构建由 CLI 负责。 应用不声明打包工具、wrangler、Cloudflare 软件包或 worker 构建脚本。编写 server/index.ts 和前端;railcode deploy 会生成平台所需的单个自包含 ESM 模块。
接下来遵循下方的实现规则,并在 railcode.json 旁编写 manifest.yaml。
在 v2 中,必须设置 run_as: app。 只声明 worker 实际使用的功能,不能多声明。部署前先验证:
railcode manifest validate
4. 完成前先测试
运行验证中列出的检查。完成工作前修复发现的问题。
5. 部署(用户希望上线时)
railcode deploy
如需在每次推送时部署,请在项目中运行 railcode ci github:该命令会生成应用专属的
部署令牌,通过 gh 将其设为仓库密钥,并写入工作流。切勿在 CI 中使用
个人令牌。
决定加载哪些内容
只加载任务所需的参考资料:
- Worker SDK —
@railcode/sdk的接口:ctx、db、files、llm、agents、query、连接器、email、secrets。v2 开发的主要参考资料。 - App patterns — Worker 路由、前端与 Worker 的连接、数据 建模、Worker 代码中的授权、定时任务、错误转发。
- CLI workflow — 精确命令:init/dev/deploy/secrets/logs/ migrate、清单权威性、应用访问权限。
- Deployment — 部署解析、访问模式、验证。
- Migration — 将现有 v1 应用转为 v2 应用,包括 遇到 v1 应用无法实现的需求时该怎么做。
- v1 legacy — 基于浏览器 SDK 的平台,仅用于维护现有的 第 1 代应用。不要基于它构建任何新内容。
实现规则
将应用拆分为两部分,并保持清晰的边界。
前端是静态的。它不存储凭据、不调用平台,也不拥有任何权限。它只做一件看起来需要特权的事:通过 fetch() 请求你自己的 worker 在 /api/* 下的路由。v2 页面唯一可以调用的平台端点是 /_api/me 和 /_api/logout(chrome 栏会使用它们);通常应从你自己的 worker 获取身份信息。
worker 会导入 @railcode/sdk,所有实际操作都在这里进行。使用满足需求的最精简接口:
| 需求 | Worker SDK 接口 |
|---|---|
| 调用者身份 | ctx.user(已验证;cron 和公开应用的访客为 null——ctx.isGuest) |
| 记录、设置、草稿 | db——一个扁平存储;分区由你负责 |
| 文件 | files.put/get/url/urls/list/delete |
| 数据库读取 | 默认使用 query() / savedQueries();仅在有要求时使用 data()/postgres()/bigquery()/turso() |
| 第三方账户,共享或个人所有 | connector('name').fetch()(http)或 connector('name').call()(mcp)——两者共用一个接口;所有权和访问模式保存在记录中 |
| AI | llm.generate() / llm.stream(),或 agents.start() + 托管 Agent——参见 Worker LLM 与托管 Agent |
| 组织成员目录 | appUsers() |
| 每个应用的密钥 | secrets.NAME |
| 系统发送的邮件 | email.send() |
授权由你的代码负责,除此之外没有任何机制会代你处理。 应用访问控制决定谁可以打开应用。此后的所有操作——谁可以编辑、批准、删除,以及谁可以查看谁的记录——都需要你在 worker 中根据 ctx.user 编写检查。现在没有任何作用域存储会隐式处理这些事:db 是一个扁平存储,因此“每用户”意味着由你使用 ctx.user.id 作为键,并由你在读取时进行检查。
绝不要信任请求正文中用于标识身份或所有权的值。 从 ctx.user 读取。v2 worker 唯一无需额外处理就能获得的,是一个可信的调用者。
公开应用也会向陌生人开放。 所有者或组织管理员可以开启公开访问(任何拥有链接的人都能访问,无需登录)。此时,已退出登录的访客,以及登录了其他组织的用户,都可以通过 ctx.user === null 和 ctx.isGuest === true 访问每个 worker 路由,并使用清单授权的任何功能(llm、email、已保存查询、连接器),费用由组织承担。在启用公开访问前,先用 ctx.user 限制仅限成员访问的路由;并且绝不要把空调用者当成“这是 cron”——请检查 ctx.trigger。参见 app-patterns.md。(需要 CLI 0.3.10+ / SDK 0.5.1+,且实例已启用公开访问。)
为每个顶层部分分配独立路径(/companies、/companies/acme)——绝不要将导航状态保存在内存中的 view 变量里。深层链接、强制刷新以及前进/后退都必须正常工作;这些应用会被链接到 Slack 和工单中。Railcode 托管会回退到 index.html,因此无需配置,客户端路由也能正常解析。
在 /api/openapi.json 描述你的路由。 在 worker 中提供一份 OpenAPI 文档(将其放在 server/openapi.ts),并在路径参数和正文架构中设置 required。Agent 通过 Railcode MCP 调用应用时,会依靠这份文档了解路由及其参数,而不是自行猜测;没有它,错误的路径和错误的方法都会返回 404。参见 app-patterns.md。
传递 SDK 的错误状态码。 worker 路由包装 SDK 调用时,应捕获 ApiError 并使用其 .status 和正文作出响应——前端对 403/409/429 的处理依赖于错误状态码能经由这一额外环节传递。若将其吞掉并转换为 500,平台提供的所有类型化错误都会丢失。
Worker LLM 与托管 Agent
在 v2 中,页面内没有 LLM——llm 在 worker 中运行。如今真正的界限在于 worker LLM 与托管 Agent,区别在于能力,而非复杂程度:
| AI 功能…… | 使用 |
|---|---|
| 在几秒内总结/分类 worker 已读取的数据 | Worker llm |
| 读取、理解、提取、转换或生成任何文件 | 托管 Agent(app_files + 沙箱)。worker 可以 put/get/list/delete 文件,但绝不能把文件内容或文件 URL 传给 llm.generate() 来代替 |
| 编写并运行代码 | 托管 Agent(沙箱) |
| 必须在请求结束后继续运行、需要重试或需要耗时数分钟 | 托管 Agent |
| 由 Slack 或 Agent 定时任务触发 | 托管 Agent |
| 需要可供审计的运行历史记录 | 托管 Agent |
这两个平面可以组合使用:worker 自行处理快速任务,并通过 agents.start() 委派繁重步骤,然后轮询运行状态。请参阅 Worker SDK。
限制
遇到下表中的请求时,请一开始就说明,并提供最接近的受支持方案。不要悄悄构建无法运行的近似方案。
平台形态
| 无法实现 | 原因及最接近的受支持方案 |
|---|---|
| 面向公众或客户的应用 | 每位查看者都必须是已登录的组织成员——不支持匿名访问,也不支持自行注册。这些是内部工具 |
| 入站 webhook/公共 API 端点 | worker 只能在已认证的应用请求或你自己的 cron 任务中运行。改为通过 cron 轮询数据源,而不是接收事件 |
| 任意出站调用 | 出站流量采用允许列表。请在 egress: 下声明主机(精确名称或一级通配符;不能包含协议、端口或路径);默认情况下只能访问数据平面 |
| 实时推送(WebSocket、SSE 订阅、在线状态) | 不提供服务端推送接口;UI 通过轮询获取数据。支持请求范围内的流式响应——toNdjson() 可将任何可迭代对象(llm.stream() 或你自己的对象)流式传输到页面 |
| 超出预设支持范围的 Next.js | Next.js 本身可以运行:railcode init <app> --template next(App Router,页面使用其常规 URL;请参阅 references/cli-workflow.md 中的“Next.js and worker-first routing”)。不支持:Next 16.4(可以构建,但之后每个请求都会失败——请将 next 保持在 ~16.3.8)、图像优化器、next/og 及其他任何会加载 .wasm 的功能,以及定时 ISR(export const revalidate = N)。Pages Router 未经过测试。如果 CLI 没有 next 模板,或服务端没有 worker_routes(二进制版本过旧,或可自托管的服务端版本落后),migrate-to-railcode 技能中的 Next.js 指南提供了一个将应用挂载到 /api 下的备用方案 |
| 原生移动应用、推送通知 | 应用是位于 <app>.<parent> 的自适应 Web 应用。自定义域名与应用代码无关:这是组织级附加功能,由管理员在仪表板的 Settings → Custom domain 中配置 |
| 在前端代码中使用自带 API 密钥 | 前端不保存任何内容。请在 worker 中使用 secrets,或使用连接器 |
Worker 运行时
| 限制 | 数值 |
|---|---|
| worker 必须是单个自包含的 ESM 模块 | 使用代码分割、CJS 或引用依赖项的 worker 可以部署,但会在调用时崩溃。CLI 可确保其模板符合此要求;自带 worker 则需由你自行负责 |
| 子请求预算 | 每次调用约 100 次。批量操作请使用 files.urls(),不要循环调用 files.url() |
| 模块大小 | 10 MB(没有 worker_routes 的服务端为 5 MB) |
| 密钥 | 每个应用最多 64 个,每个值最多 5 KB,只写 |
| 每日上限 | 每个应用的 LLM 令牌数和邮件数;超过上限时,两者都会返回带类型的 429 |
| 调用日志 | 目前无限期保留(计划按套餐设置保留期限);每次调用的摄取量设有上限(200 行/256 KiB),超出部分会被丢弃 |
数据
| 约束 | 详情 |
|---|---|
db 是一个扁平存储 | 不支持联接、事务或聚合。分区是关键设计。将大型数据存放在数据仓库中,并通过已保存的查询读取 |
KV list() 仅返回第一页 | 默认 100 条,最多 500 条——必须在工作器中分页,否则会悄无声息地丢失后续数据 |
| 不支持嵌入或向量搜索 | LLM 网关仅支持文本输入和文本输出 |
Agent 和 cron
函数 cron 目前处于 alpha 阶段,后续规则可能会调整。 以下规则目前确实适用,拒绝也都是有意为之,但相关限制仍在积极审查中。不要构建核心流程依赖 cron 执行本表所述不支持操作的应用——请参阅 app-patterns.md,了解受支持的替代方案。
| 约束 | 详情 |
|---|---|
| cron 调用没有调用者 | ctx.user 为 null,ctx.trigger 为 "cron"。以下所有限制均由此而来 |
| cron 无法启动 Agent 运行 | 运行归属于 (app, caller),因此由 cron 启动的运行没有所有者——返回 409。请改为给 Agent 设置自己的计划 |
| cron 也无法轮询 Agent 运行 | agents.get() 会匹配相同的 (app, caller),因此 cron 无法读取由 http 调用启动的运行——同样返回 409 |
| 按用户配置的连接器无法与 cron 配合使用 | 按用户配置的连接器使用调用者的账户运行;cron 没有调用者,因此 fetch/tools/call 会返回 409 connector_needs_user(connection() / checkHealth() 也一样) |
| 若要在某人的账户上安排任务,请使用该用户拥有的连接器 | 所有者行的凭据属于该行,因此 cron 路由可以调用它(connectors: { "granola-jp": ["*"] });只有所有者(及其共享对象)可以批准它。也可以改用通过 railcode agent schedule 设置独立计划的托管 Agent——Agent 也无法指定按用户配置的连接器 |
| 你自己的授权代码必须处理调用者为空的情况 | 共享路由读取 ctx.user.roles 会在 cron 下抛出错误——或返回所有内容。扁平存储不强制执行任何规则。请用 if (!ctx.user) 进行检查 |
| 其他功能在 cron 下仍可使用 | db(包括 db.scoped)、files、data/postgres/bigquery/turso、query、llm、email、appUsers()、secrets、出口,以及组织/服务连接器——v2 工作器的权限来自其 run_as: app 清单,而不是调用者 |
| cron 限制 | 每个应用最多 5 个计划,最短间隔为 1 分钟。如果当前部署没有工作器,计划会暂停并显示原因 |
| cron 至少执行一次,且可能并行 | ctx.invocationId 是你的幂等键。切勿承诺“恰好执行一次” |
| cron 通过 POST 分发 | 仅声明为 GET 的路由每次触发都会返回 404,看起来像是计划失效 |
| Agent 运行绝不会在当前调用中等待完成 | agents.start() 会返回一个已排队的运行;agents.get(request_id) 会读取该运行。工作器 SDK 没有任何等待运行完成的调用,而且循环调用 get() 也不算——请从前端或后续调用中轮询 |
| v2 应用优先使用组织 Agent | 组织 Agent 会写入应用的共享作用域,该作用域就是 v2 应用的扁平存储。个人 Agent 会写入其所有者的用户作用域;迁移后的应用中,该作用域已冻结且只读 |
| 工作器无法访问 | 组织的角色列表和设计系统指南。ctx.user.roles 可获取调用者自己的角色;请在构建时通过 railcode design-system 获取设计指南 |
视觉方向
将脚手架视为功能性支架,而非样式指南。
如果用户选择了 Railcode 设计系统,请使用 railcode design-system 获取并遵循它。否则,默认采用其风格:低调的内部工具界面、中性色表面、紧凑控件、清晰的表格和列表、适度的边框与圆角,以及克制的强调色。
应用必须支持响应式布局——在桌面和移动端宽度下验证主要工作流,确保没有文本重叠、控件被裁切或表格无法使用。
为每个应用提供一个网站图标。 这些工具会被固定在一排标签页中,因此空白图标会带来实际影响。绘制一个简洁的 SVG 来体现应用用途——例如,流水线用漏斗,看板用板块——使用强调色,并添加链接:
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
将它放在前端的静态根目录中(Vite 技术栈放在 public/,hono+static 放在 index.html 旁边)。确保它在 16px 下仍清晰可辨:一个形状,不要包含细节或文字。在同一个文件中设置一个有效的 <title>——它会显示为图标旁的标签。
本地开发
railcode dev
同时运行前端和 worker,并提供与生产环境完全一致的路径(/api/*、/_serverFn/*)。worker 通过 HTTP 调用 CLI 的本地数据平面,使用与生产环境相同的线格式,因此**“在 railcode dev 中可用”就意味着“部署后可用”。**
db/files 使用本地临时存储(--reset 会清除它;开发过程绝不会触及线上数据)。受管控的能力——SQL、保存的查询、大模型、邮件、连接器、Agent——会在携带你身份的开发令牌下转发到真实实例。这些操作会使用真实的提供方、真实数据并产生真实费用,包括发送邮件和启动真实 Agent 运行。
可接受的限制:仅支持单一身份,密钥来自本地环境,cron 需要手动触发。
本地开发存储与已部署应用的存储相互独立:railcode app kv / railcode app files 读写的是应用的线上数据,绝不会读写本地模拟数据。
验证
cd <app>
railcode dev # confirm the frontend loads and its /api routes answer
railcode manifest validate
检查 worker 自身的日志。 这是 v2 的调试界面,v1 中没有对应功能:
railcode logs app --app <slug> # invocations: path, status, duration, who
railcode logs app <invocation_id> # one full trace: console lines, errors, authority ops
每次调用都会生成一条记录,其中每个受管控调用都会作为一项操作显示,并附带判定结果——因此,拒绝操作会显示为 denied,并标明资源名称,而不是悄无声息地失败。
为测试准备数据。 存储为空的应用只会显示空状态。部署后,使用 railcode app kv set <collection> <key> '<json>'(内容较长时使用 --file)以及 railcode app files upload <path> 添加几条符合实际的数据记录——数据结构应与 worker 实际写入的格式相符,你可以通过 railcode app kv get 确认。说明你添加了哪些数据,并在之后删除临时记录。如果应用中已有真实数据,请先征求同意。在 railcode dev 环境下,请通过应用自身的界面添加数据——CLI 不会触及本地开发存储。
如果用户要求进行浏览器测试,请在桌面和移动端宽度下端到端测试正在运行的应用。控制台错误、失败的 /api/* 调用以及布局异常都必须修复。
部署
railcode deploy
读取 railcode.json,运行由 CLI 管理的构建,上传静态文件树和 worker 作为一个整体,批准清单,并输出线上 URL。静态文件树和 worker 会一起启用和回滚——回滚会恢复部署时的确切可执行版本。
新应用默认组织范围内可访问;首次部署时使用 --private 可将应用设为私有。
如果此应用不是你创建的,你可能不是唯一的部署者。 应用支持编辑者(可共同部署的角色),因此请使用 railcode apps show <app> 检查你的权限(can edit / can manage),部署前运行 railcode pull,并将部署时出现的 409 视为同事在你上次同步后发布了新版本,而不是使用 --force 强行覆盖。
密钥属于线上应用状态,不属于部署内容。 设置一次后,之后的每次部署、回滚和冷回滚都会重新应用当前的密钥集:
railcode secrets set NAME # hidden prompt, or piped on stdin — never inline
railcode secrets ls # names + set-at + digest, never values
有关解析、访问模式和验证,请参阅部署。