Files
Genarrative/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md
T
kdletters 025f627297
Project CI / Frontend tests (push) Successful in 3m11s
Project CI / Backend tests (push) Successful in 6m52s
Project CI / Repository checks (push) Failing after 53s
Project CI / Native shell tests (push) Failing after 17m50s
新增旧玩法表阶段一清空迁移能力 (#251)
阶段一新增受 migration operator 保护的旧玩法表清空 procedure,暂不删除表定义。\n\n固定 63 张旧表,支持 dry-run 与事务 apply;生成 bindings;更新契约和决策记录;保留阶段二删表 TODO。\n\n已完成 cargo check、migration 测试、bindings/schema/encoding/diff 检查;本地 dry-run 返回 63 张表且全为 0 行,未执行 apply。

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/251
2026-09-03 12:57:01 +08:00

138 KiB
Raw Blame History

Expo React Native 与 Tauri 宿主壳方案

更新时间:2026-06-19

结论

移动端壳采用 Expo + React Native,桌面端壳采用 Tauri。两者都只作为 Genarrative H5 主站的宿主壳,不重写现有 React 主站,不把固定玩法 runtime 迁到 React Native 或 Rust UI,也不让 AI 生成 H5 游戏直接拿完整宿主能力。

固定内置玩法继续跑在现有 H5 runtime 内;移动端和桌面端通过 HostBridge 提供已接入真实宿主实现的分享、受控 H5 跳转、外链打开、文件、剪贴板、本地通知、生命周期、网络和其它系统能力。登录和支付在 Expo / Tauri 接入真实 SDK、渠道流程和后端契约前不进入两端 capability,继续由 H5 fallback 或微信小程序既有真实流程承接。AI 生成 H5 游戏继续放进独立 sandbox,只能通过受限 GameBridge 请求允许的能力。

目标

  1. 主站 H5 仍是唯一的产品体验主线,网页、小程序、移动 App 和桌面 App 共享同一套业务页面、玩法 runtime 和后端契约。
  2. 移动端用 Expo / React Native 承接 App 外壳、WebView、深链、推送、分享、支付 SDK、系统权限和少量原生页面。
  3. 桌面端用 Tauri 承接安装包、窗口、文件/剪贴板/外部浏览器等桌面能力,并用 Tauri capabilities 收窄前端可调用的命令。
  4. H5 业务层只面向 HostBridge 能力,不直接判断 Expo、React Native、Tauri、iOS、Android 或桌面平台。
  5. 壳层能力按能力白名单逐项开放,不提供“任意 native command”或“任意系统 API”透传。

非目标

  • 不把现有 React H5 主站整体迁到 React Native。
  • 不用 Tauri 重写桌面 UI,也不引入第二套桌面业务前端。
  • 不让固定玩法通过远程代码包下载流程启动。
  • 不让 AI 生成 H5 游戏直接访问登录、支付、token、完整用户资料、系统文件或宿主私有 API。
  • 不在第一阶段解决 App Store / 应用商店全部上架材料,只先固定工程边界和验证路线。

总体架构

现有 React H5 主站
  -> HostBridge
    -> browser adapter
    -> wechat mini program adapter
    -> native app adapter
      -> Expo React Native shell
      -> Tauri shell

AI 生成 H5 游戏 iframe
  -> GameBridge
    -> H5 parent runtime
      -> HostBridge 能力子集或后端 API

核心原则:HostBridge 是 H5 与宿主壳之间唯一的通用协议;GameBridge 是 AI 游戏 sandbox 与父页面之间的受限协议。两个协议不能合并。

工程布局建议

apps/
  mobile-shell/        # Expo + React Native App 壳
  desktop-shell/       # Tauri 桌面 App 壳
miniprogram/
  host-bridge/         # 微信小程序协议归一、支付 / 分享结果编解码
  shell/               # 微信小程序 Page 生命周期、wx.* 容器调用和页面工厂
packages/
  shared/
    src/contracts/
      hostBridge.ts    # HostBridge 消息契约,供 H5 / RN / Tauri 对齐
src/
  services/host-bridge/
    hostBridge.ts
    nativeAppHostBridge.ts

已落地:packages/shared/src/contracts/hostBridge.ts 保存消息 envelope、method、payload 和错误码,H5、Expo 壳与 Tauri 壳共享同一份协议类型。

三端宿主桥接层按职责对齐命名:微信小程序页面路由仍保留在 miniprogram/pages/*miniprogram/host-bridge/protocol.js 只沉淀微信壳能力、页面 URL、结果 hash / storage key 和分享消息类型等常量,dispatch.js 只作为 protocolwebViewpaymentshareGrid 的薄索引,真实协议归一、支付 / 分享结果编解码仍分别在 webView.jspayment.jsshareGrid.js,不把微信小程序硬改成 Expo / Tauri 的 request 总线;Page 生命周期、wx.* 容器调用、WebView 容器行为和页面工厂统一放在 miniprogram/shell/webView.jspayment.jsshareGrid.js,页面入口只做 Page(createWechat...Page()) 装配。Expo 移动壳使用 apps/mobile-shell/src/host-bridge/protocol.ts 承接 envelope、request 校验、ok / failure 响应和 replay 基础类型,capabilities.ts 只引用共享 HostBridge capability profile 并选择 iOS 差异能力,dispatch.ts 承接 method 分发和宿主能力调用,appearance.ts 承接系统配色读取,navigation.ts 承接外链打开、受控 H5 跳转和 WebView 刷新,network.ts 承接网络状态查询,badge.ts 承接受控角标能力,clipboard.ts 承接剪贴板读写与 HostBridge payload / 响应边界,files.ts 承接 Expo DocumentPicker / ImagePicker / File / Sharing 系统交互、取消语义、读写编排和 HostBridge 响应包装,filePayloads.ts 承接文件 MIME、大小、base64、文件名清洗和 picker 结果到 HostBridge payload 的边界,share.ts / scanner.ts / notifications.ts 分别承接分享、扫码和本地通知能力,bridge.ts 只作为 WebView message 入口、request id replay 编排和对外 facadeapps/mobile-shell/App.tsx 只装配 apps/mobile-shell/src/shell/ShellApp.tsx,由 apps/mobile-shell/src/shell/*.ts(x) 承接 WebView 容器、URL、导航、网络、生命周期、安全区、扫码 overlay 和 WebView policy。Tauri 桌面壳使用 apps/desktop-shell/src-tauri/src/host_bridge/protocol.rs 承接 envelope、method 白名单、request 校验和 replay 状态,runtime.rs 承接桌面 runtime 回包的平台、hostVersion、bridgeVersion 和 capability 清单组装,appearance.rs 承接窗口主题读取和 HostBridge 配色归一,navigation.rs 承接外链打开、受控 H5 跳转和主窗口刷新,network.rs 承接网络状态查询,badge.rs 承接受控任务栏角标能力,clipboard.rs 承接剪贴板读写与 HostBridge payload / 响应边界,title.rs 承接窗口标题 payload / 响应边界,capabilities.rs 承接共享桌面 capability profile 的 Rust 运行时镜像,dispatch.rs 承接 method 分发和宿主能力调用,files.rs 承接系统文件对话框、取消语义和异步读写编排,file_payloads.rs 承接文件 MIME、大小、base64、文件名清洗、本地副本读写和 HostBridge payload 边界,share.rs / notifications.rs 分别承接分享和本地通知能力,mod.rs 只保留模块声明、必要 re-export、host_bridge_request command facade 和 replay 编排;apps/desktop-shell/src-tauri/src/shell/runtime.rsurl.rsnavigation.rsnetwork.rslifecycle.rsfile_drop.rsevents.rsdeep_link.rstray.rswindow_state.rswebview.rs 分别承接运行态、入口 URL、导航 / 下载、网络、生命周期、拖拽图片、HostBridge 事件注入、深链、托盘、窗口状态持久化和 WebView 门面,apps/desktop-shell/src-tauri/src/app.rs 承接 Tauri builder / plugin / window 装配,main.rs 只保留薄入口并调用 app::run()

当前 npm run check:native-shells 锁定的生产文件清单以本文后续“结构门禁按完整相对路径反查文档和目录”段落为唯一文档口径;不要再维护只含文件名的短清单,避免测试文件、file_payloads.rs 或新增宿主脚本登记发生文档漂移。

根级原生壳检查同时锁定 HostBridge 模块分类:dispatch / protocol 是微信、移动、桌面三端共同模块;appearancebadgecapabilitiesclipboardfile-payloadsfilesnavigationnetworknotificationsruntimeshare 是 Expo / Tauri 原生 App 壳共同模块;移动端专属 bridgehapticsscanner,桌面端专属 modtitle,微信端专属 paymentshareGridwebView。后续新增或拆分桥接模块不能只改文件清单,必须先说明它属于三端共同、原生 App 共同还是某端专属。

2026-07-18 调整:旧玩法生成结果订阅授权已退役,微信 capability profile 不再声明 navigation.openNativePagesubscribeMessage host-bridge / shell / page 已从仓库删除,不再进入小程序包和 check:native-shells 现役文件清单。

生产替身词扫描只覆盖上述壳源码、分发配置、共享 HostBridge 契约和已接入真实宿主能力的 H5 调用链;Expo export、Tauri target/、Cargo / Metro 缓存和 release 构建产物不进入扫描范围,避免本地或 CI 生成文件污染源码门禁。

根级原生壳检查还会反查两端单端配置检查的依赖版本门禁:移动壳必须继续锁定 Expo SDK、React Native、WebView、EAS CLI 和 package-lock.json 实际解析版本;桌面壳必须继续锁定 Tauri CLI、Cargo manifest、Cargo.lock 解析版本和直接依赖关系。升级 Expo / React Native / Tauri 不能只改 package.jsonCargo.toml,必须同时更新单端配置检查、锁文件和本文档里的宿主能力假设。

桌面壳的 permissions/autogenerated/ 由 Tauri build.rs 生成且保持 gitignored。npm run check:native-shells 必须先运行桌面 Rust 测试生成权限清单,再执行桌面配置检查,最后执行 release build smoke;不能让干净检出因为静态检查早于生成步骤而失败,也不能把生成权限文件提交进仓库。

结构门禁按完整相对路径反查文档和目录:微信桥接层为 miniprogram/host-bridge/dispatch.jsminiprogram/host-bridge/payment.jsminiprogram/host-bridge/protocol.jsminiprogram/host-bridge/shareGrid.jsminiprogram/host-bridge/webView.js;微信 shell 层为 miniprogram/shell/payment.jsminiprogram/shell/shareGrid.jsminiprogram/shell/webView.js;微信页面包装层为 miniprogram/pages/share-grid/index.jsminiprogram/pages/share-grid/index.jsonminiprogram/pages/share-grid/index.wxmlminiprogram/pages/share-grid/index.wxssminiprogram/pages/web-view/index.jsminiprogram/pages/web-view/index.jsonminiprogram/pages/web-view/index.wxmlminiprogram/pages/web-view/index.wxssminiprogram/pages/wechat-pay/index.jsminiprogram/pages/wechat-pay/index.jsonminiprogram/pages/wechat-pay/index.wxmlminiprogram/pages/wechat-pay/index.wxss;移动源码根为 apps/mobile-shell/src/env.d.ts;移动桥接层为 apps/mobile-shell/src/host-bridge/appearance.test.tsapps/mobile-shell/src/host-bridge/appearance.tsapps/mobile-shell/src/host-bridge/badge.test.tsapps/mobile-shell/src/host-bridge/badge.tsapps/mobile-shell/src/host-bridge/bridge.tsapps/mobile-shell/src/host-bridge/capabilities.test.tsapps/mobile-shell/src/host-bridge/capabilities.tsapps/mobile-shell/src/host-bridge/clipboard.test.tsapps/mobile-shell/src/host-bridge/clipboard.tsapps/mobile-shell/src/host-bridge/dispatch.tsapps/mobile-shell/src/host-bridge/filePayloads.test.tsapps/mobile-shell/src/host-bridge/filePayloads.tsapps/mobile-shell/src/host-bridge/files.test.tsapps/mobile-shell/src/host-bridge/files.tsapps/mobile-shell/src/host-bridge/haptics.test.tsapps/mobile-shell/src/host-bridge/haptics.tsapps/mobile-shell/src/host-bridge/navigation.test.tsapps/mobile-shell/src/host-bridge/navigation.tsapps/mobile-shell/src/host-bridge/network.test.tsapps/mobile-shell/src/host-bridge/network.tsapps/mobile-shell/src/host-bridge/notifications.test.tsapps/mobile-shell/src/host-bridge/notifications.tsapps/mobile-shell/src/host-bridge/protocol.test.tsapps/mobile-shell/src/host-bridge/protocol.tsapps/mobile-shell/src/host-bridge/runtime.test.tsapps/mobile-shell/src/host-bridge/runtime.tsapps/mobile-shell/src/host-bridge/scanner.test.tsapps/mobile-shell/src/host-bridge/scanner.tsapps/mobile-shell/src/host-bridge/share.test.tsapps/mobile-shell/src/host-bridge/share.ts;移动 shell 层为 apps/mobile-shell/src/shell/QrScannerOverlay.test.tsxapps/mobile-shell/src/shell/QrScannerOverlay.tsxapps/mobile-shell/src/shell/ShellApp.tsxapps/mobile-shell/src/shell/deepLink.tsapps/mobile-shell/src/shell/lifecycle.tsapps/mobile-shell/src/shell/loadFailure.tsapps/mobile-shell/src/shell/navigation.tsapps/mobile-shell/src/shell/network.tsapps/mobile-shell/src/shell/runtime.tsapps/mobile-shell/src/shell/safeArea.tsapps/mobile-shell/src/shell/url.tsapps/mobile-shell/src/shell/webViewGlobals.d.tsapps/mobile-shell/src/shell/webViewHistory.tsapps/mobile-shell/src/shell/webViewPolicy.ts;桌面入口为 apps/desktop-shell/src-tauri/src/app.rsapps/desktop-shell/src-tauri/src/main.rs;桌面桥接层为 apps/desktop-shell/src-tauri/src/host_bridge/appearance.rsapps/desktop-shell/src-tauri/src/host_bridge/badge.rsapps/desktop-shell/src-tauri/src/host_bridge/capabilities.rsapps/desktop-shell/src-tauri/src/host_bridge/clipboard.rsapps/desktop-shell/src-tauri/src/host_bridge/dispatch.rsapps/desktop-shell/src-tauri/src/host_bridge/file_payloads.rsapps/desktop-shell/src-tauri/src/host_bridge/files.rsapps/desktop-shell/src-tauri/src/host_bridge/mod.rsapps/desktop-shell/src-tauri/src/host_bridge/navigation.rsapps/desktop-shell/src-tauri/src/host_bridge/network.rsapps/desktop-shell/src-tauri/src/host_bridge/notifications.rsapps/desktop-shell/src-tauri/src/host_bridge/protocol.rsapps/desktop-shell/src-tauri/src/host_bridge/runtime.rsapps/desktop-shell/src-tauri/src/host_bridge/share.rsapps/desktop-shell/src-tauri/src/host_bridge/title.rs;桌面 shell 层为 apps/desktop-shell/src-tauri/src/shell/deep_link.rsapps/desktop-shell/src-tauri/src/shell/events.rsapps/desktop-shell/src-tauri/src/shell/file_drop.rsapps/desktop-shell/src-tauri/src/shell/lifecycle.rsapps/desktop-shell/src-tauri/src/shell/mod.rsapps/desktop-shell/src-tauri/src/shell/navigation.rsapps/desktop-shell/src-tauri/src/shell/network.rsapps/desktop-shell/src-tauri/src/shell/runtime.rsapps/desktop-shell/src-tauri/src/shell/tray.rsapps/desktop-shell/src-tauri/src/shell/url.rsapps/desktop-shell/src-tauri/src/shell/webview.rsapps/desktop-shell/src-tauri/src/shell/window_state.rs。这些目录不得新增未登记子目录或生产入口;移动端和桌面端单端配置检查同样会拒绝未登记生产模块。

HostBridge 消息协议

H5 进入原生 App 壳时由壳层附加稳定 query:

?clientRuntime=native_app
&clientType=native_app
&hostShell=expo_mobile|tauri_desktop
&hostPlatform=ios|android|macos|windows|linux
&hostVersion=0.1.0
&bridgeVersion=1
&hostCapabilities=host.getRuntime,...

这些字段名和值不在各壳里单独定义。packages/shared/src/contracts/hostBridge.tsHOST_BRIDGE_RUNTIME_CONTEXT_QUERY_KEY 是 H5 runtime parser 的字段名来源,HOST_BRIDGE_NATIVE_APP_QUERY_KEY / HOST_BRIDGE_NATIVE_APP_QUERY_KEYS / HOST_BRIDGE_NATIVE_APP_QUERY 是 Expo 与 Tauri 原生壳入口 query 来源,HOST_BRIDGE_WECHAT_MINI_PROGRAM_SOURCE_QUERY 是微信 WebView clientType=mini_program / clientRuntime=wechat_mini_program 来源,HOST_BRIDGE_PRESERVED_RUNTIME_CONTEXT_QUERY_KEYS 是 H5 页面内导航保留宿主上下文的字段来源,必须同时覆盖微信小程序来源字段和原生壳 hostShellhostPlatformhostVersionbridgeVersionhostCapabilities 完整运行态字段。Expo 直接导入共享常量,Tauri Rust 和微信 CommonJS 镜像由检查脚本反查共享契约。

消息 envelope 统一为 JSON

type HostBridgeRequest = {
  bridge: 'GenarrativeHostBridge';
  version: 1;
  id: string;
  method: HostBridgeMethod;
  payload?: unknown;
  timeoutMs?: number;
};

type HostBridgeResponse = {
  bridge: 'GenarrativeHostBridge';
  version: 1;
  id: string;
  ok: boolean;
  result?: unknown;
  error?: {
    code: string;
    message: string;
  };
};

type HostBridgeEventName =
  | 'app.lifecycle'
  | 'network.statusChanged'
  | 'navigation.canGoBack'
  | 'file.imageDropped';

type HostBridgeEvent = {
  bridge: 'GenarrativeHostBridge';
  version: 1;
  event: HostBridgeEventName;
  payload?: unknown;
};

事件名同样是协议白名单,唯一来源为 packages/shared/src/contracts/hostBridge.tsHOST_BRIDGE_EVENTSExpo 壳注入函数使用 HostBridgeEventNameTauri 壳在 shell/events.rs 镜像同一清单并拒绝未知事件,H5 transport 只分发白名单内事件。

首批 method

method 用途 Expo 壳 Tauri 壳
host.getRuntime 返回宿主、平台、版本和能力清单 支持 支持
appearance.getColorScheme 读取宿主当前配色模式 支持系统 Appearance 读取 支持窗口 theme 读取
auth.requestLogin 打开宿主登录或账号绑定流程 未声明,继续走 H5 fallback 未声明,继续走 H5 fallback
payment.request 发起宿主支付 未声明,等待真实 IAP / Google Play Billing / 国内渠道流 未声明,等待真实桌面支付渠道流
share.setTarget 同步当前作品分享目标 支持 支持
share.open 打开分享动作 支持系统分享面板 复制分享文本到剪贴板
navigation.openNativePage 打开受控宿主页 支持同源 H5 route 支持同源 H5 route
app.reloadWebView 受控刷新当前 WebView 支持 WebView reload 支持主窗口 reload
app.lifecycle 通知 H5 宿主前后台 / 焦点状态 支持 AppState 事件 支持窗口 focus / blur 事件
navigation.canGoBack 通知 H5 宿主返回栈状态 支持事件 支持 H5 当前文档路由栈事件
app.openExternalUrl 用系统浏览器打开外链 支持白名单协议 支持白名单协议
app.setTitle 同步宿主窗口标题 不声明 支持
app.setBadgeCount 设置应用 / 任务栏角标 仅 iOS 支持,需 Expo Notifications badge 权限和 setBadgeCountAsync 成功返回 支持;平台底层不支持时返回错误
network.status 查询宿主网络状态 支持 Expo Network 支持主站可达性短超时查询
network.statusChanged 通知 H5 网络状态变化 支持 Expo Network 事件 桌面暂不声明,避免把 WebView online / offline 当作 Rust 桌面网络事实
clipboard.writeText 写剪贴板 支持 支持
clipboard.readText 读取纯文本剪贴板 支持 支持
file.exportText 导出文本到用户选择的本地文件 支持系统分享 / 保存面板 支持系统保存对话框
file.importText 导入用户选择的文本文件 支持系统文档选择器 支持系统选择文本文件
file.importDocument 导入用户选择的文本 / DOCX 文档副本 支持系统文档选择器 支持系统选择文档文件
file.exportImage 导出当前 H5 已持有的图片文件 支持系统分享 / 保存面板 支持系统保存对话框
file.importImage 导入用户选择的图片文件 支持系统相册选择图片 支持系统选择图片
file.captureImage 拍摄图片并导入当前 H5 流程 支持系统相机拍照 不声明
scanner.scanQrCode 扫描二维码并回传文本结果 支持 Expo Camera 扫码 不声明,返回 unsupported
file.importAudio 导入用户选择的音频文件 支持系统文档选择器 支持系统选择音频文件
file.exportAudio 导出当前 H5 已持有的音频文件 支持系统分享 / 保存面板 支持系统保存对话框
file.imageDropped 通知 H5 桌面拖入图片 不声明 支持主窗口拖拽图片事件
haptics.impact 轻量触感反馈 支持 不声明
notification.showLocal 发送即时本地系统通知 支持 Expo Notifications 支持 Rust 侧 Tauri notification

每个 method 都必须有明确 payload schema、超时、错误码和能力开关;H5 看到不支持时回退到现有浏览器路径。H5 发起原生壳请求的超时边界统一从 packages/shared/src/contracts/hostBridge.ts 读取:普通请求使用 HOST_BRIDGE_DEFAULT_REQUEST_TIMEOUT_MS,传入值最多裁剪到 HOST_BRIDGE_MAX_REQUEST_TIMEOUT_MS;宿主 runtime 回读使用 HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS;文件导入 / 导出、图片选择 / 拍摄和音频导入 / 导出使用 HOST_BRIDGE_USER_INTERACTION_TIMEOUT_MS;二维码扫码使用 HOST_BRIDGE_SCANNER_TIMEOUT_MS。H5 facade 不得在调用点手写 3000060000 等毫秒字面量,根级 check:native-shells 与桌面壳配置检查会拦截回退。

Expo React Native 壳

Expo 壳只负责 App 外壳和原生能力,不承接玩法业务。

推荐能力:

  • react-native-webview 加载 Genarrative H5。
  • H5 到 RNwindow.ReactNativeWebView.postMessage(JSON.stringify(request))
  • RN 到 H5:通过 WebView ref 注入脚本,向 H5 派发统一 bridge response / event。
  • 使用 development build,不依赖 Expo Go 作为真实集成环境;需要自定义原生配置时用 config plugin / prebuild 管理。
  • App 壳维护启动页、深链、受控分享动作、即时本地通知、权限和 App 版本;远程推送、崩溃日志、支付 SDK 等能力必须等真实端点、渠道合同、发布流程和隐私口径确定后逐项接入。
  • iOS 分发配置必须在 expo.ios.privacyManifests 中声明当前 RN / Expo 原生依赖实际使用的 required reason API,且不声明未使用的数据采集或追踪域;移动壳配置检查必须反查该清单,并确认当前 Expo config plugin 仍会消费该字段写入 PrivacyInfo.xcprivacy,避免升级 Expo SDK 或新增原生模块后隐私声明漂移。
  • 登录首期优先复用 H5 账号体系;后续再逐项接入 Apple / Android / 微信等原生登录能力。
  • 支付必须按上架渠道拆分:iOS / Android 虚拟内容优先评估 IAP / Google Play Billing 或国内渠道要求;H5 支付、小程序虚拟支付和桌面二维码支付不能直接照搬到 App Store 包。

移动端推荐首屏流程:

Expo App 启动
  -> 读取环境和远端 H5 URL
  -> WebView 加载 /?clientRuntime=native_app&hostShell=expo_mobile...
  -> H5 getHostRuntime() 识别 native_app
  -> H5 通过 HostBridge 请求宿主能力
  -> RN 壳按 allowlist 执行并回包

第一版移动端不建议做大量 RN 原生 Tab / 页面。当前 H5 已有移动端一级 Tab,重复实现会带来导航状态、登录态、返回栈和 UI 双维护成本。

Tauri 桌面壳

Tauri 壳同样只负责桌面宿主能力,不承接玩法业务。

推荐能力:

  • Release 包默认加载共享公开主站 https://www.genarrative.world/,不再打包根 H5 dist,避免包内 tauri.localhost 与线上 API 来源分裂。
  • Dev 模式允许加载本地 Vite URL,方便调试。
  • H5 通过 nativeAppHostBridge 超时封装调用 window.__TAURI__.core.invoke('host_bridge_request', request),不直接调用其它 Tauri command。
  • Rust 侧只暴露一个受控 host_bridge_request command,再在 Rust 内部按 method 白名单分发。
  • Tauri capabilities 只授予主窗口所需命令;默认不开放文件系统、shell、全局剪贴板或任意插件能力。
  • 主窗口 capability 只授予 allow-host-bridge-request;不得使用 core:defaultcore:*:default 或插件 command 权限作为兜底。窗口、菜单、托盘、剪贴板、文件、通知和外链能力只能由 Rust 壳内部调用,再通过 host_bridge_request 的 HostBridge method 白名单分发给 H5。
  • 桌面壳启动时必须按 label="main" 解析 tauri.conf.json 主窗口配置,并在创建 WebView 前补写 native_apptauri_desktop 和真实 capability 上下文;缺少主窗口配置时启动直接失败,不允许按 windows[0] 兜底或无主窗口静默运行。
  • 桌面支付首期走现有 H5 / 二维码 / 外部浏览器路径,不在 Rust 侧保存支付凭据。
  • 文件导出、作品卡保存、图片拖拽导入、系统托盘、自动更新等桌面能力按后续需求逐项开放;其中系统托盘属于桌面壳自有能力,不作为 HostBridge method 暴露给 H5。
  • 主 WebView 默认拒绝网页自动下载或 <a download> 触发的落盘动作;用户保存文本、图片、音频等内容必须走已声明的 file.exportTextfile.exportImagefile.exportAudio HostBridge method,由 Rust 侧执行 MIME、大小、文件名清洗和系统保存对话框确认。
  • 主 WebView 显式关闭 DevToolsCargo 不启用 Tauri devtools feature;本地调试通过普通浏览器和 Vite 完成,不把可分发桌面壳变成调试容器。
  • 崩溃上报、前端 analytics、桌面遥测日志、自动更新和渠道分发 SDK 都必须等真实端点、采集字段、用户同意、隐私策略、签名和发布流程确定后逐项接入;当前桌面壳不安装 Sentry、Datadog、PostHog、Segment、Amplitude、Bugsnag、OpenTelemetry、Tauri log / updater 等相关依赖。
  • 桌面壳和根 H5 包不安装 @tauri-apps/api@tauri-apps/plugin-* JS guest 包;生产 H5 只通过 Tauri 注入的 window.__TAURI__.core.invoke('host_bridge_request', request) 进入 HostBridge。opener、clipboard、dialog、notification 等能力只保留 Rust Cargo 插件,由 Rust 内部分发并受 capability 白名单约束。
  • 桌面深链只作为宿主启动 / 唤醒入口处理,不进入 HostBridge capability,也不把 deep-link 插件 command 授权给 H5。Tauri 只注册 genarrative scheme,并接受同源 https://www.genarrative.world URL;壳层会把目标路径归一为带 native_apptauri_desktop 和真实 capability 清单的同源 H5 URL,外域、明文协议和危险协议直接丢弃。navigation.openNativePage 的同源主动跳转也必须复用同一宿主上下文补写逻辑,避免新页面按普通浏览器运行态启动。
  • 桌面窗口状态持久化属于宿主壳自有体验,不进入 HostBridge capability,也不开放窗口状态插件 command 给 H5。Tauri 壳只保存主窗口大小、位置和最大化状态,不保存可见性、全屏或装饰状态,避免托盘隐藏窗口后下次启动被恢复成隐藏状态;shell/window_state.rs 必须保留 Rust 单测证明这组 flags 边界。
  • 桌面壳外链打开、WebView 新窗口外链接管、托盘关闭前生命周期注入和窗口隐藏都属于用户可见宿主动作;这些动作失败必须走统一桌面宿主事件日志,配置检查拒绝 let _ = ... 静默吞错。
  • 桌面壳托盘注册失败只允许记录 desktop tray registration failed 固定标签,不把 Tauri tray 插件错误详情写入可分发桌面壳 stderr;托盘不可用时主窗口关闭仍按无托盘路径退出。

桌面 release 和 dev 模式:

release:
  Tauri binary
    -> packaged web assets
    -> /index.html?clientRuntime=native_app&hostShell=tauri_desktop...

dev:
  Tauri binary
    -> http://127.0.0.1:<vite-port>/?clientRuntime=native_app&hostShell=tauri_desktop...

如果未来希望桌面端加载远端 H5 URL,必须额外做 origin allowlist、版本协商和 Tauri API 暴露限制;不能让任意远端页面拿到桌面命令。

AI H5 沙箱边界

移动端和桌面端统一后,AI 生成 H5 游戏仍不能直接接入 HostBridge

AI H5 游戏运行结构:

平台 H5 runtime
  -> sandbox iframe
    -> AI 生成 H5 游戏
    -> window.parent.postMessage(GameBridgeRequest)

GameBridge 只允许:

  • 读取启动参数和只读资产 URL。
  • 上报 ready、progress、score、event、error。
  • 提交候选结果给父页面,由父页面和后端裁决。
  • 请求有限的音频、震动、全屏等运行态能力。

GameBridge 禁止:

  • 登录、支付、订阅授权。
  • 读取 token、cookie、完整用户资料。
  • 任意网络代理。
  • 任意本地文件、剪贴板和系统命令。
  • 直接调用 Expo / Tauri / 小程序宿主能力。

安全约束

  • HostBridge request 必须校验 bridgeversionidmethod 和 payload shapeid 归一后必须是 1-120 字符且不含控制字符,method 必须来自共享白名单,未知 method 作为非法 request 拒绝。
  • 壳层只接受来自允许 origin / packaged asset 的消息。
  • H5 侧 HostBridge listener 只接收原生壳注入到当前窗口的 message;带有非当前窗口 source 或非当前页面 origin 的消息必须忽略,避免 AI sandbox iframe 或其它子上下文伪造 HostBridge response / event。
  • 每个请求必须有超时;H5 的 React Native WebView transport 和 Tauri invoke transport 都必须在前端侧按 timeoutMs 释放请求,默认请求超时、最大请求超时和用户交互长操作超时以 packages/shared/src/contracts/hostBridge.tsHOST_BRIDGE_DEFAULT_REQUEST_TIMEOUT_MS / HOST_BRIDGE_MAX_REQUEST_TIMEOUT_MS / HOST_BRIDGE_USER_INTERACTION_TIMEOUT_MS 为唯一来源,宿主侧执行超时也只能返回标准 HostBridge 错误。重复 id 不得重复执行支付、登录、受控分享动作、文件导入导出、本地通知等宿主副作用;Expo 和 Tauri 壳都必须按 request id 回放首次完成结果,已完成响应缓存上限以共享契约 HOST_BRIDGE_RESPONSE_CACHE_MAX 为唯一来源。
  • HostBridge 的 capability profile、宿主上下文 query 字段和值、文件 MIME 清单、导入 / 导出体积上限、文件名 fallback / 长度上限、request id 长度、角标上限、剪贴板文本长度、二维码文本长度、本地通知标题 / 正文长度、移动端 Android 本地通知 channel id 和桌面网络探测超时都必须以 packages/shared/src/contracts/hostBridge.ts 为声明来源;Expo 移动壳直接导入共享 profile 和契约常量,微信小程序壳和 Tauri 壳分别保留小程序 CommonJS / Rust 运行时代码镜像并由测试和配置门禁反查共享契约。
  • 能力按 capabilities / hostCapabilities 下发,H5 会过滤未知能力,并根据声明结果决定是否展示入口、发起宿主请求或走 fallback;进入 native_app 后主 App 会再通过真实 host.getRuntime 回读一次宿主 runtime 并缓存能力,用来补齐裁剪壳或旧入口 URL 缺少 hostCapabilities 的场景,该回读请求的短超时以共享契约 HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS 为唯一来源。不能只凭 native_app 宿主类型假设能力可用。
  • 壳能力声明与三端壳验收必须通过 npm run check:native-shells 统一校验;排查单端问题时可再分别运行微信壳测试集合、npm run mobile-shell:typechecknpm run mobile-shell:testnpm run mobile-shell:confignpm run mobile-shell:exportnpm run desktop-shell:typechecknpm run desktop-shell:testnpm run desktop-shell:build -- --no-bundle。声明的 capability 必须来自共享 HostBridge profile 并存在于共享白名单,壳 runtime 回包、H5 URL hostCapabilities、壳实现、文件载荷边界、微信 WebView / 支付 / 分享桥接行为、微信小程序页面路由、WebView source query、微信请求头运行时标记、H5 runtime parser、H5 路由保留字段、微信壳 H5 / API HTTPS 域名格式、Expo managed config、移动端 production bundle、桌面 release 构建入口和微信 / Expo / Tauri 三端生产源码临时替身词扫描不得漂移。微信小程序壳不使用 Expo / Tauri 式统一 request dispatcher,但每个声明 capability 都必须在根级门禁中映射到真实流程文件、关键 wx.* 或页面工厂调用和对应测试清单;Expo 移动壳和 Tauri 桌面壳的关键能力也必须在根级门禁中映射到真实 Expo / React Native / Tauri API、权限或配置片段、宿主分发文件和对应测试清单。
  • Expo SDK、React Native、react-native-webview、Tauri CLI、Tauri Rust crate 和桌面 Cargo 插件版本属于宿主壳行为边界。升级这些依赖前必须同步更新壳配置检查、package-lock.json / Cargo.lock 解析版本、本文档和对应验证结果,不能只改 package / Cargo 版本让生产壳行为静默漂移。
  • 登录和支付能力在真实 SDK、渠道流程、后端契约和失败回退全部落地前不得进入 Expo / Tauri capabilities,也不得写进入口 URL hostCapabilities;两端配置检查会拒绝 auth.requestLoginpayment.request 的伪声明。
  • 宿主壳不得把长期 token、支付密钥或用户敏感资料回传给 H5。
  • 桌面壳不得提前安装或初始化崩溃上报、analytics、遥测日志、自动更新或渠道分发 SDK;这类能力必须先补齐真实后端 / 第三方端点、采集口径、用户授权、隐私披露、签名和发布流程,再进入 Tauri 配置、Cargo 依赖、Node 依赖或 Rust 初始化代码。
  • Tauri 禁止把 shell / fs 等高危插件作为默认能力暴露给主 WebView。
  • Tauri 主 WebView 禁止默认下载落盘;桌面文件保存只能通过受控 HostBridge 导出能力进入系统保存对话框。
  • Tauri 主 WebView 禁止默认打开 DevTools;不得通过配置或 Cargo feature 为分发壳启用浏览器检查器。
  • RN WebView 禁止打开任意 URL 后仍保留完整 HostBridge;跳外链只允许 http:https:mailto:tel:,并使用系统浏览器或降级能力,危险协议直接阻断。
  • RN WebView 禁止网页自动下载、下载协议导航和 <a download> 直接落盘;禁止下载协议清单以 packages/shared/src/contracts/hostBridge.tsHOST_BRIDGE_MOBILE_WEBVIEW_BLOCKED_DOWNLOAD_PROTOCOLS 为唯一来源,移动端文件保存只能通过 file.exportTextfile.exportImagefile.exportAudio 等受控 HostBridge method 进入系统分享 / 保存面板。
  • Expo 移动壳的通知能力只覆盖即时本地通知;Android 包配置必须阻断重启后通知恢复和精确定时权限,前端代码不得注册 Expo push token、远程推送监听或通知响应跳转流程。
  • AI sandbox iframe 必须使用独立 CSP、sandbox 属性和单独 GameBridge allowlist。

分阶段落地

Phase 1:补齐 H5 native_app adapter

  • src/services/host-bridge/ 增加 nativeAppHostBridge transport。
  • 定义 HostBridge envelope、method、错误码和超时策略。
  • getHostRuntime() 继续识别 clientRuntime=native_app
  • 现有业务入口只通过 HostBridge 调用登录、支付、分享、原生页跳转。
  • 增加 H5 单测覆盖:支持、超时、不支持、错误回包、浏览器 fallback。

当前状态:已新增 src/services/host-bridge/nativeAppHostBridge.ts,支持 React Native WebView postMessage 和 Tauri invoke('host_bridge_request') 两种真实 transport。两条 transport 都会按 timeoutMs 在 H5 侧释放请求,超时统一抛出 timeout / host_bridge_timeout。登录、支付和原生页跳转如果宿主明确返回 unsupported_method / unsupported_capability,H5 回退到原有路径;生产代码不返回 mock 成功。

2026-06-19 追加,2026-07-18 收口:微信小程序壳当前真实能力完整清单为 auth.requestLoginpayment.requestshare.setTargetshare.open。旧订阅授权页退役后,微信 profile 不再声明 navigation.openNativePageExpo / Tauri 的同源 H5 受控导航能力不受影响。

Phase 2Expo 移动壳 MVP

  • 新增 apps/mobile-shell/
  • 接入 react-native-webview,加载 H5 URL 并附加宿主 query。
  • 实现 HostBridge RN transportruntime、openExternalUrl、share、clipboard、haptics。
  • Android 返回键与 H5 history 对齐。
  • iOS / Android 深链打开作品详情、创作页和邀请码。
  • 登录和支付先 fallback 到 H5;只把能力边界跑通。

当前状态:已新增 apps/mobile-shell/,通过 Expo development build 运行,react-native-webview 加载 H5 URL 并附加 native_app 宿主 query。移动壳使用真实品牌图标资产,已接入 genarrative:// scheme、iOS associated domain 和 Android app link filter,启动和运行时 deep link 只会映射到同源 H5 路径并继续附加 HostBridge 上下文,外域和危险协议回退到默认主站入口并记录拒绝日志;系统初始 URL 读取失败也会记录错误且保留当前安全入口。首轮真实能力包括 host.getRuntimeappearance.getColorSchemehost.eventsapp.lifecyclenetwork.statusnetwork.statusChangedshare.openshare.setTargetnavigation.openNativePagenavigation.canGoBackapp.reloadWebViewapp.openExternalUrlclipboard.writeTextclipboard.readTextfile.exportTextfile.importTextfile.importDocumentfile.exportImagefile.importImagefile.captureImagescanner.scanQrCodefile.importAudiofile.exportAudiohaptics.impactnotification.showLocal 和 Android 返回键回退;其中 appearance.getColorScheme 只读系统配色偏好,不强改 H5 或系统主题;app.lifecycle 通过 React Native AppState 注入 active / inactive / background 统一状态,供 H5 游戏循环、音频和轮询做真实暂停 / 恢复判断,H5 的 useHostLifecycleActive() 会把该事件归一成运行态可播放状态,WebAudio 背景音乐和拼图、抓大鹅等固定玩法 <audio> 背景音乐都按该状态在宿主进后台时暂停、回到前台且原播放条件仍满足时恢复;network.status / network.statusChanged 通过 expo-network 查询并订阅真实系统网络状态,供 H5 游戏运行态和生成页识别离线 / 弱网回退;iOS 额外声明 app.setBadgeCount,通过 Expo Notifications 查询 / 请求 allowBadge 权限,并以 setBadgeCountAsync(count) 的返回值确认应用图标角标是否设置成功,Android 不声明该能力。H5 会解析并过滤 hostCapabilities,也会在主 App 启动时通过真实 host.getRuntime 回读并缓存能力,只对声明或回读到的能力展示入口或调用宿主能力;其中 share.setTarget / share.open 会解析统一分享目标里的 titlemessageurlworkpathtargetPath 并调用 React Native 系统分享面板,分享 URL 只能归一到 https://www.genarrative.world 公开主站同源 URL,外域、协议相对 URL 和危险协议会拒绝请求;发布分享弹窗在 Expo 移动壳声明 share.open 时提供“系统分享”动作,失败时保留复制链接回退路径;navigation.openNativePage 在 Expo 壳内只接受同源 H5 route 并切换 WebView URL,切换前会重新附加 clientRuntime=native_apphostShell=expo_mobile、真实平台、版本和 capability 清单,确保宿主主动导航后的新页面仍按原生壳运行态启动;该能力不伪装尚未存在的登录、支付或其它原生页面,navigation.canGoBack 由 WebView 导航状态变化实时注入 H5,app.reloadWebView 只调用当前 react-native-webviewreload() 刷新内嵌 H5,不开放任意脚本执行或 URL 重写;iOS onContentProcessDidTerminate 和 Android onRenderProcessGone 首次触发时只恢复同一 WebView,短时间内连续恢复失败会记录进程失败日志并展示既有原生加载失败兜底层,用户重试会清空失败窗口并再次刷新当前 WebView,不改写 URL、不注入额外脚本、不新增宿主恢复页面;WebView 自身拦截到外域导航时只会把 http:https:mailto:tel: 且系统 Linking.canOpenURL 确认可打开的外链交给系统,危险协议或当前设备不可打开的外链直接阻断;app.openExternalUrl 也只允许同一协议白名单,并在 Linking.canOpenURL 确认可打开后才离开容器,否则返回 host_error;ICP备案号和资产调试原图等 H5 外链入口在 native_app 中优先通过该能力离开 WebView 并交给系统浏览器;clipboard.writeText 由 H5 复制服务优先调用并写入系统剪贴板;clipboard.readText 通过 Expo Clipboard 读取纯文本剪贴板并按 100000 字符上限返回,不读取图片、HTML 或监听剪贴板变化;file.exportText 通过 Expo 文件系统写入缓存文本文件,再交给系统分享 / 保存面板,文件名必须清洗,单次文本不超过 5 MiB,成功只返回文件名和字节数;file.importDocument 通过 Expo DocumentPicker 打开系统文档选择器,只接受文本类文档和 DOCX,读取 base64 前必须先通过 picker size 或 Expo File.size 拿到可信 byte count 并完成 5 MiB 上限校验,成功只返回清洗后的文件名、MIME、base64 内容和字节数,不暴露设备本地 URI;file.exportImage 通过 Expo 文件系统写入缓存图片,再交给系统分享 / 保存面板,H5 只传允许 MIME 的 base64 图片数据,单次不超过 5 MiB,分享卡下载会优先走该能力;file.importImage 通过 Expo ImagePicker 请求相册权限并打开系统相册选择器,只接受 image/png / image/jpeg / image/webp、单次不超过 10 MiB,成功只返回清洗后的文件名、MIME、base64 内容和字节数,不把设备本地 URI 暴露给 H5,用户取消返回 cancelled 并由 H5 视作无选择;file.captureImage 通过 Expo ImagePicker 请求相机权限并打开系统相机拍摄图片,沿用同一 MIME、体积、base64 和文件名清洗规则,成功返回 captured 图片结果,不请求麦克风权限,也不把设备本地 URI 暴露给 H5;scanner.scanQrCode 通过 expo-camera 请求相机权限并打开 CameraView 扫描二维码,成功只返回清洗后的二维码文本和 qr_code 格式,空值、控制字符和超长文本按共享契约处理,用户关闭或权限拒绝返回明确错误并由 H5 保留现有扫码 fallback;通用创作图片输入面板 CreativeImageInputPanel 在原生壳声明 file.importImage / file.captureImage 时分别展示受控上传 / 拍摄图标动作,并把宿主返回结果转换为现有 File 上传回调,拼图、拼消消、敲木鱼等复用该面板的主图和描述参考图选择无需新增玩法分叉;反馈页上传凭证、个人资料头像上传、方洞结果页图片槽位上传和汪汪声浪结果页三图槽位上传在原生壳声明 file.importImage 时优先打开宿主系统图片选择器,并把宿主返回结果转回现有 H5 数据结构,其中反馈页继续复用原本的 4 张数量限制、单张 1 MiB、总 4 MiB 和 data URL 提交流程,头像继续复用现有图片类型、5 MiB 大小限制、方形裁剪和资料更新链路,方洞结果页继续写回现有封面 / 背景 / 形状 / 洞口槽位与自动保存链路,汪汪声浪结果页继续把图片交给 uploadBarkBattleAsset 上传和当前玩家 / 对手 / UI 背景槽位写回链路;file.importAudio 通过 Expo DocumentPicker 打开系统音频选择器,只接受 audio/mpegaudio/mp4audio/wavaudio/oggaudio/webm 或对应扩展名,单次不超过 20 MiB,读取音频或生成 base64 前必须先通过 picker size 或 Expo File.size 拿到可信 byte count 并完成上限校验,成功只返回清洗后的文件名、MIME、base64 内容和字节数,不暴露设备本地 URI;file.exportAudio 通过 Expo 文件系统写入缓存音频,再交给系统分享 / 保存面板,H5 只传当前页面已持有的允许 MIME 音频 base64 数据,单次不超过 20 MiB,成功只返回文件名和字节数;通用音频输入面板 CreativeAudioInputPanel 在原生壳声明导入能力时优先调用宿主导入,在声明导出能力且当前资产为本地 Blob 时展示导出入口,并继续复用现有 readFileAsAsset(file, 'uploaded') 处理链路;haptics.impact 通过 Expo Haptics 承接运行时轻触反馈,H5 在宿主不支持时回退到浏览器 vibration;notification.showLocal 通过 expo-notifications 请求系统通知权限并调度即时本地通知,Android 只使用固定本地 channel,不启用后台远程通知、远程推送 token 或定时提醒。登录和支付尚未接入渠道 SDK / 原生页面时明确返回 unsupported,让 H5 fallback 承接。

2026-06-18 追加:移动壳当前通用真实能力完整清单为 host.getRuntimeappearance.getColorSchemehost.eventsapp.lifecycleshare.openshare.setTargetnavigation.openNativePagenavigation.canGoBackapp.reloadWebViewapp.openExternalUrlnetwork.statusnetwork.statusChangedclipboard.writeTextclipboard.readTextfile.exportTextfile.importTextfile.importDocumentfile.exportImagefile.importImagefile.captureImagescanner.scanQrCodefile.importAudiofile.exportAudiohaptics.impactnotification.showLocal

2026-06-19 追加:移动壳声明并实现 scanner.scanQrCode,通过 expo-camera 请求系统相机权限并用 CameraView 扫描二维码。扫码控制器一次只允许一个进行中的请求,成功结果复用共享 normalizeHostBridgeQrCodeValue 边界,只回传清洗后的二维码文本和 qr_code 格式;用户关闭返回 cancelled,H5 个人中心扫码入口不会在取消后继续弹出浏览器摄像头权限。宿主缺能力、旧壳 unsupported 或返回非法结果时,H5 继续打开原浏览器扫码弹层。Tauri 桌面壳只保留 method 白名单并返回 unsupported_method,不声明桌面扫码 capability。

2026-06-19 追加:微信小程序壳真实能力 profile 为 HOST_BRIDGE_WECHAT_MINI_PROGRAM_CAPABILITIES,当前包含 auth.requestLoginpayment.requestshare.setTargetshare.opennavigation.openNativePageminiprogram/host-bridge/protocol.js 继续保留 WECHAT_HOST_CAPABILITIES 作为小程序运行时镜像,不直接 import TypeScript shared 包,miniprogram/host-bridge/protocol.test.js 和根级 npm run check:native-shells 必须反查它与共享微信 profile 完全一致。微信壳能力由 WebView 登录页、支付页、分享目标消息、九宫切图页和订阅生成结果通知页等真实页面流程承接,不改造成统一 request dispatchernavigation.openNativePage 在微信壳中只表示受控打开已登记的小程序原生页,当前用于订阅授权等已有页面,不代表任意小程序页面跳转。根级门禁会把每个微信 capability 反查到对应 miniprogram/host-bridge/*miniprogram/shell/*miniprogram/pages/*、H5 入口文件、关键 wx.login / wx.requestPayment / wx.requestVirtualPayment / wx.saveImageToPhotosAlbum / wx.requestSubscribeMessage 调用或页面工厂,以及已纳入 check:native-shells 的测试文件。移动壳 iOS 额外真实能力为 app.setBadgeCountpackages/shared/src/contracts/hostBridge.ts 中的 HOST_BRIDGE_WECHAT_MINI_PROGRAM_CAPABILITIESHOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIESHOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIESHOST_BRIDGE_TAURI_DESKTOP_CAPABILITIES 是三端宿主能力 profile 来源;移动壳直接引用共享 profile,微信小程序壳和桌面壳分别以运行时镜像承接并由门禁反查同一共享 profile。npm run check:native-shells 会从共享 profile 反查能力清单文档、小程序协议镜像、微信 capability 真实流程映射和桌面 Rust 镜像,避免实现、入口 URL 和方案文档再次漂移。

2026-06-18 追加:移动壳 navigation.canGoBack 不再只读取 react-native-webview 的原生跨文档导航状态。Expo 壳会在 WebView injectedJavaScriptBeforeContentLoaded 中注入固定脚本,追踪当前 H5 文档内 pushState / replaceState / popstate 写入的路由栈,并通过内部 genarrative.mobile.historyState 消息回传;壳层把原生 WebView back-forward 状态与 H5 当前文档路由栈状态合成为 HostBridge navigation.canGoBack 事件。Android 返回键优先执行固定 window.history.back(); true; 回退 H5 SPA 路由,只有 H5 当前文档不可回退时才调用 WebView 原生 goBack();该内部消息不是 HostBridge request method,也不开放 H5 到原生的通用事件写入通道。

2026-06-19 追加:H5 主 App 开始消费 navigation.canGoBack 事件。useHostNavigationCanGoBack() 只有在宿主同时声明 host.eventsnavigation.canGoBack 时才订阅返回栈状态,并会在宿主 runtime 刷新后重新确认能力;原生壳内直达非平台首页、非 runtime 的二级 H5 route 且当前 history state 没有应用导航标记时,App 会先把当前条目替换成 / 返回锚点,再把当前路径连同保留的宿主 query 推回 history,保证 Android 返回键、桌面菜单后退或宿主回退事件能落回平台首页。H5 不读取任意原生 back-forward list,普通浏览器、小程序、runtime 路由、已有应用 history 或旧壳缺能力时不注入该锚点。

2026-06-18 追加:移动壳声明并实现 file.importText,通过 Expo DocumentPicker 打开系统文档选择器,只接受 text/plaintext/markdowntext/csvapplication/json 或对应扩展名,单次不超过 5 MiB;读取文本内容前必须先通过 picker size 或 Expo File.size 拿到可信 byte count 并完成上限校验,成功只返回清洗后的文件名、MIME、UTF-8 文本内容和字节数,不暴露设备本地 URI,也不开放通用文件系统。H5 创作 Agent 工作台在原生壳声明该能力时优先打开宿主系统选择器,再把返回文本转换成现有浏览器 File 并继续调用 /api/runtime/creation-agent/document-inputs/parse,不在前端绕过后端文档解析、大小校验或 docx 处理。

2026-06-19 追加:H5 创作 Agent 工作台在移动壳声明 file.exportText 时提供会话 Markdown 导出入口。H5 只把当前会话标题、摘要、进度、锚点、消息、流式回复和输入草稿组装成 text/markdown 文本,先按共享 5 MiB 上限计算 UTF-8 byte,再通过 exportHostTextFile() 交给 Expo 系统分享 / 保存面板;宿主取消、缺能力或 unsupported 时不做浏览器下载回退,保持原生壳文件保存只走受控 HostBridge 能力。

2026-06-20 追加:移动壳文件能力的原生系统异常必须可观测。Expo Sharing 可用性 / 分享面板、DocumentPicker、文本 / base64 文件读取、相册 / 相机权限请求和相册 / 相机打开失败时,apps/mobile-shell/src/host-bridge/files.ts 必须记录 mobile HostBridge file failed for ... 日志;HostBridge 回包仍只返回稳定的 host_error / unsupported_capability / cancelled / invalid_request 语义,不把原生异常明细透传给 H5。移动壳配置检查反查这些日志 label 和对应单测,避免用户可见文件导入导出动作失败后只剩静默兜底。

2026-06-20 追加:移动壳系统分享失败必须可观测。React Native Share.share(...) reject 时,apps/mobile-shell/src/host-bridge/share.ts 必须记录 mobile share failed for open.share 日志;HostBridge 回包仍只暴露稳定 share unavailable 语义,不透传原生分享面板异常明细。

2026-06-19 追加:视觉小说结果页素材选择弹窗在移动壳声明 file.importImage / file.importAudio 时优先打开宿主系统图片或音频选择器,把 HostBridge 返回的 base64 副本转换成浏览器 File 后继续调用 uploadVisualNovelAsset,不绕过平台素材上传、历史素材、AI 图片生成或草稿写回链路;用户取消原生选择时停留在壳流程内,不连带弹出浏览器文件输入。普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。

2026-06-19 追加:帮助与反馈页在移动壳声明 file.captureImage 时展示“拍摄凭证”入口,调用 Expo 相机拍摄后把 HostBridge 返回的图片副本转换成浏览器 File,继续复用反馈页最多 4 张、单张 1 MiB、总 4 MiB、图片 MIME、data URL 预览和提交 payload 校验。拍摄取消时停留在壳流程内,不触发浏览器文件输入;Tauri 桌面壳不声明拍摄能力,普通浏览器、小程序和未声明能力的裁剪壳不显示该入口。

2026-06-18 追加:移动壳图片和音频导入 / 导出不再只信 Expo picker 返回的 MIME 或 H5 声明的 MIME。Expo 壳会对 PNG / JPEG / WebP、MP3 / MP4-M4A / WAV / OGG / WebM 做 base64 bytes 头部识别,导入时要求系统 picker 的 MIME 与 bytes 匹配,导出时要求 H5 payload 的 mimeTypebase64Data 解码后的 bytes 匹配;不匹配统一返回 invalid_request,不会写入缓存文件、调起系统分享或把内容回传给 H5。移动图片导出还会按 MIME 补齐 .png / .jpg / .webp 文件名扩展,避免系统分享 / 保存面板拿到无扩展或错扩展的缓存图片。

2026-06-18 追加:移动壳音频导入的 DocumentPicker type 参数包含 audio/*,用于兼容 iOS / Android 文件提供方只按通用音频类型暴露候选文件的情况;这不放宽 HostBridge 返回契约。系统返回 application/octet-stream、空 MIME 或其它宽泛 MIME 时,移动壳只会按 .mp3 / .m4a / .mp4 / .wav / .ogg / .webm 扩展名归一到允许 MIME,并继续执行 base64 bytes 匹配,扩展名或 bytes 任一不匹配都返回 invalid_request

2026-06-18 追加:H5 个人中心的邀请码填写和兑换码弹窗开始消费 clipboard.readText。Expo 壳仍只通过 expo-clipboard 返回纯文本,H5 只把文本填入现有输入框,不自动提交,也不把剪贴板内容交给宿主侧业务处理;普通浏览器、小程序和未声明该能力的裁剪壳不显示粘贴动作。

2026-06-18 追加:H5 的草稿生成完成 / 失败收口开始消费 notification.showLocal。Expo 壳仍只发送即时本地通知,不注册远程推送 token、不做定时提醒;H5 按草稿来源对完成和失败通知去重,同一草稿重新进入生成中后才允许再次通知,通知失败不阻断弹窗、作品架和后端状态回读。根级原生壳门禁必须覆盖平台壳同步层通过真实 HostBridge transport 发出该 method,避免只保留模型文案测试。

2026-06-18 追加:移动壳 Android 包配置显式声明 POST_NOTIFICATIONS 承接 notification.showLocal,并阻断 RECEIVE_BOOT_COMPLETEDSCHEDULE_EXACT_ALARMUSE_EXACT_ALARM,只保留即时本地通知所需的普通通知权限和前台展示 handler,但不允许重启后恢复通知、精确定时提醒、远程推送 token 或通知响应跳转流程进入当前壳。配置检查会同时扫描 Expo public config 和移动壳通知源码,拒绝接入 getExpoPushTokenAsync、设备 push token、push token listener、通知响应 listener,以及 timeIntervaldatecalendardailyweeklymonthlyyearlysecondsrepeats 等定时 / 周期触发;Notifications.scheduleNotificationAsync 只能保留 iOS / 默认 trigger: null 和 Android 共享契约固定 channelId 的即时通知结构。

2026-06-18 追加:H5 的作品架未读草稿生成完成更新开始消费 app.setBadgeCount。Expo 移动壳仍只在 iOS 声明该能力,Android 不声明、不伪造成功;H5 只同步可见作品架内未读完成草稿数量,同一草稿多恢复 ID 只计 1,宿主不支持或设置失败不影响 H5 红点与作品架状态。根级原生壳门禁必须覆盖平台壳同步层通过真实 HostBridge transport 发出该 method,避免未读计数模型和宿主消费链路脱节。

2026-06-20 追加:移动壳本地通知和 iOS 角标的原生异常必须可观测。Expo Notifications 权限读取 / 权限请求、本地通知 Android channel 设置 / 通知调度、iOS 角标权限读取 / 请求、setBadgeCountAsync reject 或返回 false 时,移动壳必须分别记录 mobile notification failed for ...mobile app badge failed for ... 日志;HostBridge 回包仍只暴露稳定 notification permission unavailablenotification delivery unavailableapp badge permission unavailableapp badge update unavailable 语义。该约束不新增远程推送 token、后台通知、定时提醒或 Android 角标能力。

2026-06-18 追加:H5 的平台外部生成队列概览开始消费 network.status / network.statusChanged。宿主未声明网络能力时继续按原逻辑轮询;宿主明确离线或不可达时暂停概览请求,恢复在线后重新刷新,不改变生成任务、作品架或后端回读事实。

2026-06-20 追加:移动壳剪贴板、触觉和网络查询的原生异常必须可观测。Expo Clipboard 写入 / 读取、Expo Haptics 触觉派发、Expo Network 状态查询失败时,移动壳必须分别记录 mobile clipboard failed for ...mobile haptics failed for ...mobile network failed for ... 日志;HostBridge 回包仍只暴露稳定 clipboard write unavailableclipboard read unavailablehaptics impact unavailablenetwork status unavailable 语义。该约束不新增后台网络探测、任意系统能力或 H5 业务兜底路径。

2026-06-20 追加:移动壳 HostBridge 外链打开异常必须可观测。Expo app.openExternalUrl 捕获 Linking.canOpenURL / openURL 原生异常时记录 mobile HostBridge navigation failed for external.openHostBridge 回包仍只暴露稳定 external URL cannot be opened 语义,不透传系统异常细节。

2026-06-18 追加:移动壳 haptics.impact 只接受 lightmediumheavy 三档 impact style,缺省为 light;未知强度返回 invalid_request,不会静默降级成真实设备触觉反馈。桌面壳不声明该能力,H5 继续按 HostBridge fallback 处理。

2026-06-19 追加:移动壳 haptics.impact 的 HostBridge payload 解析、共享 style 归一、Expo style 映射和 Haptics.impactAsync(...) 真实调用统一收口在 apps/mobile-shell/src/host-bridge/haptics.tsdispatch.ts 只按 method 委托 runMobileHostBridgeHapticsImpact(request.payload)

2026-06-20 追加:桌面壳剪贴板和本地通知插件异常必须可观测。Tauri clipboard-manager 读写失败、notification permission state / request 失败、系统通知 show 失败时,桌面壳必须分别记录 desktop clipboard failed for ...desktop notification failed for ... 日志;HostBridge 回包仍只暴露稳定 clipboard write unavailableclipboard read unavailablenotification permission unavailablenotification delivery unavailable 语义。该约束不新增遥测 SDK、后台通知、自动更新或 H5 直连 Tauri JS 插件。2026-06-21 调整:桌面剪贴板和本地通知失败日志只记录 desktop clipboard failed for <stage> / desktop notification failed for <stage> 固定标签,不把 clipboard-manager、notification 插件错误或系统细节写入 stderr。

2026-06-20 追加:桌面壳文件导入导出的系统异常必须可观测。Tauri dialog 路径转换失败、后台文件读写任务失败或任务 join 失败时,桌面壳必须分别记录 desktop file export failed for ...desktop file import failed for ... 日志;HostBridge 回包仍只暴露稳定 file export unavailablefile import unavailable 语义,不透传本地路径、系统错误或线程细节。用户取消系统文件对话框仍返回 cancelled,不记录为异常。2026-06-21 调整:该日志只记录 desktop file export failed for <stage> / desktop file import failed for <stage> 固定标签,不把本地路径转换、文件读写或后台任务 join 错误写入 stderr。

2026-06-20 追加:桌面壳窗口状态小能力的系统异常必须可观测。Tauri 主窗口缺失、主题读取失败、角标设置失败或窗口标题设置失败时,桌面壳必须分别记录 desktop appearance failed for ...desktop app badge failed for ...desktop window title failed for ... 日志;HostBridge 回包仍只暴露稳定 appearance unavailablebadge unavailablewindow title unavailable 语义,不透传系统错误、窗口内部信息或平台细节。2026-06-21 调整:桌面外观、角标和窗口标题失败日志只记录 desktop appearance failed for <stage> / desktop app badge failed for <stage> / desktop window title failed for <stage> 固定标签,不把 Tauri theme() / set_badge_count / set_title 错误详情或主窗口缺失细节写入 stderr。

2026-06-20 追加:桌面壳导航系统异常必须可观测。Tauri 外链打开失败、同源 H5 route 导航失败、WebView reload 失败或主窗口缺失时,桌面壳必须记录 desktop navigation failed for ... 日志;HostBridge 回包仍只暴露稳定 external URL cannot be openednative page unavailablewebview reload unavailable 语义,不透传系统错误、窗口内部信息或平台细节。2026-06-21 调整:该日志只记录 desktop navigation failed for <stage> 固定标签,不把 opener、window.navigate、WebView reload 错误或主窗口缺失说明写入 stderr。

2026-06-20 追加:桌面壳网络状态查询系统异常必须可观测。Tauri 桌面壳后台解析网络状态任务失败时必须记录 desktop network failed for status.resolve 日志;HostBridge 回包仍只暴露稳定 network status unavailable 语义,不透传 resolver、线程或系统错误细节。2026-06-21 调整:该日志只记录固定标签,不把后台任务 join 错误或 resolver 异常写入 stderr。

2026-06-21 追加:桌面壳主站可达性探测失败诊断只允许记录 desktop network reachability probe failed 固定标签,不把探测 host / port、DNS 解析错误或 TCP 连接错误写入可分发桌面壳 stderr;network.status 回包继续只表达在线 / 离线稳定状态。

2026-06-20 追加:桌面壳分享缓存内部异常必须可观测。Tauri 桌面壳读写 share.setTarget 缓存失败时必须记录 desktop share failed for ... 日志;HostBridge 回包仍只暴露稳定 share unavailable 语义,不透传锁状态、内部缓存状态或 Rust 同步原语细节。2026-06-21 调整:该日志只记录 desktop share failed for target.lock / desktop share failed for target.store 固定标签,不把锁异常说明写入 stderr。

2026-06-18 追加:H5 账号状态刷新开始消费 app.reloadWebView。用户登录成功、退出登录、其它身份边界变化或登录状态异常页点击重新尝试时,AuthGate 会优先请求 Expo 壳刷新当前 WebView;宿主未声明或刷新失败时再回退浏览器刷新,避免在移动壳内绕过受控容器刷新入口。

2026-06-18 追加:移动壳 WebView 内容 / 渲染进程终止时复用同一受控刷新路径。iOS onContentProcessDidTerminate 和 Android onRenderProcessGone 只调用当前 react-native-webviewreload(),不改写 H5 URL、不注入额外脚本、不新增宿主恢复页面,避免系统回收 WebView 进程后留下空白容器。

2026-06-18 追加:移动壳 WebView 首载或同源主文档 HTTP 加载失败时,由 Expo 壳层展示原生失败兜底层和重试动作;重试只调用当前 WebView 的 reload(),不会改写 URL、绕过同源校验或向 H5 注入错误事件。失败归一逻辑只接受同源 H5 页面,about:blank、外域、危险协议、favicon 等非主页面失败不进入兜底,避免把导航拦截策略误展示为页面加载失败。

2026-06-21 追加:移动壳加载失败兜底层不得展示或持有完整 H5 运行态 URL、query、hash 或系统原生错误描述。normalizeMobileShellLoadFailure(...) 可以使用完整 URL 判断是否为当前同源主文档,但返回给 UI 的 url 只保留 origin + pathnamedetail 只使用稳定文案,例如“服务器暂时没有返回可用页面”“当前页面没有加载成功”或“当前页面连续恢复失败”。移动壳配置检查反查 query / hash 脱敏、系统 description 不直出和稳定文案测试,避免加载失败面板泄露 sessionIddraftIdworkId、登录恢复上下文或 WebView 原生错误明细。

2026-06-18 追加:移动壳 WebView 每次同源主站页面成功加载后都会补发当前 app.lifecyclenetwork.statusChanged 状态,覆盖首载、app.reloadWebView、进程恢复 reload 和 H5 自身刷新后的新 JS 上下文。补发仍只使用 HostBridge event,不新增 capability,不向 about:blank、外域或错误页注入宿主状态。

2026-06-18 追加:移动壳根布局接入 react-native-safe-area-context,用 SafeAreaProvider 和四边 SafeAreaView 承接 iOS 刘海、底部 Home Indicator、Android 状态栏和横屏边缘安全区;WebView 仍加载同一 H5 主站,不改变 H5 路由、玩法 runtime 或 HostBridge capability。该能力属于宿主壳布局保护,不在 H5 内补额外占位 UI。移动壳 safeArea.test.ts 必须证明 MOBILE_SHELL_SAFE_AREA_EDGES 固定覆盖 top / right / bottom / left 四边,配置检查会反查该测试,避免后续只保护竖屏常见边缘。

2026-06-18 追加:移动壳不锁定竖屏,Expo orientation 固定为 default,由 iOS / Android 设备方向和 H5 响应式布局共同承接竖屏、横屏与后续横屏玩法;壳层只负责安全区、WebView 容器和 HostBridge,不为某一类玩法硬编码方向。npm run mobile-shell:typechecknpm run mobile-shell:config 会拒绝重新锁回 portrait / landscape。

2026-06-18 追加:移动壳 Android 软键盘布局固定为 softwareKeyboardLayoutMode=resize,让系统键盘打开时真实调整 WebView 可视高度,由 H5 现有 viewport / 输入法聚焦适配承接创作表单和聊天输入;壳层不新增遮挡补偿面板、不伪造键盘状态。移动壳配置检查和 Expo public config smoke 会拒绝该字段缺失或漂移。

2026-06-18 追加:移动壳 WebView 媒体策略显式化。Expo 壳允许 H5 在 WebView 内内联播放媒体并允许用户打开全屏视频,但仍保留 mediaPlaybackRequiresUserAction,不放开无手势自动播放;H5 游戏音频继续由用户音乐开关、运行态状态和宿主生命周期共同控制。配置检查会拒绝这些 WebView 媒体策略漂移。

2026-06-18 追加:移动壳启动 H5 URL 增加宿主侧归一。EXPO_PUBLIC_GENARRATIVE_WEB_URL 和 deep link 基准地址只接受生产主站 https://www.genarrative.world,以及本机开发联调的 http://127.0.0.1http://localhosthttp://[::1] 入口;空值、相对路径、外域、file:javascript: 等非法配置统一回退到默认 H5 地址,再附加 native_app 宿主上下文。deep link 仍只允许映射到归一后基准 origin 的 H5 路径,避免外域页面被装进带完整 HostBridge 的 WebView。

2026-06-18 追加:移动壳 navigation.openNativePage 与 deep link 共享同一宿主上下文补写口径。受控主动导航先解析为当前允许 origin 的同源 H5 URL,再通过 buildMobileShellUrl(...) 重新附加 clientRuntime=native_apphostShell=expo_mobile、当前平台、版本和真实 capability 清单;deep link 同样在同源路径归一后复用该构造函数。apps/mobile-shell/scripts/check-config.mjs 会拒绝主动导航或 deep link 绕过宿主上下文构造入口,避免新页面按普通浏览器运行态启动。

2026-06-18 追加:移动壳 HostBridge 协议名和协议版本统一从 packages/shared/src/contracts/hostBridge.tsHOST_BRIDGE_PROTOCOL / HOST_BRIDGE_VERSION 读取。Expo 入口 query、WebView 事件注入、host.getRuntime 回包和 Expo public config smoke 都必须反查共享常量;移动壳配置检查会拒绝重新写死 GenarrativeHostBridge 或字面量版本。

2026-06-19 追加:公开 H5 主站 origin / 默认 URL 统一以 packages/shared/src/contracts/hostBridge.tsHOST_BRIDGE_PUBLIC_WEB_ORIGIN / HOST_BRIDGE_PUBLIC_WEB_URL 为源。移动壳 DEFAULT_MOBILE_SHELL_WEB_URLALLOWED_PRODUCTION_WEB_ORIGIN 只是壳层语义别名,分享 URL 归一、WebView 下载协议判定、启动 URL 回退、iOS associated domain 和 Android App Link 验收都不得重新写死 https://www.genarrative.world;桌面 Rust 侧 WEB_APP_ORIGIN 保留为运行时镜像常量,但 apps/desktop-shell/scripts/check-config.mjs 必须反查共享 origin,桌面网络探测也必须从该 origin 解析 host / port,不得在 network.rs 里重新写死主站域名。两端配置检查会拒绝本地复刻或漂移同值 origin。

2026-06-18 追加:桌面壳 HostBridge 协议名和协议版本也必须反查同一共享契约。Tauri Rust 侧仍保留 host_bridge/protocol.rs 常量作为运行时代码入口,但 apps/desktop-shell/scripts/check-config.mjs 会把 Rust HOST_BRIDGE_PROTOCOL / HOST_BRIDGE_VERSIONpackages/shared/src/contracts/hostBridge.ts 对齐,避免桌面壳事件注入、runtime 回包和 H5 transport 分叉。

2026-06-18 追加:移动壳默认 H5 地址固定为 https://www.genarrative.world/。开发联调如需加载本机 Vite,必须显式设置 EXPO_PUBLIC_GENARRATIVE_WEB_URL=http://127.0.0.1:3000/http://localhost:3000/http://[::1]:3000/;生产包不得在未配置环境变量时默认加载设备本机 localhost,也不得通过环境变量把第三方外域 H5 放入带完整 HostBridge 的 WebView。

2026-06-18 追加:移动壳系统深链声明固定为生产主站唯一入口。iOS associatedDomains 只能包含 applinks:www.genarrative.worldAndroid intentFilters 只能存在一个 VIEW / autoVerify=true 的 App Link 过滤器,category 只能是 BROWSABLEDEFAULTdata 只能绑定 https://www.genarrative.world,不得额外声明外域、明文协议、pathPattern 或其它可接管范围。实际 deep link 解析仍由壳层把同源路径归一后附加 HostBridge 上下文,非法来源回退默认首页;配置检查和 Expo public config smoke 会从共享 HOST_BRIDGE_PUBLIC_WEB_ORIGIN 解析 host,再反查上述平台 manifest 字段。

2026-06-18 追加:移动壳安装包身份固定为 world.genarrative.mobile。Expo app.json 中的 ios.bundleIdentifierandroid.package 使用同一包标识,应用版本为 0.1.0iOS buildNumber 从字符串 "1" 起步,Android versionCode 从整数 1 起步;后续每次生成可分发安装包时只递增构建号 / versionCode,产品版本号按发布节奏单独调整。apps/mobile-shell/scripts/check-config.mjs 会校验这些字段与 package.json 版本一致,避免 iOS、Android 和 H5 HostBridge hostVersion 发生静默漂移;npm run mobile-shell:config 会调用真实 Expo CLI 解析 public managed config,确认最终 Expo 配置仍保留同一包身份、深链、安全字段、插件权限和 HostBridge 版本。当前仍不写入假商店上架信息、假更新端点或占位渠道 SDK 配置。

2026-06-19 追加:移动壳 H5 入口 query 和 host.getRuntime 回包统一读取 MOBILE_SHELL_HOST_VERSION,该值由移动壳 app.json 的 Expo version 配置解析,异常配置只回退到与 app.json / package.json 一致的受检 fallback。配置检查会拒绝在 apps/mobile-shell/src/shell/ShellApp.tsxapps/mobile-shell/src/host-bridge/appearance.tsapps/mobile-shell/src/host-bridge/badge.tsapps/mobile-shell/src/host-bridge/bridge.tsruntime.ts 内重新散落硬编码版本,避免升级移动安装包时 H5 首屏上下文和宿主 runtime 回读版本不一致。该收口不引入 expo-constants、OTA 更新、渠道分发或应用安装信息业务。

2026-06-18 追加,2026-08-21 按 npm workspaces 更新:移动壳默认显式关闭 Expo OTA 更新,直到存在真实发布通道、更新端点、签名 / 回滚策略和团队发布流程后再接入。app.json 只允许 updates.enabled=false,不得配置 runtimeVersion、release channel、EAS channel、expo-updates 插件或移动端 crash / analytics / CodePush 依赖;apps/mobile-shell/scripts/check-config.mjs 和 Expo public config smoke 会共同拒绝这些发布通道能力被提前打开,移动壳生产入口、HostBridge 和 URL/runtime 配置也不得提前初始化 Sentry、Firebase Analytics、PostHog、Amplitude、Segment、CodePush 或 Expo Updates。根 H5 与 Mobile workspace 的 manifest 都不得直接声明这些 SDK;统一根 lock 可能因其它合法传递依赖出现同名包,禁用能力归属应检查 workspace manifest 和 Mobile 依赖闭包,不能把聚合 lock 的全局存在性直接当成 Mobile 启用。expo-application 可能由 Expo 自身传递解析,但项目不得把它作为 direct dependency 主动用于渠道逻辑。

2026-06-18 追加:移动壳可分发身份、外观和默认权限进入配置门禁。Expo name 固定为 Genarrativeslug 固定为 genarrative-mobile-shelluserInterfaceStyle 固定为 automaticassetBundlePatterns 固定为 ["**/*"]extra.genarrativeHostBridgeVersion 固定为 1;源 app.json 的 Android permissions 只允许显式声明 android.permission.RECORD_AUDIO,用于同源 H5 实时声音玩法,最终 Expo public config 只允许 android.permission.CAMERAandroid.permission.RECORD_AUDIO 两类权限,其中相机来自扫码 / 拍摄能力,麦克风来自同源 H5 实时玩法。所有其它当前不需要的高风险权限只能通过 blockedPermissions 阻断。后续新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback 方案,再补配置与检查。apps/mobile-shell/scripts/check-config.mjs 会检查源 app.jsonapps/mobile-shell/scripts/check-expo-config.mjs 会检查 Expo CLI 最终解析出的 public config,避免 config plugin 或 Expo 解析阶段引入身份、资源或权限漂移。

2026-06-18 追加:移动壳命令入口进入配置门禁。apps/mobile-shell/package.jsondevandroidiostestconfig:smokeexport:smoketypecheck 以及根 package.jsonmobile-shell:* 入口必须保持指向真实 Expo / RN / Vitest / Expo config / Metro export / 配置检查流程,不能替换成只跑静态脚本或绕过生产 bundler 的快捷命令。

2026-06-18 追加,2026-08-21 按 npm workspaces 更新:移动壳关键依赖版本进入配置门禁。Expo SDK 56、React 19、React Native 0.86、react-native-webview 13.16、react-native-safe-area-context、Expo Camera / Clipboard / DocumentPicker / FileSystem / Haptics / ImagePicker / Linking / Network / Notifications / Sharing / StatusBar、TypeScript 和 Vitest 由 apps/mobile-shell/package.json 拥有,根 H5 不再重复声明移动端专属依赖;统一根 package-lock.json 中 Mobile workspace entry 与实际解析版本必须由配置检查锁定。升级这些依赖必须同步审查 WebView 安全开关、Expo managed config、production export、HostBridge 能力实现和 H5 fallback,不能只更新依赖声明或锁文件。

Phase 3Tauri 桌面壳 MVP

  • 新增 apps/desktop-shell/
  • 配置 Tauri dev / release web asset 加载。
  • Rust 暴露 host_bridge_request command。
  • capabilities 只开放该 command 和必要窗口能力。
  • 实现 runtime、openExternalUrl、clipboard、share fallback、窗口标题同步。
  • 验证 macOS / Windows / Linux 至少一条本地 smoke。

当前状态:已新增 apps/desktop-shell/,Tauri dev 直接加载本地主站 Vite,release 加载共享公开主站 https://www.genarrative.world/。Rust 侧只把 host_bridge_request command 授给主窗口,appearance.getColorScheme 由 Rust 内部读取主窗口 theme() 并返回 light / dark / unknown,不设置或覆盖系统主题;app.lifecycle 由主窗口 focus / blur、窗口 resize 后的状态读取、托盘隐藏 / 恢复和页面加载重放注入统一状态,不开放 Tauri event 插件给前端。桌面壳只向 H5 派发共享契约里的 active / inactive / background,隐藏到托盘和系统最小化都归一为 backgroundhiddenminimizedfocusedblurred 只写入 nativeState 便于排障;H5 通过 useHostLifecycleActive() 统一归一窗口焦点和后台状态,WebAudio 背景音乐和拼图、抓大鹅等固定玩法 <audio> 背景音乐都会在窗口失焦、隐藏或最小化时暂停、恢复焦点且原播放条件仍满足时恢复;network.status 由 Rust 从 WEB_APP_ORIGIN 解析主站 host / port 并做短超时 TCP 可达性查询;桌面壳暂不声明 network.statusChanged,避免把 WebView online / offline 事件当作桌面 Rust 网络事实,不开放任意网络探测给 H5;app.openExternalUrl 由 Rust 内部通过 opener 插件执行且只允许 http:https:mailto:tel: 外链协议,ICP备案号和资产调试原图等 H5 外链入口在 native_app 中优先通过该能力离开主窗口并交给系统浏览器;navigation.openNativePage 只接受 https://www.genarrative.world 同源 H5 route 并在主窗口内受控跳转,跳转前与桌面 deep link 共用同一宿主上下文补写方法,重新附加 clientRuntime=native_apphostShell=tauri_desktop、当前平台、版本和真实 capability 清单,确保新页面继续按桌面壳运行态启动;navigation.canGoBack 由桌面壳注入固定脚本追踪当前 H5 文档内的 pushState / replaceState / popstate 路由栈并通过 HostBridge event 回放,不读取或声明跨文档 native back-forward listapp.reloadWebView 只调用 Tauri 主 WebViewWindow 的 reload() 刷新当前 H5,不开放 JS guest API、任意命令或任意 URL 导航,clipboard.writeText 由 Rust 内部通过 clipboard-manager 插件写入系统剪贴板并由 H5 复制服务优先调用,clipboard.readText 由 Rust 内部通过同一插件读取纯文本剪贴板并按 100000 字符上限返回,不开放剪贴板插件 JS guest API、图片读取、HTML 读取或监听事件,app.setTitle 通过 Tauri 主窗口 API 同步窗口标题并拒绝空标题 / 控制字符,app.setBadgeCount 通过主窗口 set_badge_count 设置任务栏角标,数量只接受 0 到共享契约 HOST_BRIDGE_BADGE_COUNT_MAX 之间的整数且 0 表示清除;H5 主站会按当前平台阶段先更新 document.title,再通过 app.setTitle 把同一标题同步给 Tauri 窗口,Expo 移动壳不声明该能力时静默忽略;不把 opener、clipboard、dialog 或 notification 插件命令直接暴露给前端。当前真实能力为 host.getRuntimeappearance.getColorSchemehost.eventsapp.lifecyclenetwork.statusshare.setTargetshare.opennavigation.openNativePagenavigation.canGoBackapp.reloadWebViewapp.openExternalUrlapp.setTitleapp.setBadgeCountclipboard.writeTextclipboard.readTextfile.exportTextfile.importTextfile.importDocumentfile.exportImagefile.importImagefile.importAudiofile.exportAudiofile.imageDroppednotification.showLocal;H5 会在主 App 启动时通过真实 host.getRuntime 回读并缓存这些能力,即使入口 URL 缺少 hostCapabilities,也只按宿主真实回包开启能力入口;其中 host.events 只表示桌面壳会通过 HostBridge message 派发生命周期、H5 返回栈和拖拽图片等事件,不新增 request method,也不开放 Tauri event 插件;share.open 会把直接传入的分享 payload 或 share.setTarget 缓存的作品目标整理成非空分享文本并写入系统剪贴板,分享 URL 只能归一到 https://www.genarrative.world 公开主站同源 URL,外域、协议相对 URL 和危险协议会拒绝请求,显式非法 payload 不得回退到之前缓存的 share.setTarget 目标,成功返回 copied_to_clipboard,发布分享弹窗在桌面壳中通过该能力展示“复制分享文案”,成功后显示“已复制”;file.exportText 通过系统保存对话框让用户选择本地路径,清洗文件名、限制单次文本导出不超过 5 MiB,写入成功后只返回文件名和字节数,不把本机绝对路径暴露给 H5,用户取消返回 cancelledfile.importDocument 通过系统选择框读取用户选择的文本类文档或 DOCX,单次不超过 5 MiB,成功只返回清洗后的文件名、MIME、base64 内容和字节数,不把本机绝对路径暴露给 H5,也不开放通用文件系统;file.exportImage 同样通过系统保存对话框写入 H5 生成的图片字节,只接受 image/png / image/jpeg / image/webp base64 数据,单次不超过 5 MiB,分享卡下载会优先走该能力,用户取消返回 cancelledfile.importImage 通过系统选择框读取用户选择的图片,file.imageDropped 通过主窗口拖拽事件读取用户拖入的图片,二者都只接受 image/png / image/jpeg / image/webp、单次不超过 10 MiB,成功只返回文件名、MIME、base64 内容和字节数,不把本机绝对路径暴露给 H5,也不开放通用文件系统;拖入目录、文本、损坏图片或没有任何有效图片时,桌面壳不派发 file.imageDropped payload,扩展名合法但 payload 读取失败的候选会记录 file.imageDropped.payload 固定宿主事件失败标签并继续尝试后续有效图片,不把本机读取错误或 payload 细节写入 stderrfile.importAudio 通过系统选择框读取用户选择的音频,只接受 audio/mpegaudio/mp4audio/wavaudio/oggaudio/webm 或对应扩展名,单次不超过 20 MiB,成功只返回清洗后的文件名、MIME、base64 内容和字节数,不把本机绝对路径暴露给 H5,也不开放通用文件系统;file.exportAudio 通过系统保存对话框写入 H5 当前已持有的音频字节,只接受允许 MIME 的 base64 数据,单次不超过 20 MiB,成功只返回文件名和字节数,不让宿主代读任意本地文件;通用音频输入面板 CreativeAudioInputPanel 在桌面壳声明导入能力时优先打开系统音频选择框,在声明导出能力且当前资产为本地 Blob 时展示导出入口,并继续复用 readFileAsAsset(file, 'uploaded') 处理链路;notification.showLocal 由 Rust 内部通过 tauri-plugin-notification 发送即时系统通知,只接受清洗后的标题和正文,发送前先检查系统通知权限,处于 prompt 状态时只在 Rust 侧请求一次权限,最终未授权时返回失败,不开放插件 JS guest API、远程推送、定时提醒或通知 token;通用创作图片输入面板 CreativeImageInputPanel 在桌面壳声明 file.importImage 时会优先打开系统图片选择框,在同时声明 file.imageDropped 时会按宿主拖入坐标把图片交给命中的主图槽位,并把结果转换为现有 File 上传回调;反馈页上传凭证、个人资料头像上传、方洞结果页图片槽位上传和汪汪声浪结果页三图槽位上传同样优先消费 file.importImage,但继续交给 H5 原有预览、裁剪、槽位编辑、大小限制、上传和提交 / 保存链路。普通浏览器、小程序和未声明能力的壳继续保留原文件输入路径。原生系统分享面板、登录和支付未接入真实插件 / 渠道前不声明支持,不返回临时成功。

2026-06-19 追加:桌面壳当前真实能力完整清单为 host.getRuntimeappearance.getColorSchemehost.eventsapp.lifecycleshare.openshare.setTargetnavigation.openNativePagenavigation.canGoBackapp.reloadWebViewapp.openExternalUrlapp.setTitleapp.setBadgeCountnetwork.statusclipboard.writeTextclipboard.readTextfile.exportTextfile.importTextfile.importDocumentfile.exportImagefile.importImagefile.importAudiofile.exportAudiofile.imageDroppednotification.showLocalnpm run check:native-shells 会用共享桌面 capability profile 反查本段和 Rust 运行时镜像;桌面壳 Rust 单测同时要求该清单无重复、包含全部真实能力,并排除 auth.requestLoginpayment.requestfile.captureImagescanner.scanQrCodehaptics.impact 等未接入能力,避免实现、入口 URL、单测和方案文档再次漂移。

2026-06-18 追加:桌面壳主窗口 capability 最小化为 allow-host-bridge-request。Tauri core:default 会同时包含 event、window、webview、menu、tray 等默认命令集合,其中 webview 默认集包含 DevTools 内部切换命令;当前壳不再授予这些 core / plugin JS 权限。apps/desktop-shell/scripts/check-config.mjs 会拒绝 core:defaultcore:*:default、任意 core 子权限和 dialog / fs / notification / opener / clipboard / deep-link / window-state 等插件权限进入主窗口 capability。

2026-06-18 追加:桌面壳的网络状态同样只通过 HostBridge 暴露给 H5。H5 外部生成队列概览在桌面壳明确离线或不可达时暂停轮询,窗口恢复在线后重新刷新;Tauri 仍不开放任意网络探测、shell 或文件系统能力给主站。

2026-06-18 追加:桌面壳声明并实现 file.importText,通过系统文件选择框读取用户选择的文本文件,只接受 text/plaintext/markdowntext/csvapplication/json 对应扩展名,单次不超过 5 MiB;成功只返回清洗后的文件名、MIME、UTF-8 文本内容和字节数,不暴露本机绝对路径,也不开放通用文件系统。H5 创作 Agent 工作台复用同一 HostBridge 文本导入入口和后端解析接口;普通浏览器、小程序和未声明该能力的裁剪壳继续保留原 <input type="file"> 路径。

2026-06-19 追加:移动壳和桌面壳共同声明并实现 file.importDocument,作为创作 Agent 工作台的优先文档导入路径。Expo 通过 DocumentPicker、Tauri 通过系统文件选择框读取用户选择的文本类文档或 DOCX,单次不超过 5 MiB,读取 base64 前必须先完成可信 byte count 门禁;成功只返回清洗后的文件名、MIME、base64 内容和字节数,不暴露设备 URI、本机绝对路径或通用文件系统。H5 把返回内容转换成浏览器 File 后继续调用 /api/runtime/creation-agent/document-inputs/parse,不在前端解析 DOCX,也不绕过后端解析、大小校验或错误口径;旧壳只声明 file.importText 时才回退到文本导入。

2026-06-19 追加:创作 Agent 工作台的参考图上传接入移动壳和桌面壳共同声明的 file.importImage。H5 在 native_app 且宿主声明该能力时优先打开 Expo 相册选择器或 Tauri 系统图片选择框,把返回的 base64 图片副本转换成浏览器 File 后继续交给既有 onReferenceImageChange 校验、预览和上传链路;用户取消时停留在原生壳文件流程内,不再连带弹出浏览器文件输入。普通浏览器、小程序和未声明 file.importImage 的裁剪壳继续使用原隐藏文件输入。

2026-06-19 追加:桌面壳同样承接 H5 创作 Agent 会话 Markdown 导出。H5 侧只在宿主声明 file.exportText 且当前会话存在可导出内容时显示导出按钮,导出前完成 5 MiB UTF-8 byte 校验;Tauri 只负责系统保存对话框、文件名清洗、写入文本和返回文件名 / 字节数,不把 dialog、fs 或本机路径暴露给 H5。

2026-06-19 追加:视觉小说结果页素材选择弹窗在桌面壳声明 file.importImage / file.importAudio 时同样优先打开 Tauri 受控系统选择框,把返回的图片或音频副本转换成浏览器 File 后继续调用 uploadVisualNovelAsset 和当前封面、角色、场景素材写回链路;Tauri 仍不暴露本机绝对路径、通用文件系统或 dialog JS guest API。普通浏览器、小程序和未声明能力的裁剪壳继续使用原文件输入。

2026-06-18 追加:桌面壳图片和音频导入 / 导出不再只信扩展名或 H5 声明的 MIME。Tauri Rust 侧会对 PNG / JPEG / WebP、MP3 / MP4-M4A / WAV / OGG / WebM 做 bytes 头部识别,导入时要求文件扩展名对应的允许 MIME 与真实 bytes 匹配,导出时要求 H5 payload 的 mimeTypebase64Data 解码后的 bytes 匹配;不匹配统一返回 invalid_request,继续不暴露本机绝对路径或通用文件系统能力。

2026-06-18 追加:H5 个人中心的邀请码填写和兑换码弹窗开始消费 clipboard.readText。Tauri 壳仍只通过 Rust 侧 clipboard-manager 返回纯文本,不开放插件 JS guest API;H5 只把文本填入现有输入框,不自动提交,也不把剪贴板内容交给宿主侧业务处理。

2026-06-18 追加:H5 的草稿生成完成 / 失败收口开始消费 notification.showLocal。Tauri 壳仍只通过 Rust 侧 notification 插件发送即时系统通知,发送前先检查 permission_state(),处于 prompt 状态时调用 request_permission() 后再复判,最终未授权时返回 host_error;桌面 capability 仍只授权 allow-host-bridge-request,不开放 notification 插件 JS guest API、远程推送、定时提醒或通知 token;H5 按草稿来源对完成和失败通知去重,同一草稿重新进入生成中后才允许再次通知,通知失败不阻断弹窗、作品架和后端状态回读。根级原生壳门禁必须覆盖平台壳同步层通过真实 HostBridge transport 发出该 method,避免只保留模型文案测试。

2026-06-18 追加:H5 的作品架未读草稿生成完成更新开始消费 app.setBadgeCount。Tauri 壳仍只通过主窗口受控设置任务栏角标,不开放任意窗口或系统托盘插件 API;H5 只同步可见作品架内未读完成草稿数量,同一草稿多恢复 ID 只计 1,宿主不支持或设置失败不影响 H5 红点与作品架状态。根级原生壳门禁必须覆盖平台壳同步层通过真实 HostBridge transport 发出该 method,避免未读计数模型和宿主消费链路脱节。

2026-06-18 追加:H5 账号状态刷新开始消费 app.reloadWebView。用户登录成功、退出登录、其它身份边界变化或登录状态异常页点击重新尝试时,AuthGate 会优先请求 Tauri 主 WebViewWindow 刷新;宿主未声明或刷新失败时再回退浏览器刷新,Tauri 仍只暴露 host_bridge_request 这一受控命令入口。

2026-06-18 追加:桌面壳 WebView 每次页面加载完成后都会回放当前 app.lifecycle,覆盖托盘刷新、app.reloadWebView 和 H5 自刷新后的新 JS 上下文。回放会读取主窗口当前 is_visible()is_minimized()is_focused(),隐藏或最小化状态下不得误报 active;生命周期事件仍只通过 HostBridge message 注入,不开放 Tauri event 插件或额外 command。2026-06-20 调整:桌面壳不再声明 network.statusChanged,直到网络变化事件能由 Rust 侧真实网络源统一驱动;H5 仍可按 network.status 主动查询桌面网络可达性。2026-06-21 调整:生命周期窗口状态读取失败和生命周期 / 返回栈事件注入失败只记录 desktop host event failed for app.lifecycle.<field> 或对应事件固定标签,不把 Tauri 错误详情写入可分发桌面壳 stderr。

2026-06-18 追加:桌面壳启用 Tauri 真实系统托盘,并复用品牌图标。托盘菜单只提供宿主壳级动作:显示主窗口、刷新主窗口和退出应用;左键点击托盘图标恢复并聚焦主窗口。该能力不进入 HostBridge capability 清单,不向 H5 暴露托盘 API、菜单 API、shell API 或任意窗口控制;如果当前桌面环境无法注册托盘,壳会继续启动主窗口。托盘注册成功时,用户点击主窗口关闭按钮会先通过 app.lifecycle 注入 background / hidden,再隐藏到托盘,必须通过托盘“退出”动作结束应用;托盘恢复、单实例唤醒和 deep link 唤醒都会重新显示、取消最小化、聚焦主窗口并回放当前生命周期状态。托盘注册失败时不拦截关闭,避免窗口消失后没有恢复入口;托盘显示和刷新主窗口动作失败必须记录桌面宿主事件日志,不能静默吞错。

2026-06-22 调整:桌面壳取消 Tauri 原生应用菜单和窗口菜单,不再注册 shell/menu.rs,也不向 H5 暴露菜单 API、shell API 或任意窗口控制;显示主窗口、刷新主窗口和退出应用继续由系统托盘、单实例唤醒和 deep link 唤醒路径承接,桌面目录门禁同步移除 shell/menu.rs

2026-06-18 追加:桌面壳启用 Tauri 单实例。用户重复启动桌面 App 时,新实例会退出并唤醒已有主窗口;该回调只执行显示、取消最小化和聚焦主窗口,不把第二实例的命令行参数或工作目录作为事件透传给 H5。Windows / Linux 上由单实例插件的 deep-link feature 把二次实例 URL 交给 deep-link 插件,仍由 shell/deep_link.rs 做受控归一;单实例唤醒主窗口失败必须记录桌面宿主事件日志,不能静默吞错。

2026-06-18 追加:桌面壳启用 Tauri 窗口状态持久化。shell/window_state.rs 只配置 tauri-plugin-window-state 保存主窗口大小、位置和最大化状态,并排除可见性、全屏和装饰状态;插件注册顺序固定为 single-instance 优先,其后才是窗口状态、deep-link 和其它系统能力插件。该能力不进入 HostBridge 清单,不向 H5 暴露任意窗口状态读写,也不改变托盘关闭隐藏、单实例唤醒和托盘恢复主窗口的既有语义。apps/desktop-shell/scripts/check-config.mjs 会反查 desktop_window_state_flags_keep_visible_state_out_of_persistence 单测,避免后续把可见性、全屏或装饰状态误纳入持久化。

2026-06-18 追加:桌面壳接入 Tauri deep-link 插件,但不开放插件 JS guest API,也不把 deep-link 命令加入 capability。桌面配置只注册 genarrative schemeRust 层只接受 genarrative://open/...genarrative://app/...genarrative://<path>https://www.genarrative.world/...,统一跳到同源 H5 路径并补写 clientRuntime=native_apphostShell=tauri_desktop、当前平台、版本与真实 hostCapabilities。外域、http:mailto:javascript:file: 等来源不进入主 WebView;冷启动当前 deep link 读取失败、归一成功后的 window.navigate(...) 与主窗口恢复 / 聚焦失败必须记录桌面宿主事件日志,不能静默吞错。2026-06-21 调整:deep-link scheme 注册失败和冷启动当前链接读取失败只记录 desktop host event failed for deep_link.register / desktop host event failed for deep_link.current 固定标签,不把插件错误详情写入可分发桌面壳 stderr。

2026-06-20 追加:移动壳 production bundle smoke 必须读取 Metro 导出的 iOS / Android JS bundle,并确认 bundle 内包含共享 HostBridge 契约中的生产 H5 URL、native_app 宿主上下文、expo_mobile 壳标识以及 hostCapabilities / hostVersion / bridgeVersion 等启动 query token,且不包含 http://localhosthttp://127.0.0.1http://[::1] 这类本机开发 H5 入口。开发态仍允许 EXPO_PUBLIC_GENARRATIVE_WEB_URL 指向本机 Vite,但可分发包验收不能让本机 URL 混入生产 bundle,也不能丢掉 H5 进入原生宿主运行态所需的上下文 token。

2026-06-18 追加:桌面壳安装包身份固定为 world.genarrative.desktop,产品名为 GenarrativeTauri、Node package 与 Cargo package 版本统一为 0.1.0。Release 主窗口只能加载共享公开主站 https://www.genarrative.world/,不得配置 frontendDist 打包根 H5 资产;dev URL 只能指向本机 Vite 调试入口;CSP 必须保持 script-src 'self',不得加入 unsafe-evaltauri:file: 这类扩大桌面攻击面的来源。当前不配置自动更新器,直到存在真实更新端点、签名密钥和发布流程再接入;apps/desktop-shell/scripts/check-config.mjs 会校验这些包身份、版本、CSP 和 updater 禁用约束。

2026-06-18 追加:桌面壳命令入口进入配置门禁。apps/desktop-shell/package.jsondevbuildtypecheck,根 package.jsondesktop-shell:* 入口,以及 Tauri beforeDevCommand / beforeBuildCommand 都必须保持在真实 Tauri dev / build、根 H5 dev:web 和桌面壳配置检查路径上;本地桌面调试不能改成加载外部 H5、跳过根 Vite 或绕过 Tauri release build smoke 的命令。桌面壳 dev 脚本必须显式设置 WEB_PORT=3000,与 Tauri devUrl=http://127.0.0.1:3000/ 保持一致,避免 Linux 多用户端口段把 npm run dev:web 自动映射到其它端口后桌面壳仍加载固定 3000。

2026-06-18 追加:桌面壳默认不接入崩溃上报、analytics、遥测日志或渠道分发 SDK。apps/desktop-shell/scripts/check-config.mjs 会拒绝 Sentry、Datadog、PostHog、Segment、Amplitude、Bugsnag、OpenTelemetry、Tauri log / updater 等 Node / Cargo 依赖、package-lock.json / Cargo.lock 解析包和 Rust 初始化片段;后续只有在真实采集端点、数据字段、用户授权、隐私披露、签名和发布流程确定后,才能按单项能力补充方案与实现。

2026-06-18 追加:桌面壳 release / dev 在 tauri.conf.json 中只保留基础入口 index.htmlhttp://127.0.0.1:3000/,由 Rust shell/url.rs 统一补写 clientRuntime=native_apphostShell=tauri_desktophostVersionbridgeVersion 和真实 hostCapabilitieshostVersion 必须与 Tauri tauri.conf.json、Node package 和 Cargo package 版本一致,host.getRuntime 回包继续使用 env!("CARGO_PKG_VERSION");配置检查会拒绝在 Tauri 配置里重新手写宿主上下文,避免桌面包升级或能力变化时 H5 首屏上下文与 runtime 回读不一致。

2026-06-20 追加:桌面壳主窗口启动配置单测必须覆盖 release index.html 入口补写后的完整宿主上下文,至少包含 clientRuntime=native_appclientType=native_apphostShell=tauri_desktophostPlatformhostVersionbridgeVersionhostCapabilitiesapps/desktop-shell/scripts/check-config.mjs 会反查这些断言存在,避免只保留基础入口检查而遗漏首屏 H5 进入桌面壳运行态所需的 query 字段。

2026-06-18 追加:桌面壳静态 Tauri 配置不再写入 hostPlatform 或其它宿主上下文 query。Rust setup 手动创建主窗口前会把基础入口归一为当前 macos / windows / linux 平台和完整宿主上下文,保证 H5 首屏 query 与 host.getRuntime 回读的平台一致;不通过第二实例参数、外部 deep link 或 H5 自报值覆盖该平台字段。

2026-06-18 追加:桌面壳主 WebView 增加顶层导航边界。打包资产 URL 和 https://www.genarrative.world 同源 H5 route 可以继续留在主窗口;外域 http: / https:mailto:tel: 导航和 window.open 请求只交给系统 opener 后阻止留壳;javascript:file: 等危险协议直接阻断。该规则不新增 HostBridge capability,也不开放 opener 插件 JS guest API,避免外域页面停留在带 host_bridge_request 权限的主 WebView 内。

2026-06-18 追加:桌面壳命令暴露面收紧为唯一受控入口。Tauri build.rs manifest、Rust generate_handler!、主窗口 capability 权限列表和本地自动生成权限目录都只能出现 host_bridge_request;如果本地构建残留了其它 command 的自动生成权限文件,apps/desktop-shell/scripts/check-config.mjs 会直接失败。后续接入新的桌面系统能力时仍先扩展 HostBridge method 和 Rust 内部分发,不新增可被 H5 直接 invoke 的 Tauri command,也不把插件 JS guest API 授权给主窗口。

2026-06-18 追加:桌面壳 capability 作用域进入配置门禁。Tauri 配置只能声明一个 label=main 的主窗口,src-tauri/capabilities/ 只能存在 main.json,该 capability 的 identifier 必须为 mainwindows 必须只包含 mainpermissions 必须只包含 allow-host-bridge-request。这保证 opener、clipboard、dialog、notification 等插件只由 Rust 内部通过受控 HostBridge 分发使用,不把 core 默认命令、插件 JS guest API 或额外窗口权限授给 H5 主站。

2026-06-18 追加,2026-08-21 按 npm workspaces 更新:桌面壳 JS guest 依赖进入门禁。apps/desktop-shell/package.json 与根 H5 package.json 不得声明 @tauri-apps/api 或任何 @tauri-apps/plugin-*,避免生产前端绕过 nativeAppHostBridge 直接调用 Tauri JS 客户端 API;统一根 package-lock.json 会合法聚合 AGC workspace 的 Tauri guest,不能按全局 lock 条目禁止。Tauri CLI 仍只作为构建工具留在 devDependencies,桌面系统能力继续由 Rust 侧 Cargo 插件和唯一 host_bridge_request command 承接。

2026-06-18 追加,2026-08-21 按 npm workspaces 更新:桌面壳关键依赖版本进入配置门禁。Tauri CLI 和 TypeScript 由 apps/desktop-shell/package.json 声明,统一根 package-lock.json 中 Desktop workspace entry 与实际解析版本必须一致;根 H5 只有自身确实使用同一构建工具时才单独声明,不再为 Desktop 镜像依赖。src-tauri/Cargo.tomltauri-buildtauribase64serdeserde_json 以及 clipboard、dialog、notification、opener、single-instance、window-state 插件版本和 Tauri tray-icon feature 都由 apps/desktop-shell/scripts/check-config.mjs 固定检查,src-tauri/Cargo.lock 中桌面壳 direct dependency 的实际解析版本也必须同步受检。升级这些依赖必须同步审查 capability、CSP、唯一 command、插件初始化、release build smoke 和本文档。

2026-06-18 追加:H5 到 Tauri 的 command 名进入共享契约。packages/shared/src/contracts/hostBridge.ts 导出 HOST_BRIDGE_TAURI_COMMAND='host_bridge_request'nativeAppHostBridge 只能通过该常量调用 Tauri 注入的 core.invoke;桌面壳配置检查会对齐共享常量、Tauri build manifest、Rust generate_handler! 和 H5 transport,禁止 H5 侧写死或调用其它 Tauri command。

2026-06-18 追加:H5 到 Tauri 的 core.invoke 也必须经过 nativeAppHostBridge 的超时封装,不能裸等 Rust command Promise。Tauri transport 与 React Native WebView transport 共享 timeoutMs 归一化和 timeout / host_bridge_timeout 错误语义;如果桌面宿主迟到返回,H5 侧已拒绝的请求结果不得被改写。apps/desktop-shell/scripts/check-config.mjs 会锁定 H5 Tauri transport 的超时封装,避免后续退回裸 invoke

2026-06-19 追加:原生壳请求超时边界进入共享契约。packages/shared/src/contracts/hostBridge.ts 导出 HOST_BRIDGE_DEFAULT_REQUEST_TIMEOUT_MS=8000HOST_BRIDGE_MAX_REQUEST_TIMEOUT_MS=60000H5 nativeAppHostBridge 的 React Native WebView transport 和 Tauri transport 都必须导入这两个常量做 timeoutMs 归一化,不得在 H5 transport 本地重声明默认 / 最大超时。npm run check:native-shells 和桌面壳配置检查会拒绝回退到本地超时边界。

2026-06-19 追加:H5 原生宿主 runtime 回读短超时进入共享契约。packages/shared/src/contracts/hostBridge.ts 导出 HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS=3000src/services/host-bridge/hostBridge.tsrefreshNativeAppHostRuntime() / getNativeAppHostRuntime() 请求 host.getRuntime 时必须使用该常量;根级原生壳门禁和桌面壳配置检查会拒绝 H5 facade 重新声明 HOST_RUNTIME_REFRESH_TIMEOUT_MS

2026-06-19 追加:H5 用户交互型宿主请求长超时进入共享契约。packages/shared/src/contracts/hostBridge.ts 导出 HOST_BRIDGE_USER_INTERACTION_TIMEOUT_MS=30000,文件导入 / 导出、图片选择 / 拍摄等需要等待系统面板或用户选择的 HostBridge facade 请求必须使用该常量;根级原生壳门禁和桌面壳配置检查会拒绝 H5 facade 重新写入 timeoutMs: 30000

2026-06-18 追加:原生壳注入消息来源进入门禁。Expo 和 Tauri 注入给 H5 的 HostBridge response / event 都显式带 origin: window.location.originsource: windowH5 nativeAppHostBridge listener 会忽略带非当前窗口 source 或非当前页面 origin 的 message。这样后续 AI sandbox iframe 即使能向父页面 postMessage 同形 envelope,也不能结算宿主请求或伪造宿主事件;GameBridge 继续走单独 allowlist。

2026-06-19 追加:H5 页面内应用导航会保留完整原生宿主上下文。pushAppHistoryPath()replaceAppHistoryPath() 必须通过共享 HOST_BRIDGE_PRESERVED_RUNTIME_CONTEXT_QUERY_KEYS 补齐 clientTypeclientRuntimeminiProgramEnvhostShellhostPlatformhostVersionbridgeVersionhostCapabilities,并用应用 history state 标记 H5 自己写入的导航条目。这样直达二级页补返回锚点、平台内页面切换和原生壳 runtime 能力刷新不会因为 H5 自己跳转而掉回普通浏览器运行态。

2026-06-18 追加:HostBridge request id 进入宿主侧 replay 门禁。Expo 壳会缓存已完成响应并让进行中的同 id 请求共用同一执行结果;Tauri 壳在唯一 host_bridge_request command 外层通过 HostBridgeReplayState 对同 id 请求做等待 / 回放。重复 id 只返回首次结果,不会二次触发系统分享、外链、剪贴板、文件选择 / 保存、本地通知或窗口动作。

2026-06-19 追加:HostBridge response replay 缓存上限进入共享契约。packages/shared/src/contracts/hostBridge.ts 导出 HOST_BRIDGE_RESPONSE_CACHE_MAX=128,Expo 移动壳直接导入该常量裁剪已完成响应缓存;Tauri 桌面壳保留 Rust 运行时镜像,但 apps/desktop-shell/scripts/check-config.mjs 会反查共享常量并拒绝数值漂移。两端测试都覆盖超过上限后淘汰最早响应,避免 request id 重试缓存无限增长。

2026-06-18 追加:HostBridge request envelope 校验收紧。共享契约提供 isHostBridgeMethodnormalizeHostBridgeRequestId;Expo 壳直接复用,Tauri 壳镜像同一 method 白名单和 request id 规则。空 id、控制字符 id、超长 id 和未知 method 都在进入 replay / 能力分发前返回 invalid_request,已知但当前壳未实现的登录 / 支付等 method 才返回 unsupported_method。Tauri 唯一 host_bridge_request command 入口必须先经过 prepare_host_bridge_request(...),再进入 HostBridgeReplayState reserve / wait / execute;非法 envelope 不得占用 replay slot,也不得触发任何宿主能力分发。

2026-06-20 追加:Tauri 桌面壳 HostBridge replay 的内部锁、等待和缓存状态异常只写入桌面壳 stderr 观测日志,H5 侧统一收到 host_error 与固定文案 desktop host bridge request failedstderr 只输出 desktop host bridge replay failed for <stage> 固定阶段标签,不记录 mutex / condvar 的 Rust 错误文本。HostBridgeReplayState::reserve(...) 返回 Resultcomplete(...) / wait_for_response(...) 也不得用 expect 让宿主进程 panic。桌面配置检查会反查 replay 失败日志、稳定错误响应、label-only 诊断和 poison lock 单测,避免后续把内部 mutex / condvar 细节或 Rust panic 泄露到 H5 调用链。

2026-06-20 追加:Expo 移动壳 host.getRuntime 回包里的 capabilities 必须直接等于共享契约中的 HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIESHOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES,且使用同一个已归一的平台值选择 profile;移动壳 runtime 单测和配置检查会反查精确 profile 断言,避免 H5 实际消费的能力回包与入口 URL 或共享 profile 分叉。

2026-06-18 追加:HostBridge method 白名单进入跨壳门禁。packages/shared/src/contracts/hostBridge.tsHOST_BRIDGE_METHODS 是唯一协议来源;Expo 壳的 HostBridge 分发 case 不得处理共享契约外 methodTauri 壳 Rust HOST_BRIDGE_METHODS 必须与共享契约逐项一致。两端配置检查会在 npm run check:native-shells 中拒绝 method 白名单漂移,新增宿主能力必须先更新共享契约,再落壳实现。

2026-06-19 追加:HostBridge event 白名单进入跨壳门禁。packages/shared/src/contracts/hostBridge.tsHOST_BRIDGE_EVENTS 是宿主注入事件名的唯一来源,当前只包含 app.lifecyclenetwork.statusChangednavigation.canGoBackfile.imageDropped;事件名必须存在于 capability 白名单,各宿主壳只声明自身真实发射的事件 capability。Expo 壳事件注入函数使用 HostBridgeEventNameTauri 壳 shell/events.rs 镜像同一清单并在脚本生成前拒绝未知事件,H5 nativeAppHostBridge 只分发 isHostBridgeEventName() 认可的事件。H5 subscribeHostAppLifecycle()subscribeHostNetworkStatusChange()subscribeHostNavigationCanGoBack()subscribeHostImageDrop() 必须同时校验 host.events 与对应事件 capability,缺任一能力时不绑定事件监听;根级 npm run check:native-shells 会反查这四个订阅 facade 都通过 canUseNativeHostEventCapability(...) 进入同一双能力门控,并拒绝新增事件后漏补 H5 facade 覆盖。

2026-06-18 追加:HostBridge capability 与 request handler 关系进入门禁。共享契约中属于 request method 的 capability,如果被 Expo 或 Tauri 壳声明,就必须在对应壳的 HostBridge 分发中显式处理;反过来,壳分发中处理的 method 必须已被该壳声明。共享 method 白名单中未进入桌面 capability profile 的 method 必须自动派生为桌面 unsupported 覆盖清单,当前包括 auth.requestLoginpayment.requestfile.captureImagescanner.scanQrCodehaptics.impact;请求实际到达桌面壳时只能返回明确 unsupported_method,不得伪造成功或半接入。H5 facade 除 host.getRuntime 真实回读外,所有 native_app request 能力都必须通过 canUseNativeHostCapability(...) 统一门控,不得在单个业务能力函数内直接读取 runtime.hostCapabilities.includes(...);根级 npm run check:native-shells 从共享 HOST_BRIDGE_METHODS 自动派生该门控清单。host.eventsapp.lifecyclenetwork.statusChangedfile.imageDroppednavigation.canGoBack 等事件类 capability 不要求 request handler。

2026-06-18 追加:桌面壳 release CSP 与 dev CSP 分离。Release csp 不再包含 http://127.0.0.1:*ws://127.0.0.1:*,只允许打包资产、自身脚本、生产 HTTPS / WSS API、图片、媒体和 sandbox frame 所需来源;本地 Vite、HMR WebSocket 和开发 frame 只写入 Tauri devCspapps/desktop-shell/scripts/check-config.mjs 会拒绝 release CSP 混入本机调试源,也会校验 dev CSP 仍保留本机开发源。

2026-06-19 追加:桌面壳 macOS 媒体权限说明进入门禁。Tauri 桌面壳仍不新增摄像头或麦克风 HostBridge method,不把系统媒体能力暴露成桌面命令;同源 H5 页面可继续使用浏览器标准 getUserMedia 承接儿童动作热身 Demo 的实时摄像头输入和汪汪声浪正式 runtime 的实时麦克风输入。macOS 分发包必须通过 bundle.macOS.infoPlist="Info.plist" 合并受控用途说明:NSCameraUsageDescription 只描述同源 H5 实时动作输入,NSMicrophoneUsageDescription 只描述同源 H5 实时声音玩法。apps/desktop-shell/scripts/check-config.mjs 会校验 plist 路径和两条文案,并把 Info.plist 纳入生产壳替身词扫描,防止桌面包缺少系统授权说明、把媒体权限扩写成通用采集能力,或在 macOS 分发配置里留下临时替身文本。

2026-06-18 追加:桌面壳 release 构建烟测进入统一验收。npm run check:native-shells 会在 H5 HostBridge、Expo 壳和 Tauri 单测通过后执行 npm run desktop-shell:build -- --no-bundle,确认 Tauri release 入口指向共享公开主站、受控命令白名单、图标和 Rust release 编译可以共同产出桌面二进制;构建后 desktop-shell:stage-release-binary 会把当前平台二进制复制到根目录 build/native/desktop/genarrative-desktop-shellbuild/native/desktop/genarrative-desktop-shell.exe,该目录沿用根 build/ 的 gitignore,只作为本机或 CI 可收集产物目录。统一验收必须检查 staged 二进制存在、非空且符合当前平台可执行文件头;macOS 校验同时接受 32/64 位与 fat Mach-O 的大端、反字节序合法魔数,不得把 arm64 常见的 cf fa ed fe 文件头误拒绝。apps/desktop-shell/scripts/check-config.mjs 会反查根级门禁仍保留 release build smoke、staging 步骤、二进制路径、Linux ELF / macOS Mach-O / Windows PE 文件头和可执行位检查,避免桌面产物验收被改成只看命令退出码。该烟测不生成平台安装包,避免把 Linux 本机缺少的系统打包器误判为 HostBridge 回归。

2026-06-18 追加:移动壳 Expo managed config 烟测进入统一验收。npm run check:native-shells 会执行 npm run mobile-shell:config,在 apps/mobile-shell 目录内调用 expo config --type public --json,校验 Expo CLI 实际解析结果中的包名、scheme、深链、ATS / cleartext / backup / 相机与麦克风权限、启动页、adaptive icon、插件配置和 HostBridge 版本没有漂移。apps/mobile-shell/scripts/check-config.mjs 会反查根级门禁仍保留 EAS build profile、Expo config 和 Metro export 三个移动分发烟测,避免移动壳验收退回到只看源码类型检查。

2026-06-18 追加:移动壳 Metro export 烟测进入统一验收。npm run check:native-shells 会执行 npm run mobile-shell:export,用 Expo CLI 分别为 Android 和 iOS 生成 production bundle,校验 Metro metadata 版本、单平台 fileMetadata、Hermes AppEntry-*.hbc bundle 路径、assets 数组、生产 H5 URL 和原生宿主上下文 token 后清理临时 .expo-export-smoke/ 目录。该烟测不生成 APK、AAB 或 IPA,也不写商店资料,只证明移动壳入口、Expo/RN 依赖和平台差异代码能被真实生产 bundler 打包。

2026-06-20 追加:移动壳新增 EAS 原生包构建 profile。apps/mobile-shell/eas.json 固定 EAS CLI 下限、使用本地 app.json 版本字段,并提供 Android production 本地构建脚本产出可安装 APK、iOS production-simulator 本地构建脚本产出 release simulator 包;两者都设置 EXPO_NO_DOTENV=1,避免本机 .env 把开发 H5 URL 或私密配置带进可分发包。真实 mobile-shell:build:android 输出到根目录 build/native/mobile/genarrative-mobile-android.apk,真实 mobile-shell:build:ios 输出到 build/native/mobile/genarrative-mobile-ios-simulator.tar.gz,该目录沿用根 build/ 的 gitignore,只作为本机或 CI 可收集产物目录。当前仍不写商店提交 profile、签名凭据来源、自动递增、OTA runtimeVersion 或 releaseChannelnpm run check:native-shells 只跑 mobile-shell:build-config 校验构建 profile、脚本、本地 eas-cli devDependency、固定输出路径和从 apps/mobile-shell 目录解析到的 EAS CLI 版本,真实 mobile-shell:build:android / mobile-shell:build:ios 作为具备 Android SDK、Xcode / simulator 环境和 EAS 本地构建依赖时的分发构建命令。真实构建完成后必须运行 npm run mobile-shell:build-artifacts,校验 Android APK 是 ZIP 格式且包含 AndroidManifest.xmlclasses.dexassets/index.android.bundleiOS simulator 包是 gzip tar 且包含 .app/Info.plist.app/Genarrative.app/main.jsbundle,避免把空文件、错误压缩包或非原生包当作移动端壳产物。

2026-06-18 追加:登录 / 支付能力伪声明进入门禁。auth.requestLoginpayment.request 保留在共享 HostBridge 契约中供未来真实接入,但 Expo 与 Tauri 壳在没有真实 SDK、渠道流程和后端契约前不得声明这些 capability,也不得把它们写入 hostCapabilities;请求到达壳层时必须返回明确 unsupported_method,让 H5 fallback 承接。两端壳测试会直接覆盖这两个 method,避免后续半接入时返回伪成功。

2026-06-20 追加:移动壳 HostBridge 能力失败诊断统一只记录稳定标签。Expo 壳的 badge、clipboard、haptics、navigation、network、notification 和 share 等 HostBridge 能力在捕获原生 SDK / WebView / 系统 API 异常时,只能向控制台输出 mobile ... failed for <stage> 这类固定阶段标签,不把原生 error 对象、native stack、平台错误码、文件路径或系统返回结构作为日志参数输出;HostBridge response 继续返回共享契约里的稳定 host_error / cancelled / unsupported_capability 等错误。移动壳 shell 层的 HostBridge message 注入、宿主事件注入、外链离壳、WebView 下载阻断、深链读取 / 拒绝、扫码权限请求和 H5 history 同步失败也按同一 label-only 口径记录;WebView 进程失败只记录失败窗口计数和固定阶段标签,不记录当前 H5 URL 或 query。apps/mobile-shell/scripts/check-config.mjs 会反查这些能力模块和 shell 层日志形式,避免后续为了排查方便把原生错误对象、WebView 事件或 H5 地址重新带进可分发移动壳日志。

2026-06-20 追加:桌面壳注入到 H5 的 history 追踪脚本同样只记录稳定标签。Tauri 桌面壳的 desktop_navigation_state_script 在 H5 replaceState 同步失败时只输出 desktop navigation state sync failed,不把页面内 error 对象传给控制台;apps/desktop-shell/scripts/check-config.mjs 会拒绝恢复 console.warn(..., error) 形式,避免桌面 WebView 内脚本异常对象进入可分发壳诊断日志。

2026-06-20 追加:桌面壳 WebView 下载阻断诊断只记录固定能力标签。Tauri 主窗口默认拒绝网页自动下载落盘;当 WebView download 事件被阻断时,宿主 stderr 只输出 desktop host event blocked for webview.download,不记录完整下载 URL、query、blob 标识或本地路径。apps/desktop-shell/scripts/check-config.mjs 会拒绝恢复 ...: {url} 形式,确保桌面下载诊断不外泄 H5 运行态地址或私有参数。

2026-06-18 追加:微信 / Expo / Tauri 三端壳的生产源码和配置禁止出现 mock / fake / placeholder / stub / TODO / FIXME / 占位 / 模拟 / 伪造 / 未实现 / 临时 等脚手架或替身词;测试文件仍可使用 vi.mock 或等价测试替身。apps/mobile-shell/scripts/check-config.mjsapps/desktop-shell/scripts/check-config.mjs 会扫描各自生产入口、配置和壳实现;移动壳 EAS / Expo config / Metro export smoke 脚本也属于可分发验收入口,必须纳入移动壳单端结构清单和替身词扫描。根级 npm run check:native-shells 会统一扫描 miniprogramapps/mobile-shellapps/desktop-shell、H5 HostBridge transport 和共享 HostBridge 契约生产源码,防止把临时替身或占位文案带进可分发壳。

2026-06-19 追加:H5 HostBridge 真实调用链扫描改为自动发现。根级 npm run check:native-shells 会从 src/ 生产文件里收集直接导入并调用真实宿主能力 facade 的文件,以及 useHostLifecycleActiveuseHostNetworkOnlineplatformProfileHostClipboard 等薄 wrapper 的消费者;新增 reloadHostWebViewshowHostLocalNotificationsetHostAppBadgeCountimportHostTextFileimportHostImageFilecaptureHostImageFilesubscribeHostImageDropreadHostClipboardTextopenHostExternalUrl 等 H5 调用点时,不再手工维护单个扫描文件清单。登录与支付外链属于敏感跳转路径,src/services/authService.tssrc/services/payment/paymentRedirect.ts 额外列入必扫清单,防止后续绕回裸浏览器跳转却脱离 HostBridge 调用链门禁。H5 业务文件允许正常表单 placeholder 属性、业务占位图文案和真实兼容 / 故障语义中的“未实现”“临时”表述,但仍会拒绝 mock / fake / stub / TODO / FIXME / 模拟 / 伪造等替身痕迹。

2026-06-18 追加:微信壳行为测试进入原生壳统一验收。npm run check:native-shells 会运行 miniprogram/host-bridge/miniprogram/shell/pages/web-view 样式和 scripts/miniprogram-web-view-auth.test.ts 测试,覆盖微信 WebView 入口、登录触发、分享目标、支付结果和九宫切图桥接行为;三端桥接层文件结构检查只锁定职责边界,真实行为回归必须由同一门禁中的微信壳测试证明。

2026-06-21 追加:微信 web-view 壳读取小程序 envVersion 失败时只允许记录 [web-view] read mini program env failed 固定标签,不把 wx.getAccountInfoSync() 原生异常对象写入生产日志;根级原生壳门禁会拒绝恢复 console.warn(..., error)

2026-06-21 追加:微信九宫切图壳的下载、图片读取、切图导出、保存相册和最终保存失败诊断只允许记录 [share-grid] <stage> 固定标签,不把微信原生错误对象或后端细节写入生产日志;页面仍只展示 九宫切图保存失败。 稳定文案。

2026-06-21 追加,2026-07-18 收口:微信支付参数解析和虚拟支付失败诊断只允许记录 [wechat-pay] <stage> 固定标签,不把微信原生错误对象写入生产日志;H5 回灌只使用稳定支付失败语义。旧生成结果订阅消息诊断随该能力退役,不属于现役壳契约。

2026-06-21 追加:微信 web-view 认证链路诊断只允许记录 [web-view] <stage> 固定标签,不把 wx.login 错误、登录 / 绑手机号 HTTP response、手机号授权 detail 或认证异常对象写入生产日志;页面仍只展示稳定登录 / 绑手机号错误文案。

2026-06-21 追加:微信 web-view 页面事件诊断只允许记录 [web-view] <stage> 固定标签,不把加载成功、加载失败或 H5 message 的 event.detail 写入生产日志;分享目标仍从结构化 message payload 解析,但日志不能输出原生事件体。

2026-06-19 追加,2026-07-18 收口:微信小程序壳路由一致性进入原生壳统一验收。npm run check:native-shells 会反查 miniprogram/app.json.pagesminiprogram/host-bridge/protocol.js 页面 URL 常量一致,H5 src/services/host-bridge/hostBridge.ts 的小程序登录、支付和九宫切图页面常量与微信协议常量一致,并检查 miniprogram/host-bridge/webView.js 的 WebView 分享入口和分享目标消息类型不漂移;旧订阅授权页面与 wechatMiniProgramSubscribe 不再进入现役扫描。门禁同时校验 miniprogram/config.js 的生产 / 开发 H5 入口与 API base URL 都是显式配置的纯 HTTPS 域名,WEB_VIEW_SOURCE_QUERY 与共享 HOST_BRIDGE_WECHAT_MINI_PROGRAM_SOURCE_QUERY 一致,miniprogram/shell/webView.js 请求头从 WEB_VIEW_SOURCE_QUERY 读取 clientType / clientRuntimeH5 runtime parser 读取共享 HOST_BRIDGE_RUNTIME_CONTEXT_QUERY_KEYH5 路由保留字段读取共享 HOST_BRIDGE_PRESERVED_RUNTIME_CONTEXT_QUERY_KEYS。运行时对开发域名的生产域名回退只作为异常兜底,不作为配置口径;新增小程序页面、改页面路径、调整来源 query 或切换域名格式时,必须同步协议常量、app.json、共享 HostBridge query 契约和这条门禁。

2026-06-18 追加:原生壳本地生成物不作为生产源码门禁输入。Expo .expo/、Expo export smoke 临时目录、Tauri target/、Tauri schema gen/、Tauri 自动生成权限目录和根目录 build/native/ 都必须保持 gitignored;根级生产壳敏感词扫描只检查可提交的壳源码和配置,避免本机工具输出影响生产门禁。npm run check:native-shells 会同时检查这些目录没有被 Git 追踪,并用 git check-ignore -v 确认对应 gitignore 规则仍然生效。手写 capability / 权限配置仍需保留在扫描范围内。

2026-06-18 追加:移动壳启动页与 Android adaptive icon 复用现有真实品牌图标 apps/mobile-shell/assets/icon.png,背景色固定为 H5 壳根背景 #fffdf9。该 PNG 是 1024x1024 RGBA 透明前景品牌资产,不新增占位图;Expo splash 使用同一图标 contain 展示,Android adaptiveIcon.foregroundImage 使用同一透明前景图,check-config.mjs 会校验图标尺寸、透明像素、启动页和 adaptive icon 配置。

2026-06-18 追加:桌面壳 bundle 图标集从现有真实品牌 PNG apps/desktop-shell/src-tauri/icons/icon.png 派生,补齐 32x32.png128x128.png128x128@2x.pngicon.icoicon.icnsTauri bundle.icon 同时声明这些平台图标。没有引入外部素材或占位图;apps/desktop-shell/scripts/check-config.mjs 会校验 PNG 尺寸、ICO 多尺寸头部、ICNS 容器长度和 bundle 图标列表,避免后续退回单图标或替换为非品牌素材。

2026-06-21 追加:桌面壳本地开发入口必须让 Tauri devUrl 和主站 Vite 实际监听端口保持一致。apps/desktop-shell/src-tauri/tauri.conf.jsondevUrl 固定为 http://127.0.0.1:3000/beforeDevCommand 必须执行 npm --prefix ../.. run dev:web -- --web-port 3000 --strict-web-port,不能只依赖 WEB_PORT=3000 环境变量;Linux 用户端口段分配会改写未显式指定的 Web 端口,裸 dev:web 可能漂移到 10000+,导致 Tauri 窗口加载 3000 时白屏或连接失败。3000 被占用时必须直接失败并提示释放端口,不能静默漂移。由于桌面壳主窗口设置了 create=false 并由 Rust 手动创建,apps/desktop-shell/src-tauri/src/app.rs 在 dev build 下必须把主窗口 URL 替换为 build.devUrl 后再补写 HostBridge query,不能继续用 release 的 index.html,否则 WebView 会落到 tauri.localhost/index.htmlapps/desktop-shell/scripts/check-config.mjsscripts/dev.test.ts 和桌面壳 Rust 单测会反查该口径。

2026-06-18 追加:移动壳生产包网络安全元数据显式收紧。Android usesCleartextTraffic=falseiOS NSAppTransportSecurity.NSAllowsArbitraryLoads=false,并设置 ITSAppUsesNonExemptEncryption=false 作为当前未接入自定义加密能力的出口合规声明;开发联调本机 Vite 仍通过显式 EXPO_PUBLIC_GENARRATIVE_WEB_URL=http://127.0.0.1:3000/ 进入 development build,不把任意明文流量开关带进默认包配置。apps/mobile-shell/scripts/check-config.mjs 会校验这些网络安全字段。

2026-06-19 追加:移动壳麦克风权限只服务同源 H5 实时玩法。汪汪声浪正式 published runtime 必须通过浏览器标准 getUserMedia({ audio: true }) 采样真实音量;Expo 壳因此显式配置 iOS 麦克风用途文案、Android RECORD_AUDIO 权限和 react-native-webviewmediaCapturePermissionGrantType="grantIfSameHostElsePrompt"。该权限不新增 HostBridge method,不开放后台录音、远程语音 SDK、任意文件系统或 AI H5 sandbox 直连宿主能力;只有通过同源主站 WebView 留壳规则的 H5 页面能触发系统授权。expo-cameraexpo-image-picker 的麦克风用途文案必须保持同一条真实玩法说明,android.blockedPermissions 不得再移除 RECORD_AUDIO,最终 public config 只允许 CAMERAPOST_NOTIFICATIONSRECORD_AUDIO 三类显式权限。移动拍照、扫码、相册、音频导入和本地通知仍按各自受控能力边界执行,不把麦克风权限当作通用录音入口。apps/mobile-shell/scripts/check-config.mjscheck-expo-config.mjs 会拒绝权限、文案或 WebView 媒体捕获策略漂移。

2026-06-18 追加:移动壳 Android 自动备份显式关闭。Expo android.allowBackup=false,避免 WebView cookie、localStorage、缓存文件、系统剪贴板导入中间文件等本地宿主状态进入 Google Drive 自动备份 / 恢复链路;正式业务事实仍以后端账号、作品、钱包和草稿状态为准,不依赖设备备份恢复。apps/mobile-shell/scripts/check-config.mjs 会拒绝恢复默认允许备份的 Android 包配置。

2026-06-18 追加:移动壳 WebView 原生安全开关显式收紧。react-native-webview 只加载同源主站入口,保留 JS 和 DOM storage 以运行现有 H5,但禁用 JS 自动开窗、多窗口、文件访问、file URL 跨源访问、HTTPS 页面加载 HTTP 混合内容、第三方 Cookie、共享 Cookie 和 WebView 远程调试;外链继续只允许 http:https:mailto:tel: 离开 WebView 交给系统。apps/mobile-shell/scripts/check-config.mjsnavigation.test.ts 会覆盖这些壳边界,避免后续为单个页面调试把完整 HostBridge 暴露给外域页面。

2026-06-18 追加:移动壳 WebView 默认下载路径显式关闭。壳层在 WebView 注入脚本中阻断 <a download> 点击、危险下载协议链接、window.open 下载 URL 和程序化 anchor clickonShouldStartLoadWithRequest 会在同源 / 外链分流前拒绝 blob:data:file:filesystem: 导航,避免下载 URL 进入带完整 HostBridge 的 WebView 或交给系统外部应用;iOS onFileDownload 事件只丢弃不落盘,Android 包配置阻断外部存储读写、管理外部存储和请求安装包权限;H5 文本、图片、音频保存继续只能走 file.exportTextfile.exportImagefile.exportAudio 的受控 HostBridge 导出能力。

2026-06-18 追加:移动壳 HostBridge 消息入口增加来源校验。onMessage 不只依赖导航拦截和 originWhitelist,还会读取 event.nativeEvent.url,只有同源主站页面才能进入 handleMobileHostBridgeMessageabout:blank、外域 URL、协议降级或危险协议页面发来的消息全部丢弃,不返回 HostBridge 错误细节。该校验与 navigation.openNativePage 共用同源规则,防止历史中间页或异常页面在带完整 HostBridge 的 WebView 中发起宿主能力请求。

2026-06-18 追加:app.openExternalUrl 的协议白名单以共享 HostBridge 契约 HOST_BRIDGE_EXTERNAL_URL_PROTOCOLS 为唯一来源,当前只允许 http:https:mailto:tel:。Expo 壳直接复用共享归一化逻辑,Tauri 壳 Rust 侧用 URL parser 镜像同一清单;npm run check:native-shells 会反查共享契约与桌面壳协议清单,防止某一端单独放宽外链协议。

2026-06-18 追加:微信、移动端和桌面端桥接层文件结构按职责对齐。微信小程序的 web-view、支付和九宫切图桥接逻辑统一迁入 miniprogram/host-bridge/webView.jspayment.jsshareGrid.js,页面目录只保留页面生命周期、WXML/WXSS 和装配;移动壳拆成 apps/mobile-shell/src/host-bridge/protocol.tscapabilities.tsdispatch.tsfiles.tsfilePayloads.tsfiles.test.tsnavigation.test.tsnavigation.tsscanner.test.tsscanner.tsshare.test.tsshare.ts 和 facade bridge.ts,与桌面端 host_bridge/protocol.rscapabilities.rsdispatch.rsfiles.rsfile_payloads.rsshare.rsmod.rs 对齐,其中移动 files.ts 只承接 Expo 系统文件交互和 HostBridge 响应包装,filePayloads.ts 承接 MIME、大小、base64、文件名和 picker payload 边界;移动壳根 App.tsx 也保持薄入口,只装配 src/shell/ShellApp.tsx,WebView 容器、深链、网络、生命周期、安全区、扫码 overlay 和安全策略全部留在 src/shell/;桌面壳 Rust 源码拆成 apps/desktop-shell/src-tauri/src/app.rshost_bridge/*.rsshell/*.rs,其中 app.rs 承接 Tauri builder / plugin / window 装配,runtime.rsurl.rsnavigation.rsnetwork.rslifecycle.rsfile_drop.rsevents.rsdeep_link.rstray.rswindow_state.rswebview.rs 分别承接运行态、入口 URL、导航 / 下载、网络、生命周期、拖拽图片、HostBridge 事件注入、深链、托盘、窗口状态持久化和 WebView 门面,薄 main.rs 只声明模块并调用 app::run()。根级 npm run check:native-shells 会锁定三端桥接层目录清单,避免后续把能力逻辑重新散落到页面、移动入口或桌面入口。

2026-06-19 追加:HostBridge 载荷边界以共享契约为单一声明来源。packages/shared/src/contracts/hostBridge.ts 导出文本 / 图片 / 音频 MIME 清单、导入 / 导出字节上限、导出文件名 fallback 与长度上限,以及 request id、角标、剪贴板和本地通知文本长度边界;Expo 移动壳必须直接导入这些共享常量,不再本地重声明文件大小或 MIME 清单,并且文本 / 音频导入必须在读取内容前通过 picker size 或 Expo File.size 完成大小门禁,无法拿到可信 byte count 时直接拒绝导入;Tauri 桌面壳的配置检查会反查 Rust 镜像实现,拒绝文件大小、MIME 清单、文件名、通知、剪贴板或 request id 边界与共享契约漂移。新增文件类型或调整体积上限必须先更新共享契约、壳实现和门禁,再进入玩法或 H5 facade。

Phase 4:宿主能力扩展

  • 移动端接入系统分享、推送、原生登录和渠道支付。
  • 移动端和桌面端的自动更新、崩溃上报、analytics、渠道分发、原生登录和渠道支付都必须等真实 SDK、后端契约、发布流程和隐私口径确定后逐项接入;文件导出、图片拖拽导入、系统托盘、即时本地通知和受控分享动作已按真实宿主能力逐项接入。
  • Tauri 桌面壳的文件导入导出执行边界分为两层:apps/desktop-shell/src-tauri/src/host_bridge/files.rs 统一承接系统文件对话框过滤器、用户取消语义、路径转换、异步读写编排和 HostBridge 响应,apps/desktop-shell/src-tauri/src/host_bridge/file_payloads.rs 统一承接 MIME、大小、base64、文件名清洗、本地副本读写和 payload 组装;dispatch.rs 只按 method 委托文件模块。
  • 所有新增能力先更新 HostBridge 契约和测试,再落壳实现。

Phase 5AI H5 sandbox

  • 定义 GameBridge 契约。
  • 生成代码包只进入 sandbox iframe。
  • 父页面负责资产授权、事件转发和后端裁决。
  • Expo / Tauri 壳只感知父页面 HostBridge,不直接感知 AI 游戏代码。

验收清单

  • 普通浏览器、小程序、Expo 壳、Tauri 壳都能返回正确 getHostRuntime()
  • 未支持的宿主能力不会阻断主流程,H5 fallback 可用。
  • 固定玩法在四类宿主里读取同一作品数据和运行态 snapshot,不走代码包下载。
  • 支付、登录、分享都有幂等、超时和错误回包。
  • AI sandbox 无法调用 HostBridge,也无法读取 H5 登录态。
  • Tauri release 包不允许任意远端页面调用桌面命令。
  • Expo WebView 外链离开主站后不保留完整 HostBridge。
  • 根级验收入口 npm run check:native-shells 必须同时覆盖 H5 HostBridge 关键路径、宿主上下文 query 契约、微信小程序页面路由与来源 query 反查、三端桥接层文件结构门禁、Expo 壳 typecheck / test / EAS build config smoke / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面 release --no-bundle 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描。该扫描范围必须包含微信小程序壳生产 .js、共享 HostBridge 契约、H5 native transport,并自动覆盖已接入真实宿主能力 facade 的 H5 生产调用链文件。

参考资料

  • Expo development buildshttps://docs.expo.dev/develop/development-builds/introduction/
  • Expo custom native codehttps://docs.expo.dev/workflow/customizing/
  • Expo config pluginshttps://docs.expo.dev/config-plugins/introduction/
  • React Native WebView guidehttps://github.com/react-native-webview/react-native-webview/blob/master/docs/Guide.md
  • Tauri commandshttps://v2.tauri.app/develop/calling-rust/
  • Tauri capabilitieshttps://v2.tauri.app/security/capabilities/
  • Tauri permissionshttps://v2.tauri.app/security/permissions/

关联文档

  • docs/【前端架构】宿主壳能力统一协议-2026-06-17.md
  • src/services/host-bridge/hostBridge.ts