mcpField
O
omni-embed

omni-embed

github.com/exploreomni开发45168 次安装1 天前更新
代码仓库

关于 omni-embed

omni-embed 是用于将 Omni 仪表板嵌入外部应用的技能,面向开发者提供签名 URL、主题配置和 iframe 事件交互的指导。

主要功能

  1. 01生成签名仪表板 URL
  2. 02配置嵌入内容主题
  3. 03处理 iframe 消息事件
  4. 04支持用户属性与行级安全
  5. 05传递筛选与钻取数据

适用场景

  1. 01在外部应用展示 Omni 仪表板
  2. 02为嵌入仪表板匹配品牌主题
  3. 03响应仪表板筛选和加载事件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/exploreomni/omni-agent-skills --skill omni-embed

安装后在 Claude Code 中输入/omni-embed确认已加载。

附带文件evals/evals.json

详细介绍

Omni Embed

使用签名 iframe URL 在外部应用中嵌入 Omni 仪表板。@omni-co/embed SDK 可处理 URL 签名和主题自定义。Omni 的 postMessage 事件支持父应用与嵌入的 iframe 之间进行双向通信。

Tip:使用 omni-content-explorer 查找要嵌入的仪表板,并使用 omni-admin 管理嵌入用户权限和用户属性,以实现行级安全。

前提条件

npm install @omni-co/embed
# Verify the Omni CLI is installed — if not, ask the user to install it
# See: https://github.com/exploreomni/cli#readme
command -v omni >/dev/null || echo "ERROR: Omni CLI is not installed."
# Show available profiles and select the appropriate one
omni config show
# If multiple profiles exist, ask the user which to use, then switch:
omni config use <profile-name>

# Confirm the active profile is authenticated and inspect your permissions:
omni whoami whoami

export OMNI_EMBED_SECRET="your-embed-secret"   # Admin → Embed (for URL signing)

Auth:配置文件通过 API 密钥或 OAuth 进行身份验证(与下方的嵌入密钥不同)。如果 whoami(或任何调用)返回 401,请交由用户处理——请用户运行 ! omni config login <profile>(OAuth 2.1 浏览器流程;会在浏览器中阻塞约 2 分钟)。不要在无头/CI 会话中自行运行 config login(没有浏览器会导致超时);在本地交互式设备上则可以运行。有关配置文件设置(omni config init --auth oauth)以及如何使用 --schema 探索命令和请求正文格式,请参阅 omni-api-conventions 规则。

嵌入密钥可在 Omni 实例的 Admin → Embed 中找到。不要假设 OMNI_BASE_URL 就是嵌入主机:CLI/API URL 通常使用 .omniapp.co、自定义 域名或内部 playground 域名。签署 iframe URL 时,将 OMNI_EMBED_HOST 设置为不带协议和路径的嵌入 主机名(例如 yourorg.embed-omniapp.co)。

安全签名默认设置

  • 使用 SDK 签名器 — 使用 @omni-co/embed 中的 embedSsoDashboard() 生成签名 URL。除非用户明确要求底层实现,否则不要手动编写 HMAC 签名。
  • 绝不要将 OMNI_API_TOKEN 用作嵌入密钥 — API token 用于验证 REST/CLI 调用,不能用作有效的嵌入签名密钥。请使用 Admin → Embed 中的 OMNI_EMBED_SECRET。
  • 如果无法获取嵌入密钥 — 不要伪造签名 URL。请返回调用 embedSsoDashboard() 且使用 secret: process.env.OMNI_EMBED_SECRET 的服务端代码,并告知用户设置该环境变量。
  • 使用嵌入主机生成 iframe URL — host 必须是不带 https://、路径或端口的 .embed-omniapp.co 主机名。
  • 除非 OMNI_BASE_URL 已经是嵌入主机,否则不要从中推导嵌入主机 — CLI/API 基础 URL 通常使用 .omniapp.co 或自定义 API 域名。如果只知道 API 基础 URL,请在服务端代码中保留 host: process.env.OMNI_EMBED_HOST(或类似 yourorg.embed-omniapp.co 的占位值),并指出必须单独设置此值。
  • 如果无法使用 Node.js — 仍然提供使用 @omni-co/embed 的 TypeScript/Node 服务端代码;不要仅为了让本地演示可运行就改用 Python 或在浏览器端手动进行 HMAC 签名。

探索命令

omni scim --help        # Embed user lookup
omni documents --help   # Document listing
omni folders --help     # Folder listing

Tip:使用 -o json 强制输出结构化内容,以便程序解析;使用 -o human 输出易读表格。默认值为 auto(在 TTY 中输出人类可读格式,通过管道传输时输出 JSON)。

签署嵌入 URL

使用 @omni-co/embed SDK 中的 embedSsoDashboard() 在服务端生成签名 URL,然后在客户端将其加载到 iframe 中。

