@blinkk/root-cms

中文文档
4.3.5发布于 2026/11/06MITTypeScriptES Module构建来源已验证近 30 天下载 1.6万
中文使用文档
文档目录7 节
  1. 说明 *@domain 角色补助金
  2. 改革提议
  3. 预览提案
  4. 可下载代理技能
  5. 无头 doc 编辑器( 流行或 iframe)
  6. 无头根 AI( 弹出或 Iframe)
  7. 从 CMS 预览面板( “ 单击以编辑 ” ) 中突出字段

建立消防站

消防车必须设置为 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});
  });
}

版本与发布记录

@blinkk/root-cms 共发布 340 个版本,首次发布于 2022/10/29;过去 12 个月发布了 91 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
4.3.5latest2026/11/062 天前
4.3.32026/11/044 天前
4.3.22026/11/035 天前
4.3.12026/11/035 天前
4.2.22026/10/2910 天前
4.2.12026/10/2810 天前

其他发布通道:rc → 2.0.0-rc.1,debug → 1.3.14-debug.4,beta → 3.0.1-beta.9,alpha → 4.0.0-alpha.8。

关于 @blinkk/root-cms 的常见问题

如何安装 @blinkk/root-cms?

在项目目录运行 npm install @blinkk/root-cms,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@blinkk/root(4.3.5)、firebase-admin(>=11)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。它还提供命令行工具 root-cms,可以用 npx root-cms 直接运行。

@blinkk/root-cms 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@blinkk/root-cms 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@blinkk/root-cms 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@blinkk/root-cms 可以商用吗?

@blinkk/root-cms 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@blinkk/root-cms 的下载量有多少?

根据 npm 官方下载统计,@blinkk/root-cms 在 2026/10/09 – 2026/11/07 这 30 天内被下载 16,146 次,日均约 538 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@blinkk/root-cms 还在维护吗?

最新版本 4.3.5 发布于 2026/11/06(2 天前);至今共发布 340 个版本,其中过去 12 个月发布了 91 个;首次发布于 2022/10/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。