Skip to content

[Bug] pi.agents-anywhere v1.0.3 完全不可用:activate() 第一行即崩溃(pi.storage 不存在),附带 11 项叠加缺陷 #63

Closed

Description

@mylastfree

pi.agents-anywhere v1.0.3 完全不可用:activate() 第一行即崩溃(pi.storage 不存在)

环境

项 值
PI-Desktop 0.15.8(Windows 11)
插件 pi.agents-anywhere v1.0.3(市场当前最新)
包 plugins.aiuo.net/packages/pi.agents-anywhere-1.0.3.piplug,shasum 1a11b8ce…115f
provenance mazongYY/pi-desktop-plugins @ 62bb22489f43153360604d1a035008a7fce1290a,console-upload@1.0.0 @ 2026-09-21T10:27:15Z

现象

  1. 插件列表显示已启用(enabled: true, status: ready);
  2. 每次 PI-Desktop 启动都记录一条错误:
    [pi.agents-anywhere] Failed to activate: TypeError: Cannot read properties of undefined (reading 'get')
        at activate (...\pi.agents-anywhere\main.js:329:42)
        at Object.<anonymous> (...\pi.agents-anywhere\main.js:379:1)
    
  3. 面板粘贴配置后点保存/连接毫无反应,无报错,状态永远「离线」;
  4. %USERPROFILE%\.pi-desktop\plugins\data\pi.agents-anywhere\ 从未生成。

结论:插件从未成功运行过。 业务逻辑一行都没执行。


根因

1(致命)pi.storage 不存在 → activate() 立即崩溃

main.js:329:

const storedConfig = (await pi.storage.get("connector_config")) || { ... };

宿主 buildApi()(app.asar)暴露的 pi 表面(77 个 API)为
app / themes / plugin / commands / speech / ui / project / workspace / desktop / fs / agent / models / session / usage / services / bus / clipboard / shell / browser / net / audio / keyboard / events——没有 storage。

持久化接口只有:

plugin.getSettings()   plugin.setSettings()   plugin.getDataPath()

pi.storage 为 undefined → 抛 TypeError。因为异常发生在 activate() 第 3 行,后面所有代码都未执行:

  • pi.bus.subscribe("anywhere:save_config") 未注册 → 面板保存无效
  • pi.bus.subscribe("anywhere:get_status") 未注册 → 面板查询无效
  • pi.events.on("session:turnEnded") / pi.events.on("workspace:changed") 未注册 → 无时间线同步

这正好解释「点了没反应」。

2(致命)面板调用了不存在的 window.pi.bus.send

views/panel.html:276, 287, 293:

await window.pi?.bus?.send('anywhere:save_config', parsed);
await window.pi?.bus?.send('anywhere:get_status');

宿主面板 preload 只暴露 window.pluginBridge(contextBridge.exposeInMainWorld("pluginBridge", bridge)):

{ invoke(channel, payload), send(channel, payload), on(event, handler), getDroppedFilePath(file) }

面板侧不存在 window.pi。因使用可选链 ?.,三次调用静默返回 undefined——不报错也不发送,这是「点击毫无反应」的直接原因。

此外宿主把面板 invoke 路由到插件导出的 onPanelInvoke(channel, payload)(plugin-host-process.js 的 case "panel.invoke"),而本插件未导出该函数。

3(致命)未导出宿主生命周期钩子

宿主契约:

module.exports = { onLoad, onUnload };
// globalThis.pi = buildApi();
// pluginModule = await loadPluginModule(entry);
// if (pluginModule?.onLoad) await pluginModule.onLoad();

本插件 main.js:379 只有模块底部自执行 activate().catch(...),完全没有 module.exports:

  • onUnload 缺失 → 卸载时不 WebSocket 断连、不清心跳定时器(连接泄漏)
  • onPanelInvoke 缺失 → 面板 invoke 全部失败

4(致命)WebSocket 端点与鉴权方式均错误

main.js:119, 131-140:

const wsUrl = `${serverUrl.replace(/^http/, "ws")...}/connector/v2/rpc`;
await pi.net.websocket.connect({
  url: wsUrl,
  headers: { "Authorization": `Connector ${id}:${token}` },
});

官方协议是两段式(我已从官方 PyPI 包 anywhere-cli 0.1.7.1 的 connector/runtime.py 独立验证):

步骤 端点 鉴权头
1. 换取 accessToken POST {server_url}/connector/auth Authorization: Connector <id>:<token>
2. 建立隧道 WSS {ws_url}/connector/ws Authorization: Bearer <accessToken>

官方源码:

