---
name: pywebview_macos_packaging
description: 当打包 PyWebview macOS App、调试 "The string did not match" 错误、修改 _packaging/ 目录时，应该索引本记忆
---

# PyWebview macOS 打包项目记忆

## 目标
将 Vue 3 + Python Flask 项目打包为 macOS 桌面 App（PyWebview），不使用 Electron/Tauri 或原生 macOS 对话框。

## 核心文件
| 文件 | 作用 |
|------|------|
| `_packaging/app.py` | PyWebview 主入口：检测 `.env` → 直接启动 / setup页面 |
| `_packaging/setup.html` | 首次设置页面（4步：工作区→模型→管理员→确认），classic暖奶油主题 |
| `_packaging/app.spec` | PyInstaller 打包配置 |
| `_packaging/dist/AI-Agent-System.app` | 打包产物 |
| `_packaging/AI-Agent-System-v8.dmg` | 最后一个可用DMG（527MB） |

## 关键修复历史

### 1. DMG 禁止标志
- **原因**：DMG 内同时存在 `AI-Agent-System/`（onedir目录）和 `AI-Agent-System.app`
- **修复**：DMG 只放 `.app` + `Applications` 符号链接
- **hdiutil 创建需完全访问权限**：当前受限制，需切换到完全访问权限模式

### 2. OpenSSL 符号冲突
- **原因**：`_ssl.cpython-311-darwin.so` 加载 psycopg2 自带的旧版 `libcrypto.3.dylib`
- **修复**：用 Homebrew OpenSSL 3.6.2 的 dylib 替换 bundle 中 `__dot__dylibs` 目录

### 3. `.env` 加载路径
- **修复**：`config/__init__.py` 的 `_load_dotenv()` 改为 frozen 时读 `~/.astrion/astrion/host/.env`

### 4. engineio Invalid async_mode
- **原因**：PyInstaller frozen 环境缺少 `engineio.async_drivers.threading`
- **修复**：`app.spec` hiddenimports 添加 `'engineio.async_drivers.threading'`

### 5. PyWebview 6.x private_mode 🚨 **关键问题**
- **现象**：Playwright 浏览器中正常（0 errors），PyWebview App 中所有 API 返回 HTML 导致 "The string did not match the expected pattern"
- **根因**：`webview.start()` 默认 `private_mode=True`，WKWebView 隐私模式下 cookie 不工作，session 丢失
- **修复**：`webview.start(private_mode=False)`

### 6. App Transport Security
- **修复**：`app.spec` Info.plist 添加 `NSAppTransportSecurity: { NSAllowsLocalNetworking: True }`

### 7. 环境变量污染
- **问题**：旧环境变量 `AGENTS_HOST_HOME=~/.agents/host-clone` 导致数据目录错误（现已被 `ASTRION_DATA_ROOT` 替代）
- **注意**：`~/.astrion/astrion-clone` 是本对话副本数据，不要动
- **修复**：`_start_flask` 导入前清除 `ASTRION_DATA_ROOT`/`DATA_DIR`/`LOGS_DIR` 等旧环境变量污染

### 8. JSON 错误处理器
- **修复**：`_start_flask` 中注册全局 error handler，确保任何异常都返回 JSON 而非 HTML

## 当前状态（2026-06-12）

### ✅ 已确认正常
- Flask 后端在浏览器中完全正常（curl/Playwright 测试 0 errors）
- 登录、对话列表、数据加载均正常
- API 返回纯 JSON

### ❌ 仍存在问题
- **PyWebview App 中仍然无数据**，前端报 "The string did not match the expected pattern"
- 已排除：backend 崩溃、engineio 错误、private_mode、ATS、环境变量污染
- **问题定位**：在 PyWebview 的 WKWebView 内部，fetch API 请求被某种机制拦截/修改，导致返回非 JSON 响应

### 🔍 下一步排查方向
1. 在 PyWebview 中启用远程调试（`webview.start(debug=True)`）查看 WKWebView console
2. 检查 PyWebview 6.x 的 `request_sent`/`response_received` 事件是否拦截了请求
3. 检查 WKWebView 对 `127.0.0.1` 的 same-origin 策略是否有特殊处理
4. 尝试用 `localhost` 替代 `127.0.0.1`
5. 考虑降级 PyWebview 到 5.x 或尝试其他打包方案（Tauri/Electron）

## 打包命令
```bash
# Python 必须是 Homebrew Python 3.11（Xcode Python 3.9 会 SIGKILL）
cd _packaging
pyinstaller --noconfirm --distpath ./dist --workpath ./build app.spec

# 安装到 /Applications
cp -R dist/AI-Agent-System.app /Applications/
codesign --force --deep -s - /Applications/AI-Agent-System.app
xattr -cr /Applications/AI-Agent-System.app

# 创建 DMG（需完全访问权限）
hdiutil create -srcfolder dist/AI-Agent-System.app -volname "AI_Agent_System" -ov -format UDZO AI-Agent-System.dmg
```

## 用户要求
- 禁止原生 macOS/浏览器组件
- setup 页面需与项目设计风格统一
- 只读一个 `.env`（数据存储目录 ~/.astrion/astrion/host/.env）
- 等待时间不要设置太长
- `~/.astrion/astrion-clone` 是本对话副本数据，不要乱动
- `~/.astrion/astrion/host` 是正常运行时数据
