mcpField
P
portal-micro-app

portal-micro-app

github.com/xgent-ai開発1172 回インストール今日更新
リポジトリ

portal-micro-app について

portal-micro-appは、サンドボックス化されたiframeでPortalに埋め込むWebマイクロアプリの開発スキルです。SDK連携、マニフェスト、リリース、ルーティング、権限、ブラウザー検証を案内します。

主な機能

  1. 01ViteとReactのセットアップ案内
  2. 02Portal SDKのハンドシェイクとルーティング規則
  3. 03マニフェスト、ナビゲーション、scope、ACLの設定
  4. 04Portalホスト経由のサービス呼び出し
  5. 05同意、トークン、権限の取り扱い規則
  6. 06ブラウザー検証チェックリスト

活用シーン

  1. 01Portalに埋め込むマイクロアプリの作成
  2. 02アプリのルーティングをホストのナビゲーションと連携
  3. 03SDK経由でアプリのバックエンドを呼び出す
  4. 04トークン処理と権限境界を確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/xgent-ai/skills --skill portal-micro-app

インストール後、Claude Code で/portal-micro-appを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
manifest-and-acl.md
open-api-and-extensions.md
sdk-reference.md

詳細

プロジェクトの README より

portal-micro-app · 嵌入式微应用(iframe + SDK)开发

微应用是被 Portal 以沙箱 iframe 嵌入的普通 Web 页面:不自己实现登录、不持有 App Secret,身份由宿主经 postMessage 握手注入。本文件是工作流与红线;具体契约按需读 references/(可整目录拷到任何 App repo 使用)。

这里的宿主身份是门户成员。若同一个产品另有面向顾客/学生的独立网站或原生客户端, 终端身份的注册、登录、会话与隐私治理读 portal-external-app skill 的 references/end-users.md; 不能把成员 SDK 握手、角色或成员 OAuth 直接当作终端用户认证。

/apps/<key>/ 是线上前端挂载 URL;本文的 references/ 相对于本 skill 目录。

按任务读参考

任务读
握手 / SDK API / 令牌与 consent / callServicereferences/sdk-reference.md
声明导航、scope、ACL、Widget、依赖references/manifest-and-acl.md
调 Open API / Widget 推送 / 通知 / 内容 / 计划任务 / 审计 / 错误码references/open-api-and-extensions.md

新建一个 micro App 前端的最小闭环

  1. 在 App 自己的仓库新建 Vite + React 前端,配置固定空闲端口并开启 strictPort,避免自动换端口后加载错应用。
  2. 安装下述公开分发的 SDK/UI 包,按本地 SDK 参考 接入握手与路由。
  3. 在自己的 app.manifest.json 声明 navItems / scopes / aclManifest / dashboardWidgets(顶层数组,见 Widget 契约);租户管理员不能代填开发者声明。
  4. Vite base 设为 /apps/<key>/,构建 dist/,用 xgent-app-release skill 的 publish --dist / publish --manifest 提交;涉及治理字段等待审批,按本次 proposal 的交付状态核对结果。
  5. 本地联调用 portal-dev-setup 的一盒环境。生产优先 sdk.callService 与同源托管;需要开发 CORS 白名单时交平台管理员处理。
  6. 在真实浏览器从宿主进入并走通主路径、路由往返与权限边界;不以构建通过代替 UI 验证。

@xgent/portal-sdk / @xgent/portal-ui / @xgent/shared 在你的 repo 里照常作为 npm 依赖安装, 来源是平台的私有包仓(公共 npm 上没有,直接 bun add 会 E404)。配法见 xgent-app-release skill 的「第 0 步」。仓里还没有 .xgent-registry.env ⇒ 停下,请开发者先去门户「开发者应用」生成 配置文件。不许 vendor:不把 SDK 的产物或源码拷进仓,也不用 file: / link: / tar 依赖顶替。

