# 安装包旧 URL 残留与 `_greenlet` 运行时缺陷修复方案

## 1. 背景

当前安装包在 A 电脑上暴露出两类独立问题：

1. 运行时云端地址仍命中旧地址 `https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1`，导致绑定失败。
2. 删除缓存后，应用可以启动，但在专家页触发小红书专家链路时，报错 `DLL load failed while importing _greenlet: 找不到指定的模块。`

结合项目代码与日志，两个问题不是同一个根因：

- 旧 URL 问题来自本地配置项残留，不是 `runtime-cloud-cache.json` 回退。
- `_greenlet` 问题发生在专家页触发的 Python + Playwright 自动化链，不是启动阶段本身失败。

## 2. 现状结论

### 2.1 旧 URL 问题

- 当前默认运行时云端地址已经是新地址：
  - `apps/desktop/src/main/services/app-config.ts`
- 但只要本地 `app-config.json` 中存在 `runtimeCloudApiBaseUrl`，运行时就优先使用配置值。
- 启动日志已经明确显示：
  - `baseUrl = https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1`
  - `baseUrlSource = config`

结论：
- A 电脑不是“删了缓存还在读缓存”，而是“本地配置还保留旧地址”。
- 仅删除 `runtime-cloud-cache.json` 不足以修复；还需要处理 `app-config.json` 中的旧值。

### 2.2 `_greenlet` 问题

- 第二份 startup log 显示应用最终进入 `ready`，说明启动链路本身已完成。
- 专家页小红书项目会进入 Python/Playwright 自动化链。
- 当前运行时探测和 installer smoke 只校验了顶层包能否导入，例如 `playwright`。
- 但并没有显式验证：
  - `greenlet`
  - `from playwright.async_api import async_playwright`
  - 专家页真实触发后的项目执行路径

结论：
- 当前安装包验证口径偏浅。
- 安装包即使通过现有 smoke，仍可能在真实专家执行链路里因 `_greenlet` 或其 DLL 依赖失败。

## 3. 修复目标

修复后需要满足以下结果：

1. 旧机器升级或重装后，不会继续使用废弃的 runtime cloud URL。
2. 新机器首次安装时，默认直接使用新地址，无需手工清缓存或改配置。
3. 安装包里的 Python 运行时必须能真实支持专家页小红书链路所需的 Playwright/greenlet 依赖。
4. installer 验证必须能在构建阶段拦截这类问题，而不是等到用户机器上暴露。

## 4. 修复方案

### 4.1 运行时云端地址迁移

在 `apps/desktop/src/main/services/app-config.ts` 增加已废弃 runtime cloud URL 的迁移逻辑：

- 定义废弃地址列表，至少包含：
  - `https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1`
- 在配置加载归一化时，如果 `runtimeCloudApiBaseUrl` 命中废弃地址，则自动替换为当前默认地址：
  - `https://spb-bp1wv2oe0hvfvi98.supabase.opentrust.net/functions/v1`
- 替换后立即持久化写回 `app-config.json`

约束：

- 只迁移“已知废弃地址”
- 其他用户手工填写的非空自定义地址仍保留
- 不依赖用户手工删除配置文件

同时补充诊断：

- 启动日志中记录一次配置迁移事件
- diagnostics 中保留运行时云端地址的最终值，并在可能的情况下标记发生过迁移

### 4.2 打包 Python 运行时补齐 `greenlet`

在运行时 Python 依赖定义中显式纳入 `greenlet`，而不是只依赖 `playwright` 的间接依赖关系：

- 更新：
  - `build/runtime/python/requirements.in`
  - `build/runtime/python/runtime-requirements.lock.txt`
- 确认 `build/scripts/materialize-runtime-payload.ps1` 物化后的运行时 payload 中实际包含对应依赖

目标：

- 安装包中的 `resources/vendor/openclaw-runtime/python` 具备完整依赖
- 不把 `greenlet` 缺失问题留给最终用户机器

如果最终确认 Windows 下还依赖额外系统 DLL 或 VC runtime，则需要二选一：

1. 能随安装包自带则一并处理
2. 不能自带则在运行时探测和错误提示里明确报缺少系统依赖

### 4.3 提升 Python 运行时探测粒度

修改 `packages/runtime-manager/src/index.ts`：

- 在运行时探测清单中加入 `greenlet`
- 对关键模块不再只用 `find_spec`
- 至少对以下内容做真实导入探测：
  - `import greenlet`
  - `from playwright.async_api import async_playwright`

判定规则调整为：

- 只有关键依赖真实可导入时，`pythonReady` 才能为 `true`
- 如导入失败，错误信息要直接反映缺失模块或 DLL 问题

这样可以避免：

- 顶层包存在，但底层二进制扩展无法加载
- 系统显示 `pythonReady = true`，专家页一执行就崩

### 4.4 补强 installer smoke

修改 `build/scripts/installer-smoke.ps1`，把当前浅层导入校验升级为真实关键依赖校验。

现有检查仅覆盖：

- `import playwright`

应至少追加：

- `import greenlet`
- `from playwright.async_api import async_playwright`

若其中任意一步失败，则直接将安装包判定为：

- `payload-validation-failure`

同时在 smoke 输出中增加以下字段，便于回溯：

- `greenletImportProbe`
- `playwrightAsyncImportProbe`
- 对应 stderr 或异常消息

### 4.5 增加专家页真实链路 smoke

在现有 smoke 基础上新增一条安装包级别的真实专家链路验证：

- 启动已安装应用
- 进入 experts 页
- 选择小红书专家
- 发送一条最小测试消息
- 至少验证流程能进入对应 Python 自动化入口，不在 `_greenlet` 处崩溃

该 smoke 不要求真的发布内容，但要覆盖真实调用路径。

目标：

- 防止“安装包通过了基本 smoke，但真实专家链路仍不可用”

### 4.6 优化用户可见错误

当专家页项目执行失败时，错误呈现需要区分：

1. 启动失败
2. 项目执行失败
3. Python 运行时依赖缺失

改进方向：

- 主进程保留完整 stderr 到日志
- UI 不直接暴露生硬 Python 原始异常
- 对 `_greenlet` / DLL 类错误归类为：
  - 打包运行时依赖缺失或损坏
- 页面提示用户导出 diagnostics，而不是只看 startup log

## 5. 测试与验收

### 5.1 旧 URL 迁移验证

1. 人工构造包含旧 `runtimeCloudApiBaseUrl` 的 `app-config.json`
2. 启动应用
3. 确认配置被自动改写为新地址
4. 确认启动日志记录了迁移行为
5. 确认 runtime-cloud 请求实际打到新地址

### 5.2 Python 运行时验证

1. 在物化后的打包 Python 环境中执行：
   - `import greenlet`
   - `from playwright.async_api import async_playwright`
2. 验证 `pythonReady` 只有在两者都通过时才为 `true`
3. 人为破坏依赖后，确认系统能明确报错而不是误判 ready

### 5.3 安装包验证

1. 新机器或全新用户目录安装
   - 应默认使用新 runtime cloud 地址
2. 带旧配置目录启动
   - 应自动迁移旧地址
3. 运行 installer smoke
   - 必须通过 `greenlet` 与 `async_playwright` 检查
4. 运行专家页真实链路 smoke
   - 不应再出现 `_greenlet` 导入失败

## 6. 风险与默认决策

默认决策：

- 旧地址采用自动迁移，不要求用户手工处理
- `greenlet` 作为打包运行时显式依赖维护
- installer smoke 必须覆盖关键二进制依赖的真实导入
- 专家页真实链路增加单独 smoke，不再只依赖浅层 import 检查

当前假设：

- 当前有效 runtime cloud 默认地址保持为 `https://spb-bp1wv2oe0hvfvi98.supabase.opentrust.net/functions/v1`
- 旧 `xuphfkscoptnjoaecbvn.supabase.co/functions/v1` 已视为废弃地址
- 小红书专家链路继续基于打包 Python + Playwright 运行
- 旧 URL 问题主要影响升级机或迁移机；`_greenlet` 问题则属于安装包本身质量缺口，新电脑也可能遇到

## 7. 后续执行顺序建议

建议按以下顺序落地：

1. 先修 `app-config` 的旧 URL 自动迁移
2. 再补齐 Python 打包依赖与运行时探测
3. 然后升级 installer smoke
4. 最后补上 experts 页真实链路 smoke 与错误提示优化

这样可以先消除旧机器必现问题，再封堵安装包在新机器上的真实运行风险。