# 步骤 1
response = await client.post(
    urljoin(self.config.server_url + "/", "connector/auth"),
    headers={"Authorization": f"Connector {self.config.connector_id}:{self.config.connector_token}"},
)
body = response.json(); access_token = body["accessToken"]   # 另有 expiresIn

# 步骤 2
ws_url = _ws_url(self.config.server_url, "/connector/ws")
async with websockets.connect(ws_url, additional_headers={"Authorization": f"Bearer {access_token}"}):

插件跳过了 token 交换,直接拿 Connector 凭据连一个不存在的端点 /connector/v2/rpc。即便端点对了,WS 握手用 Connector 而非 Bearer 也会被服务端拒绝关闭。

5(高)消息封装格式错误

main.js:274, 280 用 { jsonrpc: "2.0", ... }。官方封装是 { type: "response" | "notification", ... }(官方源码 jsonrpc 出现 0 次):

await self._send_json({"type": "notification", "method": method, "params": params})
payload = {"id": request_id, "type": "response", "ok": ok}

6(高)心跳方法名错误

main.js:287 发 "heartbeat";官方是 connector.heartbeat(_heartbeat_loop,间隔 20s)。

7(高)net.fetch 响应字段名错误

宿主 net.fetch 返回形状(app.asar 验证):

return { status: res.status, headers: headers2, bodyText };

没有 body / text。正确取值是 res.bodyText。

8(中)bus.subscribe 缺权限与主题声明

宿主 busSubscribe() 有两道校验:

this.assertPermission(loaded, "bus.subscribe");                          // ① 权限
if (!busSubscribeAllowed(loaded.manifest.contributes?.bus?.subscribe, name))
  throw apiError("PERMISSION_DENIED", `topic not declared for subscribe: ${name}`);  // ② 主题

而 manifest.json 只有 permissions: ["ui.view","net.fetch","net.websocket","desktop.control"],contributes 里没有 bus。所以即使缺陷 1 修好,bus 订阅仍会 PERMISSION_DENIED。

9(中)默认域名 api.agents-anywhere.com 无法解析

api.agents-anywhere.com  →  NXDOMAIN        ❌
web.agents-anywhere.com  →  115.190.179.255 ✅
agents-anywhere.com      →  115.191.44.213  ✅

插件默认填的 https://api.agents-anywhere.com 不存在(main.js:330、panel.html:177,225),用户不手动改就必然连不上。

10(低)activate() 会被触发两次

因缺陷 3(无 onLoad)+ 自执行 activate(),一旦补上 onLoad 就会初始化两次,两个 AnywhereClient 争抢同一 connector;服务端对同一 connector 只允许一个连接 → 表现为无限 403。需加幂等守卫。

11(低)listSessions() 对返回形状过严

main.js:26 的 Array.isArray(result) ? result : [] 遇到 {sessions:[...]} 等形状会静默返回空数组,手机端表现为「项目和会话没同步过来」。


依赖关系

缺陷1 (pi.storage 不存在) ─> activate() 崩溃 ─> bus/事件监听全部未注册
缺陷3 (无生命周期导出)    ─> 面板 invoke 全失败 + 卸载不断连
缺陷7 (net.fetch 字段名)  ─> 拿不到 accessToken
缺陷4 (端点/鉴权错误)     ─> 403 / 连接被拒
缺陷5+6 (封装/心跳名)     ─> 对端无法解析
缺陷8 (缺 bus 声明)       ─> 即便上面都修好仍 PERMISSION_DENIED
缺陷10 (双实例)           ─> 无限 403 循环

叠加缺陷:修任意一个都不足以连通。


已存在但未合并的修复

在 fork mazongYY/pi-desktop-plugins 中已有 PR #1(HanawaBanana,2026-09-23)——「fix(pi.agents-anywhere): 修复无法激活、面板无响应与连接 403」——结论与本次独立审计高度一致,改动 4 文件 +779/−106,并附带了打包好的 pi.agents-anywhere-1.0.4.piplug。

