diff --git a/apps/desktop-shell/scripts/check-config.mjs b/apps/desktop-shell/scripts/check-config.mjs index d656d2861..a271d10de 100644 --- a/apps/desktop-shell/scripts/check-config.mjs +++ b/apps/desktop-shell/scripts/check-config.mjs @@ -13,6 +13,8 @@ const capabilityPath = new URL( import.meta.url, ); const capability = JSON.parse(fs.readFileSync(capabilityPath, 'utf8')); +const macInfoPlistPath = new URL('../src-tauri/Info.plist', import.meta.url); +const macInfoPlist = fs.readFileSync(macInfoPlistPath, 'utf8'); const buildScriptPath = new URL('../src-tauri/build.rs', import.meta.url); const buildScript = fs.readFileSync(buildScriptPath, 'utf8'); const cargoManifestPath = new URL('../src-tauri/Cargo.toml', import.meta.url); @@ -79,6 +81,7 @@ const desktopShellNetworkSource = fs.readFileSync( const productionSourceRoots = [ new URL('../package.json', import.meta.url), new URL('../src-tauri/Cargo.toml', import.meta.url), + new URL('../src-tauri/Info.plist', import.meta.url), new URL('../src-tauri/build.rs', import.meta.url), new URL('../src-tauri/capabilities/main.json', import.meta.url), new URL('../src-tauri/src/', import.meta.url), @@ -1419,6 +1422,28 @@ if (config.bundle?.active !== true || config.bundle?.targets !== 'all') { throw new Error('desktop shell bundle targets must remain enabled for all platforms'); } +if (config.bundle?.macOS?.infoPlist !== 'Info.plist') { + throw new Error('desktop shell macOS bundle must merge the checked Info.plist'); +} + +for (const [key, value] of [ + [ + 'NSCameraUsageDescription', + '允许 Genarrative 使用摄像头运行需要实时动作输入的同源 H5 体验。', + ], + [ + 'NSMicrophoneUsageDescription', + '允许 Genarrative 使用麦克风运行需要实时声音输入的同源 H5 玩法。', + ], +]) { + if (!macInfoPlist.includes(`${key}`)) { + throw new Error(`desktop shell macOS Info.plist missing ${key}`); + } + if (!macInfoPlist.includes(`${value}`)) { + throw new Error(`desktop shell macOS Info.plist ${key} text drifted`); + } +} + const csp = String(config.app?.security?.csp ?? ''); const devCsp = String(config.app?.security?.devCsp ?? ''); for (const blockedCspToken of ["'unsafe-eval'", 'tauri:', 'file:']) { diff --git a/apps/desktop-shell/src-tauri/Info.plist b/apps/desktop-shell/src-tauri/Info.plist new file mode 100644 index 000000000..df41e8a70 --- /dev/null +++ b/apps/desktop-shell/src-tauri/Info.plist @@ -0,0 +1,10 @@ + + + + + NSCameraUsageDescription + 允许 Genarrative 使用摄像头运行需要实时动作输入的同源 H5 体验。 + NSMicrophoneUsageDescription + 允许 Genarrative 使用麦克风运行需要实时声音输入的同源 H5 玩法。 + + diff --git a/apps/desktop-shell/src-tauri/tauri.conf.json b/apps/desktop-shell/src-tauri/tauri.conf.json index aadd334c6..f7ad81180 100644 --- a/apps/desktop-shell/src-tauri/tauri.conf.json +++ b/apps/desktop-shell/src-tauri/tauri.conf.json @@ -31,6 +31,9 @@ "bundle": { "active": true, "targets": "all", + "macOS": { + "infoPlist": "Info.plist" + }, "icon": [ "icons/32x32.png", "icons/128x128.png", diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index a3e4ad78b..00f604710 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -95,6 +95,7 @@ - 2026-06-18 HostBridge method 白名单跨壳门禁:`packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_METHODS` 是唯一协议来源;Expo 壳 HostBridge 分发不得处理共享契约外 method,Tauri 壳 Rust `HOST_BRIDGE_METHODS` 必须与共享契约逐项一致。新增宿主 method 必须先更新共享契约,再落两端壳实现或明确 unsupported。 - 2026-06-18 HostBridge capability / handler 关系门禁:两端壳声明 request method capability 时必须有对应 HostBridge handler;壳 handler 处理的 method 必须已被该壳声明,登录 / 支付等 SDK-backed method 只能保留明确 `unsupported_method` 路径。事件类 capability 不要求 request handler。 - 2026-06-18 桌面壳 CSP 分层:Tauri release `csp` 不得包含 `http://127.0.0.1:*`、`ws://127.0.0.1:*` 或其它本机调试源,本机 Vite、HMR WebSocket 和开发 frame 只允许出现在 `devCsp`。桌面壳配置检查会同时拒绝 release CSP 混入本机调试源、dev CSP 缺失本机开发源,以及 release / dev CSP 加入 `unsafe-eval`、`tauri:` 或 `file:`。 +- 2026-06-19 桌面壳 macOS 媒体权限说明:Tauri 桌面壳不新增摄像头 / 麦克风 HostBridge method,但同源 H5 可以继续通过浏览器标准 `getUserMedia` 承接儿童动作热身 Demo 的实时摄像头输入和汪汪声浪正式 runtime 的实时麦克风输入;macOS 分发包必须通过 `bundle.macOS.infoPlist="Info.plist"` 合并 `NSCameraUsageDescription` 与 `NSMicrophoneUsageDescription`,文案只描述同源 H5 实时动作 / 声音玩法。桌面壳配置检查会校验 plist 路径与文案,防止缺少系统授权说明或把媒体权限扩成通用宿主采集能力。 - 2026-06-18 壳生产代码禁用临时替身:微信 / Expo / Tauri 三端壳的生产源码和配置不得出现 mock / fake / placeholder / stub / TODO / FIXME 以及对应中文脚手架词;测试文件仍可使用 mock。两端原生壳配置检查会扫描生产入口、配置和壳实现,根级 `npm run check:native-shells` 会统一扫描 `miniprogram`、`apps/mobile-shell`、`apps/desktop-shell`、H5 HostBridge transport 和共享 HostBridge 契约生产源码,防止把临时替身、占位文案或伪实现带进可分发壳或真实调用链。 - 2026-06-19 H5 HostBridge 调用链自动扫描:根级 `npm run check:native-shells` 从 `src/` 生产文件自动收集真实宿主能力 facade 的直接消费者,以及 `useHostLifecycleActive`、`useHostNetworkOnline`、`platformProfileHostClipboard` 等薄 wrapper 的消费者。H5 业务文件允许正常表单 `placeholder` 属性和业务占位图文案,但不得出现 mock / fake / stub / TODO / FIXME / 模拟 / 伪造等替身痕迹;壳源码和配置仍继续禁用 placeholder / 占位。 - 2026-06-18 原生壳本地生成物边界:Expo `.expo/`、Expo export smoke 临时目录、Tauri `target/`、Tauri schema `gen/` 和 Tauri 自动生成权限目录都必须保持 gitignored,不作为生产源码敏感词扫描输入;手写 capability / 权限配置仍在扫描范围内。 diff --git a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md index ca13010b9..5878f9f4a 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -465,6 +465,8 @@ GameBridge 禁止: 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 `devCsp`。`apps/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 路径和两条文案,防止桌面包缺少系统授权说明或把媒体权限扩写成通用采集能力。 + 2026-06-18 追加:桌面壳 release 构建烟测进入统一验收。`npm run check:native-shells` 会在 H5 HostBridge、Expo 壳和 Tauri 单测通过后执行 `npm run desktop-shell:build -- --no-bundle`,确认根 `dist` H5 资产、Tauri release 入口、受控命令白名单、图标和 Rust release 编译可以共同产出桌面二进制;该烟测不生成平台安装包,避免把 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 版本没有漂移。