SDK 硬规则

  • 私有页面从 const sdk = createPortalClient(); const init = await sdk.ready(); 开始。SDK 0.4.0 支持 iframe 与同源 standalone 共用自举;声明 openModes 后从 /open/<key> 进入。顶层必须处理 ready() typed error 和 onContextChanged(),失效时卸载私有视图与应用缓存,并提供显式 openGate();不要自动跳登录。匿名页可不调用 ready()。生产从 /apps/<key>/ 推导 key,开发的 appKey/host/apiBase 来自构建配置,不能读 URL 配认证主机。详见 SDK 参考。
  • sdk.getToken() 自动缓存、到期前 30s 续签——不要自己存 TDT、不要写 localStorage。
  • 用户长期凭证由本 App 后端委托门户签发。前端只将新建/迁移响应保存在当前一次性展示组件;不要用 Query/Mutation 缓存保存含 secret/token 的响应。关闭后清除,复制失败提供选中文本和重试。门户集中管理入口是 /me?tab=keys;App 保留项目/能力/路由配置、旧密钥停用与远端撤销重试。
  • 全局平台管理员不在 TDT JWT / InitPayload 里;sdk.acl.bypass 和 sdk.userinfo().role 只表示当前租户。前端即使拿到 session-only /auth/me.isPlatformAdmin 也只能做展示,不能转发给后端授权;跨租户后端只认 TDT 自省返回的 isPlatformAdmin。
  • 调自己的独立后端一律 sdk.callService("<listingKey>", path, opts)(宿主代转发、零跨域、401/403 自动重铸重试一次)。iframe 直接跨域 fetch 独立后端是错误姿势。
  • 权限:sdk.acl.can(pid) / sdk.acl.scope(pid) 只做隐藏按钮/入口的 UX 门;真正拦截靠后端。前端判过 ≠ 安全。
  • 路由:内部路由变化调 sdk.routeSync(path) 同步到 ?r=;同时必须订阅 sdk.onRoute 处理宿主推回的路由(浏览器后退、同 App 多导航项切换)。只写单向 routeSync 会出现"同一 App 两个菜单点了不切换"的 bug。二级详情页把完整路径(如 /items/<id>)routeSync 出去,刷新/分享才能还原。
  • 版头面包屑:调 sdk.setBreadcrumbs(crumbs) 上报页面层级(宿主最左恒为「应用图标 + 应用名」= 应用首页,你报的是其后那几级;首页推 [],每次是整条 trail 的全量覆盖)。四条规矩:① 挂在视图驱动的 effect 上,不要散在各个 routeSync() 调用点——宿主发起的路由变化只走 onRoute,挂错的症状是"侧栏切页后面包屑空着";② onRoute("")(空路由)必须落到首页视图,版头最左那一级靠它工作;③ 标签要异步查询才知道时宁可少一级,不推 id 占位、不推"加载中…",数据回来再整条覆盖(FR-12 这种展示 key 可以推,内部 UUID 不可以);④ label 用自己当前的语言解析好(宿主不翻译),切语言在 onLocale 里重推。上限 6 级 / label 120 字符 / route 512 字符且必须 / 开头(不得以 // 开头),超限被静默截断;末级不带 route。
  • App 内不要自绘图标与名称:版头最左那一级恒为「应用图标 + 应用名」且可点回应用首页,App 再画一份就是同屏说两次,而且租户改过 App 名之后两处会说得不一样(apps.name 租户可改、i18n 词条改不了)。tagline / 副标题同理(应用中心已展示过)。可以留:角色 / 工作区 / 当前对象这类运行期事实,以及 main.tsx 里脱离门户打开时的 standalone 壳。浏览器验收时检查版头与 App 内容没有重复标题。
  • 帮助页入口 helpEntry:清单里声明一个可空字符串,版头工具条最右侧(全屏与刷新的左边)就多一枚帮助按钮;不声明就不出按钮。两种形态:"/help" = App 内路由(宿主置 ?r=,不离开门户,自动档)、"https://docs.example.com" = 外站文档(新标签 + noopener,走治理审核,因为门户版头等于替这个域名背书)。/… 与 crumb route 同一条规则;外站必须 https(http:// 连回环也拒)、无 fragment。它是安装期快照,改了要 bump 自己的 version;租户不可覆盖(想指向自家知识库去知识库 App 建条目)。门户不托管、不渲染、不翻译帮助正文。
  • 低频入口放版头、不占侧栏:帮助中心 / 接入指引这类低频页别长期占着侧栏一个位置 —— 用 helpEntry 挂到版头,并把这一级显式推进面包屑。
  • 主题/语言:订阅 sdk.onTheme / sdk.onLocale;未保存更改 sdk.setDirty(true) 让宿主拦 Shell 离开、刷新/关闭,以及跨宿主 pathname 的前进后退;取消保留原 iframe。同 App 只改 ?r= 不拦。历史守卫需要包含 Wise PDF 平台增量(2026-10)的门户运行件;SDK 0.5.7 的注释更新本身不启用旧宿主。App 内部应使用内存路由 + routeSync,不在 iframe 内 pushState。
  • iframe 高度二选一:满高型(编辑器、聊天、带底部输入栏)不调 sdk.resize,外壳 height: 100dvh(或 100%)+ 内部滚动——软键盘缩小可见区时宿主让 iframe 一起变矮、在 iframe 内触发 resize,App 在 resize 里把插入点 / 焦点输入框滚回视图,且 iOS 上输入控件计算字号 ≥16px(更小时 Safari 聚焦即放大,宿主不让位);内容高度型调 sdk.resize(scrollHeight),由宿主滚动区滚动,调过一次就不再复位为满高、也不再为键盘让位。没有键盘高度事件,也不需要。详见 SDK 参考「iframe 高度」。

Consent 硬规则

  • 宿主挂 iframe 前有 consent 门;mint 会把本次签发的 scope 记为用户的同意范围。用子集 scope mint 会收窄已有同意,之后更宽的 mint 触发 CONSENT_REQUIRED。日常 getToken 不要传裁剪过的 scopes。
  • 声明了 exchangeTargets 的 App,用户首次进入时 consent 门会共授跨应用交换同意——跨应用读数据为空时先想到这个(详见 portal-app-exchange skill)。

iframe 已知坑(都真实踩过)

  • window.prompt / window.confirm / window.alert 在跨源沙箱 iframe 里被静默忽略——一律用应用内 DOM 模态框。
  • Tailwind 的 /alpha 颜色修饰符(如 bg-primary/50)在主题的普通 var() 颜色上静默失效——用 opacity-NN 或预算好的色值。
  • Radix 菜单/Popover 触发器放在模态 Dialog 里"点了没反应":触发器组件必须 forwardRef,且弹层 z-index 要高于 dialog overlay。
  • 跨源 iframe 里剪贴板 API 受限;Chrome extension 也看不进跨源 iframe(验证时从宿主页面操作)。
  • .xg-md markdown 渲染若用 @xgent/file-preview 的 ./markdown 子路径,宿主需映射 hsl 通道 CSS 变量。

设计红线(平台一致性)

  • 配置界面别挂进你自己的导航:全平台可热改的配置做平台级管理页、每租户不同的做租户级管理页,都在清单 adminPages 里声明,由门户嵌进应用市场编辑页 / 租户应用配置;应用内的业务设置才做应用内设置页(aclManifest 授权)。判断表见 portal-external-app skill「配置放哪一层」。不自建审计页(写 POST /api/v1/audit)。
  • 不做「同步通讯录」:成员选择一律用 @xgent/portal-ui 的 UserPicker 按需 curated。
  • 列表页服务端分页,复用 Page<T> 契约与现有 helpers。
  • 复用 @xgent/portal-ui 已有组件再造新轮子。
  • 页面铺满客户区,上限 1600:iframe 就是客户区(14" 笔记本上约 1272px,App 自带侧栏再减 ~200px)。页面框架写 mx-auto w-full max-w-[1600px] + 页边距,不要把整页收成 max-w-3xl / max-w-[880px] 居中——笔记本上会左右各空 150–200px。窄只加在内容块上且左对齐:表单 / 设置列 ≤ 720px、Markdown 文档列 ≤ 880px、段落正文 ≤ 72ch;多个设置分区 lg: 并排两栏、卡片网格 xl: 加一列。允许居中窄列的只有:对话流(≤ 800px)、多步向导、授权 / 无权限卡片、空状态、纸面预览、弹窗 / 抽屉。

完成标准

类型检查/单测只证明代码对,不证明功能对。微应用改动必须在真浏览器(Chrome extension)里从宿主进入、走通主路径与关键边界后才算完成;环境起不来就显式说明"未在浏览器中验证"。

コメント