关于 omni-embed
omni-embed 是用于将 Omni 仪表板嵌入外部应用的技能,面向开发者提供签名 URL、主题配置和 iframe 事件交互的指导。
主要功能
- 01生成签名仪表板 URL
- 02配置嵌入内容主题
- 03处理 iframe 消息事件
- 04支持用户属性与行级安全
- 05传递筛选与钻取数据
适用场景
- 01在外部应用展示 Omni 仪表板
- 02为嵌入仪表板匹配品牌主题
- 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 | 实体名称 — 用于限定用户的文件夹范围(例如,根据电子邮件域名推导) |
entityFolderContentRole | EDITOR 允许用户在其实体文件夹中创建/编辑仪表板 |
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 — 了解嵌入事件数据的可用字段