import { embedSsoDashboard, EmbedSessionMode } from "@omni-co/embed";

const embedUrl = await embedSsoDashboard({
  contentId: "dashboard-uuid",
  secret: process.env.OMNI_EMBED_SECRET,
  host: process.env.OMNI_EMBED_HOST ?? "yourorg.embed-omniapp.co",
  externalId: "[email protected]",
  name: "Jane Doe",
  userAttributes: { brand: ["Acme"] },     // For row-level security
  mode: EmbedSessionMode.SingleContent,
  prefersDark: "false",
});
参数
参数必填描述
contentId是仪表板 UUID(来自 URL 或 Admin → Dashboards)
secret是来自 Admin → Embed 的嵌入密钥
host是仅限嵌入主机名 — 不含协议或端口
externalId是唯一用户标识符(通常为电子邮件)
name是用户的显示名称
userAttributes否用于行级安全的 Record<string, string[]>
mode否SingleContent(默认)或 Application(启用创建)
prefersDark否"true" 或 "false" — 控制浅色/深色模式
customTheme否主题对象(见下文“自定义主题”)
entity否工作区的实体名称(见下文“实体工作区”)

注意:host 参数必须是纯主机名(例如,yourorg.embed-omniapp.co)。包含协议(https://)或端口(:3000)会导致 Omni 返回 400。

如果未设置 OMNI_EMBED_SECRET,仍需生成这种 SDK 风格的服务端代码,并使用 process.env.OMNI_EMBED_SECRET;不要替换为 OMNI_API_TOKEN 或 其他 API 凭据。

自定义主题

将 customTheme 对象传递给 embedSsoDashboard(),以设置嵌入式仪表板内容的样式(图块背景、文本颜色、控件、按钮)。这控制的是 iframe 内部的内容 — 父应用的样式需另行设置。

const embedUrl = await embedSsoDashboard({
  // ...signing params
  prefersDark: "false",
  customTheme: {
    "dashboard-background": "#FEF2F2",
    "dashboard-tile-background": "#FFF5F5",
    "dashboard-key-color": "#E60000",
    "dashboard-key-text-color": "#ffffff",
    // ...
  },
});
属性参考

页面:

属性描述
dashboard-background仪表板页面背景
dashboard-page-padding仪表板页面内边距

图块:

属性描述
dashboard-tile-margin图块周围的间距
dashboard-tile-background图块背景颜色
dashboard-tile-shadow图块阴影
dashboard-tile-text-body-color图块中的主要文本颜色
dashboard-tile-text-secondary-color图块中的次要文本颜色
dashboard-tile-border-color图块边框颜色
dashboard-tile-border-radius图块边框圆角
dashboard-tile-border-style图块边框样式
dashboard-tile-border-width图块边框宽度
dashboard-tile-title-font-size标题字体大小
dashboard-tile-title-font-weight标题字体粗细
dashboard-tile-title-text-color标题文本颜色
dashboard-tile-title-font-family自定义标题字体(woff2 URL)
dashboard-tile-text-body-font-family自定义正文字体
dashboard-tile-text-code-font-family自定义代码字体

控件(筛选器下拉菜单):

属性描述
dashboard-control-background筛选器控件背景
dashboard-control-radius筛选器控件边框圆角
dashboard-control-border-color筛选器控件边框颜色
dashboard-control-text-color筛选器控件文本颜色
dashboard-control-placeholder-color占位文本颜色
dashboard-control-label-color控件上方标签文本颜色
dashboard-control-outline-color焦点轮廓颜色

控件弹出层(下拉菜单):

属性描述
dashboard-control-popover-background弹出层背景
dashboard-control-popover-text-color弹出层文本颜色
dashboard-control-popover-secondary-text-color弹出层中的次要文本
dashboard-control-popover-link-color弹出层中的链接颜色
dashboard-control-popover-divider-color分隔线颜色
dashboard-control-popover-radius弹出层边框圆角
dashboard-control-popover-border-color弹出层边框颜色
dashboard-filter-input-background筛选器输入框背景
dashboard-filter-input-radius筛选器输入框边框圆角
dashboard-filter-input-border-color筛选器输入框边框颜色
dashboard-filter-input-text-color筛选器输入框文本颜色
dashboard-filter-input-placeholder-color占位文本
dashboard-filter-input-icon-color筛选器输入框中的图标颜色
dashboard-filter-input-outline-color筛选器输入框的焦点轮廓颜色
dashboard-filter-input-accent-color复选框、单选按钮和切换开关颜色
dashboard-filter-input-accent-invert-color输入框中的对勾/圆点颜色
dashboard-filter-input-token-color多选标记背景颜色
dashboard-filter-input-token-text-color多选标记文本颜色

按钮:

属性描述
dashboard-key-color主要操作颜色(更新按钮)
dashboard-key-text-color主要按钮上的文本颜色
dashboard-button-radius按钮边框圆角
dashboard-button-transparent-text-color透明按钮文本颜色
dashboard-button-transparent-interactive-color透明按钮悬停颜色
dashboard-menu-item-interactive-color菜单项悬停背景
支持的 CSS 值
  • 十六进制颜色:"#FEF2F2"、"#E60000"
  • 使用 rgba 的阴影:"0 2px 8px rgba(230, 0, 0, 0.1)"(仅适用于阴影属性)
  • 通过 URL 指定自定义字体:"url(https://fonts.gstatic.com/...) format('woff2')"
  • 用于清除默认值的空字符串:""
  • linear-gradient() 和 rgba() 用于背景/颜色属性时,在 Omni 的 UI 主题编辑器中可用,但通过 SDK 传入时可能失败——为确保可靠,请使用纯色十六进制颜色
主题设置提示

为有效打造品牌形象,应统一为背景添加色调,而不只是给按钮上色:

  • dashboard-background → 淡品牌色调(类似 Tailwind 的 color-50)
  • dashboard-tile-background → 比页面背景略浅
  • dashboard-tile-title-text-color → 品牌主色(标题使用品牌色)
  • dashboard-control-label-color → 品牌主色(标签使用品牌色)
  • dashboard-tile-border-color → 中浅品牌色调(类似 color-200)
  • dashboard-key-color → 品牌主色
  • dashboard-filter-input-accent-color → 品牌主色(复选框、切换开关)

嵌入事件

Omni 通过 postMessage 与父应用通信。所有 Omni 事件的 source: "omni"。

监听事件
window.addEventListener("message", (event) => {
  if (event.data?.source !== "omni") return;

  switch (event.data.name) {
    case "dashboard:loaded":
      // Dashboard ready
      break;
    case "error":
      // Handle error
      break;
    case "dashboard:tile-drill":
      // Handle drill action
      break;
  }
});
事件参考

dashboard:loaded — 嵌入式仪表板加载完成时触发。

{ "source": "omni", "name": "dashboard:loaded" }

dashboard:filters — 嵌入式仪表板中的筛选器状态发生变化时触发。

{
  "source": "omni",
  "name": "dashboard:filters",
  "payload": { /* filter state */ }
}

error — 嵌入页面发生可检测到的错误时触发。

{
  "source": "omni",
  "name": "error",
  "payload": {
    "href": "https://...",
    "message": "Error description"
  }
}

dashboard:tile-drill — 用户对任意仪表板图块(图表、表格、地图)进行下钻时触发。无需在 Omni 端进行配置。

{
  "source": "omni",
  "name": "dashboard:tile-drill",
  "payload": {
    "userId": "string",
    "dashboard": {
      "filters": {
        "filterName": {
          "filter": {},
          "asJsonUrlSearchParam": "string"
        }
      },
      "href": "string",
      "urlId": "string",
      "path": "string",
      "title": "string"
    },
    "tile": {
      "id": "string",
      "title": "string",
      "appliedFilters": {
        "filterName": {
          "filter": {},
          "asJsonUrlSearchParam": "string"
        }
      }
    },
    "drill": {
      "field": "string",
      "fieldLabel": "string",
      "drillQueryLabel": "string",
      "rowToDrill": { "field_name": "value" }
    }
  }
}

使用 drill.rowToDrill 获取下钻行中的数据。使用 tile.appliedFilters 或 dashboard.filters 中的 asJsonUrlSearchParam 对其他仪表板进行签名和嵌入,并应用这些筛选器。

page:changed — iframe 内的 URL 发生变化时触发(包括保存新仪表板后)。

{
  "source": "omni",
  "name": "page:changed",
  "payload": {
    "pathname": "string",
    "type": "string"
  }
}

自定义可视化事件 — 用户点击已配置的表格行或 Markdown 链接时触发。需要在 Omni 中进行设置:将表格列的 Display 设为 Link → Embed event,并输入事件名称。对于 Markdown,请使用 <omni-message> 标签。

{
  "source": "omni",
  "name": "<your-event-name>",
  "payload": {
    "data": "comma-separated values"
  }
}

表格设置:字段下拉菜单 → Display 标签页 → Display as:Link → URL:Embed event → 输入事件名称。

Markdown 设置:

<omni-message event-name="product-click" event-data="{{products.name.raw}},{{products.retail_price.raw}}">
  Click here
</omni-message>
向 iframe 发送事件

dashboard:filter-change-by-url-parameter — 将筛选器从父应用推送到嵌入式仪表板。

iframe.contentWindow.postMessage({
  source: "omni",
  name: "dashboard:filter-change-by-url-parameter",
  payload: {
    filterUrlParameter: 'f--<filter_id>={"values":["value1","value2"]}'
  }
}, iframeOrigin);

在 Omni 中打开仪表板、更改筛选器值,然后从 URL 复制 f-- 参数,即可获取 filterUrlParameter 字符串。

实体工作区

实体工作区允许嵌入用户在限定文件夹中创建并保存自己的仪表板。

import {
  embedSsoDashboard,
  EmbedSessionMode,
  EmbedEntityFolderContentRoles,
  EmbedUiSettings,
  EmbedConnectionRoles,
} from "@omni-co/embed";

const embedUrl = await embedSsoDashboard({
  // ...standard signing params
  entity: "acme",
  entityFolderContentRole: EmbedEntityFolderContentRoles.EDITOR,
  mode: EmbedSessionMode.Application,
  uiSettings: {
    [EmbedUiSettings.SHOW_NAVIGATION]: false,
  },
  connectionRoles: {
    "connection-uuid": EmbedConnectionRoles.RESTRICTED_QUERIER,
  },
});
参数说明
entity实体名称 — 用于限定用户的文件夹范围(例如,根据电子邮件域名推导)
entityFolderContentRoleEDITOR 允许用户在其实体文件夹中创建/编辑仪表板
mode必须设为 Application 才能启用创建功能
uiSettings控制 Omni 内置界面(例如,如果你提供自己的侧边栏,可隐藏 Omni 的侧边栏)
connectionRoles授予查询权限:用于数据探索的 RESTRICTED_QUERIER

嵌入用户和权限

构建具备权限感知能力的体验时(例如,仅显示用户有权访问的仪表板的侧边栏),应先查询 Omni 用户 ID,再列出限定到该用户的文档。API/CLI 调用使用 Omni API 主机/配置,而不是 .embed-omniapp.co 嵌入主机。

查询嵌入用户
omni scim embed-users-list --filter 'embedExternalId eq "[email protected]"'

返回给定 externalId 对应的 Omni 用户 ID。如果未找到用户,则表示该用户尚未访问任何嵌入式仪表板。

如果嵌入用户筛选器未返回任何行,但显然存在电子邮件/用户名相同的常规 SCIM 用户,你可以使用该用户的 Omni ID 作为备用,并说明使用了哪种查询方式。不要使用未筛选的文档列表作为侧边栏结果。

按用户权限列出文档
omni documents list --user-id <omniUserId>

使用 embed-users-list 返回的 Omni 用户 ID,不要直接使用电子邮件或 externalId。渲染侧边栏条目之前,先筛选出 hasDashboard: true 的响应。documents list 使用 API 主机/配置文件;只有在使用 embedSsoDashboard() 对 iframe URL 签名时才使用 embed 主机。即使是演示应用,也应将 URL 签名放在服务器端 TypeScript/Node 函数中,并使用 SDK 签名器,而不是用 Python 或浏览器代码手动复现 HMAC 协议。

如果你编写的是服务器代码,而不是直接运行 CLI,对应的 documents API 格式如下:

GET /api/v1/documents?userId=<omniUserId>
Authorization: Bearer <OMNI_API_TOKEN>

API 调用中使用驼峰命名的 userId。CLI 标志为小写的 --user-id。

响应使用 records 数组(而不是 documents):

{
  "pageInfo": {
    "hasNextPage": false,
    "nextCursor": null,
    "pageSize": 20,
    "totalRecords": 5
  },
  "records": [
    {
      "identifier": "fb007aa3",
      "name": "Sales Dashboard",
      "hasDashboard": true,
      "folder": {
        "id": "...",
        "name": "Sales",
        "path": "sales/regional"
      }
    }
  ]
}

将 identifier 用作 embed 签名的 contentId。筛选出 hasDashboard: true,即可仅获取可嵌入的仪表板。

列出文件夹并显示易读名称

实体文件夹使用 omni-system-sso-embed-entity-folder-poc 这类技术路径。将路径映射为显示名称:

omni folders list

根据响应构建 path → name 映射,以显示易读的文件夹名称。

域名映射

embed 域(.embed-omniapp.co)和 API 域(.omniapp.co)不同:

Embed: yourorg.embed-omniapp.co  →  used for iframe URLs
API:   yourorg.omniapp.co        →  used for REST API calls

当应用存储 embed 域时,将 .embed-omniapp.co 替换为 .omniapp.co,以便用于 API 调用。

文档参考

相关技能

  • omni-content-explorer — 查找要嵌入的仪表板
  • omni-content-builder — 在嵌入之前创建仪表板
  • omni-admin — 管理嵌入用户权限、用于 RLS 的用户属性和连接
  • omni-model-explorer — 了解嵌入事件数据的可用字段

评论

支持 Markdown