它当前 closed / merged=false,关闭理由为「目标仓库是 fork,改为向原始仓库 vastsa/pi-desktop-plugins 提交」,但本仓库中并不存在对应的修复 PR(仅 #59 是当初新增插件本身),官方 catalog 至今没有 v1.0.4。

即:修复已经写好了,却卡在 PR 流转中,从未进入市场。 建议优先把它捞回来合入并发布 1.0.4。

PR #1 覆盖缺陷 1–7、10、11;未覆盖缺陷 8(它把面板通信整体改走 pluginBridge.invoke + onPanelInvoke,从而绕开 bus)。


建议修复

  1. 持久化改 pi.plugin.getSettings/setSettings,回退 plugin.getDataPath() 下 JSON;都失败时返回默认配置而不抛错(保证 activate() 永不中断)。
  2. 面板三处 window.pi.bus.send → window.pluginBridge.invoke,并在 main.js 导出 onPanelInvoke(channel, payload)。
  3. 导出 module.exports = { onLoad, onUnload, onPanelInvoke };onUnload 中断连、清定时器。
  4. activate() 加幂等守卫。
  5. 协议改为两段式(/connector/auth → Connector 头;/connector/ws → Bearer),封装改 {type:...},心跳改 connector.heartbeat,并按 expiresIn(约 900s)续期。
  6. net.fetch 取 res.bodyText。
  7. 默认域名改为可用值,或强制要求填手机端下发的地址。
  8. listSessions 兼容 array / {sessions} / {items} / {data}。

对审核流程的建议

该包 review.decision = "approved"、risk = "medium",但连「能否加载并激活」都没有验证——activate() 第一行就抛 TypeError,plugins/data/<id>/ 从未生成。

建议增加自动冒烟测试:

  1. 真实宿主加载插件,断言 onLoad 无未捕获异常;
  2. 静态扫描插件中的 pi.xxx.yyy 调用,与宿主 buildApi() 白名单比对(仅此一项即可在打包阶段拦下缺陷 1);
  3. 断言面板不引用 window.pi(应为 window.pluginBridge)。

复现

  1. 安装 pi.agents-anywhere v1.0.3;
  2. 重启 PI-Desktop;
  3. 看 %USERPROFILE%\.pi-desktop\logs\app\plugin.log → Failed to activate: TypeError: ... main.js:329:42;
  4. 确认 plugins\data\pi.agents-anywhere\ 不存在;
  5. 打开「远程助手」面板填配置点保存 → 无任何反应、无报错。

本报告为独立审计:结论来自对宿主 app.asar 的 API 表面提取、插件源码逐行核对,以及官方 anywhere-cli 0.1.7.1 connector 源码交叉验证。

Activity

  1. muzimu217 commented on Oct 6, 2026

    @muzimu217
    Contributor

    核验结论:该插件不来自本仓库,本仓库无法修复。

    • 本仓库(vastsa/pi-desktop-plugins)的 plugins/ 目录与 catalog.json 中均无 pi.agents-anywhere。
    • issue 里的 provenance 显示它由 mazongYY/pi-desktop-plugins @ 62bb224 于 2026-09-21 通过 console-upload 直接发布到 plugins.aiuo.net,源头在作者的 fork。

    你的根因分析本身质量很高(pi.storage 不存在、面板 window.pi 不存在、缺 module.exports、WebSocket 鉴权协议不符),建议把这些反馈带到上游作者仓库 mazongYY/pi-desktop-plugins,或通过 plugins.aiuo.net 控制台联系作者下架/更新 1.0.4。另外当前 #62(市场站 /packages/ 全部返回 HTML)也会阻断该插件的重新下载,需一并留意。

  2. vastsa commented on Oct 6, 2026

    @vastsa
    Owner

    复核结论:不是宿主缺陷,且不属于本仓库的范围

    证据

    1. 插件运行时 API 里没有 pi.storage 命名空间。规范 docs/spec/07-plugins/03-plugin-api.md 列出的命名空间是 app / themes / plugin / commands / speech / ui / project / workspace(fs) / agent / models / provider OAuth / session / plugin-owned sessions / usage / session collaboration / clipboard+shell / browser / services / bus / net / desktop control / audio / keyboard;插件自身的持久化数据走 pi.plugin.getSettings() 与 pi.plugin.getDataPath()。在宿主仓库的规范与运行时/SDK 代码里检索 pi.storage 无任何命中。
      因此 pi.storage.get(...) 在任何宿主版本上都是 undefined —— 这是插件按不存在的 API 写的,不是宿主回归。
    2. 本仓库是目录与安装包分发仓库:不托管、不审查插件源码。catalog.json 里也没有 pi.agents-anywhere 条目(本仓库目录只收录 25 个插件),所以这里没有可改的源码或条目。

    正确去处

    1. 插件作者仓库:报告里的 provenance 已给出 mazongYY/pi-desktop-plugins@62bb2248…(console-upload@1.0.0)。把这份缺陷清单提到那里最有效——把第一行的 pi.storage 换成 pi.plugin.getSettings()/getDataPath()(并处理其余 11 项)就能让插件真正跑起来。
    2. 插件中心:该版本是从中心发布的,可以在 plugins.aiuo.net 的插件页面报问题,或请求对明显不可用的版本做撤回,避免其他用户继续装到。

    关闭理由:报告中的崩溃根因在插件自身源码(调用了不存在的宿主 API),本仓库没有对应代码或目录条目可改。结论与去处已记录在此。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions