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 |
现象
- 插件列表显示已启用(
enabled: true, status: ready);
- 每次 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)
- 面板粘贴配置后点保存/连接毫无反应,无报错,状态永远「离线」;
%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)。
建议修复
- 持久化改
pi.plugin.getSettings/setSettings,回退 plugin.getDataPath() 下 JSON;都失败时返回默认配置而不抛错(保证 activate() 永不中断)。
- 面板三处
window.pi.bus.send → window.pluginBridge.invoke,并在 main.js 导出 onPanelInvoke(channel, payload)。
- 导出
module.exports = { onLoad, onUnload, onPanelInvoke };onUnload 中断连、清定时器。
activate() 加幂等守卫。
- 协议改为两段式(
/connector/auth → Connector 头;/connector/ws → Bearer),封装改 {type:...},心跳改 connector.heartbeat,并按 expiresIn(约 900s)续期。
net.fetch 取 res.bodyText。
- 默认域名改为可用值,或强制要求填手机端下发的地址。
listSessions 兼容 array / {sessions} / {items} / {data}。
对审核流程的建议
该包 review.decision = "approved"、risk = "medium",但连「能否加载并激活」都没有验证——activate() 第一行就抛 TypeError,plugins/data/<id>/ 从未生成。
建议增加自动冒烟测试:
- 真实宿主加载插件,断言
onLoad 无未捕获异常;
- 静态扫描插件中的
pi.xxx.yyy 调用,与宿主 buildApi() 白名单比对(仅此一项即可在打包阶段拦下缺陷 1);
- 断言面板不引用
window.pi(应为 window.pluginBridge)。
复现
- 安装
pi.agents-anywhere v1.0.3;
- 重启 PI-Desktop;
- 看
%USERPROFILE%\.pi-desktop\logs\app\plugin.log → Failed to activate: TypeError: ... main.js:329:42;
- 确认
plugins\data\pi.agents-anywhere\ 不存在;
- 打开「远程助手」面板填配置点保存 → 无任何反应、无报错。
本报告为独立审计:结论来自对宿主 app.asar 的 API 表面提取、插件源码逐行核对,以及官方 anywhere-cli 0.1.7.1 connector 源码交叉验证。
pi.agents-anywherev1.0.3 完全不可用:activate()第一行即崩溃(pi.storage不存在)环境
pi.agents-anywherev1.0.3(市场当前最新)plugins.aiuo.net/packages/pi.agents-anywhere-1.0.3.piplug,shasum1a11b8ce…115fmazongYY/pi-desktop-plugins@62bb22489f43153360604d1a035008a7fce1290a,console-upload@1.0.0@ 2026-09-21T10:27:15Z现象
enabled: true,status: ready);%USERPROFILE%\.pi-desktop\plugins\data\pi.agents-anywhere\从未生成。结论:插件从未成功运行过。 业务逻辑一行都没执行。
根因
1(致命)
pi.storage不存在 →activate()立即崩溃main.js:329:宿主
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。持久化接口只有:
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.sendviews/panel.html:276, 287, 293:宿主面板 preload 只暴露
window.pluginBridge(contextBridge.exposeInMainWorld("pluginBridge", bridge)):面板侧不存在
window.pi。因使用可选链?.,三次调用静默返回undefined——不报错也不发送,这是「点击毫无反应」的直接原因。此外宿主把面板
invoke路由到插件导出的onPanelInvoke(channel, payload)(plugin-host-process.js的case "panel.invoke"),而本插件未导出该函数。3(致命)未导出宿主生命周期钩子
宿主契约:
本插件
main.js:379只有模块底部自执行activate().catch(...),完全没有module.exports:onUnload缺失 → 卸载时不 WebSocket 断连、不清心跳定时器(连接泄漏)onPanelInvoke缺失 → 面板invoke全部失败4(致命)WebSocket 端点与鉴权方式均错误
main.js:119, 131-140:官方协议是两段式(我已从官方 PyPI 包
anywhere-cli 0.1.7.1的connector/runtime.py独立验证):POST {server_url}/connector/authAuthorization: Connector <id>:<token>WSS {ws_url}/connector/wsAuthorization: Bearer <accessToken>官方源码:
插件跳过了 token 交换,直接拿
Connector凭据连一个不存在的端点/connector/v2/rpc。即便端点对了,WS 握手用Connector而非Bearer也会被服务端拒绝关闭。5(高)消息封装格式错误
main.js:274, 280用{ jsonrpc: "2.0", ... }。官方封装是{ type: "response" | "notification", ... }(官方源码jsonrpc出现 0 次):6(高)心跳方法名错误
main.js:287发"heartbeat";官方是connector.heartbeat(_heartbeat_loop,间隔 20s)。7(高)
net.fetch响应字段名错误宿主
net.fetch返回形状(app.asar 验证):没有
body/text。正确取值是res.bodyText。8(中)
bus.subscribe缺权限与主题声明宿主
busSubscribe()有两道校验:而
manifest.json只有permissions: ["ui.view","net.fetch","net.websocket","desktop.control"],contributes里没有bus。所以即使缺陷 1 修好,bus 订阅仍会PERMISSION_DENIED。9(中)默认域名
api.agents-anywhere.com无法解析插件默认填的
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:[...]}等形状会静默返回空数组,手机端表现为「项目和会话没同步过来」。依赖关系
叠加缺陷:修任意一个都不足以连通。
已存在但未合并的修复
在 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。
建议修复
pi.plugin.getSettings/setSettings,回退plugin.getDataPath()下 JSON;都失败时返回默认配置而不抛错(保证activate()永不中断)。window.pi.bus.send→window.pluginBridge.invoke,并在main.js导出onPanelInvoke(channel, payload)。module.exports = { onLoad, onUnload, onPanelInvoke };onUnload中断连、清定时器。activate()加幂等守卫。/connector/auth→Connector头;/connector/ws→Bearer),封装改{type:...},心跳改connector.heartbeat,并按expiresIn(约 900s)续期。net.fetch取res.bodyText。listSessions兼容array/{sessions}/{items}/{data}。对审核流程的建议
该包
review.decision = "approved"、risk = "medium",但连「能否加载并激活」都没有验证——activate()第一行就抛 TypeError,plugins/data/<id>/从未生成。建议增加自动冒烟测试:
onLoad无未捕获异常;pi.xxx.yyy调用,与宿主buildApi()白名单比对(仅此一项即可在打包阶段拦下缺陷 1);window.pi(应为window.pluginBridge)。复现
pi.agents-anywherev1.0.3;%USERPROFILE%\.pi-desktop\logs\app\plugin.log→Failed to activate: TypeError: ... main.js:329:42;plugins\data\pi.agents-anywhere\不存在;本报告为独立审计:结论来自对宿主 app.asar 的 API 表面提取、插件源码逐行核对,以及官方
anywhere-cli 0.1.7.1connector 源码交叉验证。