建立消防站
消防车必须设置为 Native Mode 没有 Datastore Mode
Firestore read/writes 需要通过在安全规则上添加以下内容来被锁定(Firebase's Firestore UI) :
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /Projects/{project} {
allow write:
if isSignedIn() && userIsAdmin();
allow read:
if isSignedIn() && userCanRead();
match /{collection}/{document=**} {
allow write:
if isSignedIn() && userCanPublish();
allow read:
if isSignedIn() && userCanRead();
}
match /Collections/{collectionId}/Drafts/{document=**} {
allow write:
if isSignedIn() && userCanEdit();
}
function isSignedIn() {
return request.auth != null;
}
function getRoles() {
return get(/databases/$(database)/documents/Projects/$(project)).data.roles;
}
function userCanRead() {
let roles = getRoles();
let email = request.auth.token.email;
let domain = '*@' + email.split('@')[1];
return (roles[email] in ['ADMIN', 'EDITOR', 'CONTRIBUTOR', 'VIEWER']) || (roles[domain] in ['ADMIN', 'EDITOR', 'CONTRIBUTOR', 'VIEWER']);
}
function userCanPublish() {
let roles = getRoles();
let email = request.auth.token.email;
let domain = '*@' + email.split('@')[1];
return (roles[email] in ['ADMIN', 'EDITOR']) || (roles[domain] in ['ADMIN', 'EDITOR']);
}
function userCanEdit() {
let roles = getRoles();
let email = request.auth.token.email;
let domain = '*@' + email.split('@')[1];
return (roles[email] in ['ADMIN', 'EDITOR', 'CONTRIBUTOR']) || (roles[domain] in ['ADMIN', 'EDITOR', 'CONTRIBUTOR']);
}
function userIsAdmin() {
let roles = getRoles();
let email = request.auth.token.email;
let domain = '*@' + email.split('@')[1];
return (roles[email] == 'ADMIN') || (roles[domain] == 'ADMIN');
}
}
}
}
在 Firstore 中,在 Projects/<yourprojectid> 值为 {roles: {"[email protected]": "ADMIN"}}。 。 。 .
使用 Firestore 工作室 :
- 下
Give the collection an ID设置Collection ID改为Projects - 下
Add its first document设定Document ID转到您的项目 ID - 第一套记录
Field name改为roles带有Field type联 合 国map - 在地图上设置新
Field name给您的电子邮件,Field type改为string财务报告和审定财务报表Field value改为ADMIN并拯救我们.
说明 *@domain 角色补助金
以上规则支持通配符 *@example.com 输入 roles 那个
匹配每个已签名的用户,其已核实的电子邮件结束于 @example.com。 。 。 .
这方便于允许进入整个工作空间,但请注意:
- 任何消防基地Auth承认
@example.com电子邮件——包括: 新创建的账户和任何你不再控制的账户—— 立即增加角色,没有选择进入或审计线索. - 永远不要对自由电子邮件提供者使用通配符(例如.
*@gmail.com: 允许任何有谷歌账户的人访问. - 优先提供明确的电子邮件赠款
ADMIN财务报告和审定财务报表EDITOR; 保留 通配符CONTRIBUTOR或者说VIEWER如果你用它的话.
AI代理人的CLI(root-cms client.*(中文(简体) )
这个 root-cms CLI 暴露出允许 AI 编码代理( 或任何)
脚本)驱动 RootCMSClient 方案方面。 把他们从根中找出来
Root.js项目(包含 root.config.ts) (中文(简体) ).
发现可用的 API :
# Human-readable list of methods + descriptions
root-cms client.methods
# Machine-readable JSON, including referenced type/interface definitions
root-cms client.methods --json --types
调用 JSON 系列位置参数的任何方法 :
# getDoc(collectionId, slug, options)
root-cms client.call getDoc '["Pages", "home", {"mode": "draft"}]'
# A no-arg method (omit the JSON array)
root-cms client.call publishScheduledDocs
# Read large args from stdin by passing `-`
echo '["Pages/home", {"title": "Hello"}]' | root-cms client.call saveDraftData -
client.call 打印出一个单行 JSON 信封以退出非零
失败时 :
{"ok": true, "result": <value>}
{"ok": false, "error": "<message>"}
消防车 Timestamp, (中文(简体) ), GeoPoint,以及 DocumentReference 数值被编码
作为 {"_seconds","_nanoseconds"}, (中文(简体) ), {"_latitude","_longitude"},以及
{"_referencePath"} 分别载于论据和结果.
改革提议
人工智能特工可以 提议 内容更改而不是直接写入 火炉. 一个提议是单一的Markdown文件,描述编辑——doc id,, 字段路径、旧值、新值 —— 将致力于您的重播和 在拉动请求中审查。 在应用之前,任何内容都不要触及数据库.
每个更改和操作都是标题,每个编辑的值都是 diff 代码块,
因此,拉动请求显示了确切的行变化:
# Refresh the homepage hero
- **id:** `2026-08-20-hero-refresh`
- **version:** 1
## Edit `Pages/home`
### Set `hero.title`
```diff text
-Welcome to Acme
+Build faster with Acme
```
一个值块是 text (字符串,逐字记录), yaml,或 jsonA级 diff
块以每行前缀保存整个值 " " (不变), -
(以前),或 + (后经. 提案也可用YAML(.yaml
文件),原始格式.
支持的更改类型为 doc.edit, (中文(简体) ), doc.create, (中文(简体) ), doc.duplicate, (中文(简体) ),
release.create, (中文(简体) ), release.update,以及 translations (标题) ## Edit, (中文(简体) ),
## Create, (中文(简体) ), ## Duplicate … to …, (中文(简体) ), ## Create release, (中文(简体) ), ## Update release, (中文(简体) ),
财务报告和审定财务报表 ## Translations) (中文(简体) ). 编辑操作
(单位:千美元)set, (中文(简体) ), insert_item, (中文(简体) ), remove_item) 使用零基的已点字段路径
数组索引,并依次应用.
3个命令驱动工作流程 :
# Check the proposal format. No db access, so it works as a CI gate on a PR.
root-cms proposal.check cms-proposals/2026-08-20-hero-refresh.md
# Resolve against the live db, validate against collection schemas, and
# report what it would write.
root-cms proposal.diff cms-proposals/2026-08-20-hero-refresh.md
# Apply it.
root-cms proposal.apply cms-proposals/2026-08-20-hero-refresh.md
应用写入 仅草稿 它从不出版、时间表或删除 --
除非提案中的每一项改变都得到彻底的解决,否则什么都不是书面的.
每个命令都打印出相同的单行 JSON 信封 client.call。 。 。 .
提议 before 值是人类读取 diff 的文件;
除非您通过,否则不会与数据库核对 --verify-before。 。 。 .
预览提案
RootCMSClient 可以覆盖一个未应用的建议 对于它读到的一切,所以
您可以在接受之前先在运行中的网站看到所推荐的内容 :
import {RootCMSClient, parseProposalFile} from '@blinkk/root-cms/core';
const filepath = 'cms-proposals/x.md';
const res = parseProposalFile(fs.readFileSync(filepath, 'utf8'), filepath);
if (res.ok) {
const client = new RootCMSClient(rootConfig, {proposal: res.proposal});
// getDoc(), listDocs(), and batch reads now reflect the proposal.
}
复数仅适用于读取. 写作时从来不会折叠提议
getRawDoc() 编号 : listDocs({raw: true}) 故意绕过提议
内容永远不能泄漏到一个 setRawDoc() 圆通相去.
可下载代理技能
训练AI编码员如何使用这些指令船的准备使用技能
与软件包一起: root-cms-cli (单位:千美元) client.* 命令), root-cms-propose
(授权修改建议), root-cms-apply (审查和适用
他们, root-cms-theme (生成CMS UI的主题),并
root-cms-email-setup (部署电子邮件服务并设置电子邮件)
通知)。 装入:
root-cms skill.install
根保持对任何特定 AI 提供者的不可知性: 命令自动检测
现有代理技能目录(例如:. .claude/skills, (中文(简体) ), .agent/skills,或
任意 .*/skills 目录已经在您的工程中)并安装到它们中。 若为
没有找到,它安装到 .agent/skills。传递一个明确的目录到
超常检测(例如). root-cms skill.install ./my-agent/skills),或
--force 以覆盖现有副本。 过 --skill <name> 仅安装
其中一个.
嵌入CMS(@blinkk/root-cms/browser-client(中文(简体) )
@blinkk/root-cms/browser-client 是一个无依赖性,框架不可知
用于将Root CMS嵌入到另一个站点的浏览器库. 包起来
输入的 API 背后的 Iframe/ postMessage 线条, 所以线条协议
可以进化而不破坏你的融合.
用于弹出/帧嵌入( doc 编辑器和Root AI),嵌入页的 源代码必须允许在 CMS 插件配置中列出 :
// root.config.ts
cmsPlugin({
// ...
allowedIframeOrigins: ['https://app.example.com'],
});
无头 doc 编辑器( 流行或 iframe)
打开 /cms/embed/content/<collection>/<slug>,最小版本的文档
带有明确(非自动)保存的编辑器。 单位 popup 模式,用户的呼叫
手势(例如点击处理器),以避免弹出阻塞器。 关闭编辑器
丢弃未保存的更改 .
import {RootCMSBrowserClient} from '@blinkk/root-cms/browser-client';
const root = new RootCMSBrowserClient({cmsOrigin: 'https://cms.example.com'});
const editor = root.openEditor('Pages/about', {
mode: 'popup', // or 'iframe' (pass `container`)
deeplink: 'hero.title', // optional: field to scroll to on load
});
editor.on('ready', () => console.log('editor loaded'));
editor.on('saved', () => location.reload());
editor.on('published', () => editor.closeAndReload());
editor.on('close', () => console.log('editor closed'));
editor.focusField('hero.image'); // scroll the editor to a field
editor.reload();
editor.close(); // or editor.closeAndReload()
无头根 AI( 弹出或 Iframe)
打开 /cms/embed/ai,紧凑的 Root AI聊天,可选用 doc作为
语境 :
const ai = root.openRootAI({docId: 'Pages/about', mode: 'popup'});
ai.on('ready', () => console.log('chat loaded'));
ai.close();
从 CMS 预览面板( “ 单击以编辑 ” ) 中突出字段
当您的网站在 CMS 预览面板内渲染时, 请使用
PreviewConnection 从页面中聚焦编辑器字段并突出显示页面
编辑器要求时会结出节点. 预览面板与
CMS, 所以不需要配置, 当页面不存在时, 连接是惰性的
内嵌 :
import {RootCMSBrowserClient} from '@blinkk/root-cms/browser-client';
const preview = RootCMSBrowserClient.connectPreview();
if (preview.isEmbedded) {
// "Click to edit": focus the field in the doc editor.
el.addEventListener('click', () => preview.focusField('hero.title'));
// Highlight page nodes when the editor hovers/focuses a field.
preview.on('highlight', ({deepKey, scroll}) => {
clearHighlights();
if (deepKey) highlightNode(deepKey, {scroll});
});
}