# 异步 Browser Script 支持设计

## 问题

`collect_lineloss.js` 的 `buildBrowserEntrypointResult` 是 async 函数，但 `build_eval_js` 生成的执行代码是同步的，导致 Promise 被 JSON.stringify 序列化为 `{}`。

**日志表现**：
```
[execute_browser_script_impl]   返回成功, payload 长度: 4
```
返回 `{}(4字符)` 而不是实际的报表数据。

## 根本原因

`callback_backend.rs` 的 `build_eval_js` 函数：
```javascript
var v=(function(){return {script}})();  // 同步执行
var t=(typeof v==='string')?v:JSON.stringify(v);  // Promise -> "{}"
```

当 script 返回 Promise 时，`JSON.stringify(Promise)` 返回 `{}`。

## 解决方案

修改 `build_eval_js` 支持 Promise：

1. 用 `await` 等待 script 执行结果
2. 检测结果是否为 Promise，如果是则等待 resolve
3. 保持对同步脚本的向后兼容

## 实现细节

修改 `src/browser/callback_backend.rs` 的 `build_eval_js` 函数：

```javascript
(async function(){
  try {
    var v = await (function(){return {script}})();
    // 等待 Promise resolve
    if (v && typeof v.then === 'function') {
      v = await v;
    }
    var t = (typeof v === 'string') ? v : JSON.stringify(v);
    // ... 回调逻辑保持不变
  } catch(e) {}
})()
```

关键点：
- 包装整个 IIFE 为 async
- 用 `await` 等待 script 执行
- 检测 Promise-like 对象 (`v.then === 'function'`)
- 向后兼容：同步脚本直接返回值，async 脚本返回 Promise 后被 await

## 影响范围

- `src/browser/callback_backend.rs`: 修改 `build_eval_js` 函数
- 所有 `browser_script` 类型的 skill 自动支持 async

## 测试验证

1. 运行 `cargo test` 确保现有测试通过
2. 端到端测试 `tq-lineloss-report.collect_lineloss` 返回实际数据而非 `{}`
3. 验证同步脚本（如知乎热榜）仍然正常工作

## 不在范围内

- 不修改 `wrap_browser_script`（方案 C 的做法）
- 不修改 skill 脚本本身
