mu-agent-bridge 1.0.31 → 1.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src-ts/plugin-manager.js +17 -0
- package/dist/src-ts/plugins/qwenwork/index.js +1 -0
- package/dist/tests-ts/plugin-manager.test.js +35 -0
- package/dist/tests-ts/qwenwork-plugin.test.js +20 -0
- package/package.json +1 -1
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +1 -0
- package/plugins/qwenwork/package.json +1 -1
- package/plugins/qwenwork/plugin.json +1 -1
|
@@ -25,6 +25,7 @@ class PluginManager {
|
|
|
25
25
|
options;
|
|
26
26
|
controlUrl;
|
|
27
27
|
plugins = new Map();
|
|
28
|
+
upgrades = new Map();
|
|
28
29
|
httpRoutes = new Map();
|
|
29
30
|
root;
|
|
30
31
|
messages = [];
|
|
@@ -161,6 +162,22 @@ class PluginManager {
|
|
|
161
162
|
})]);
|
|
162
163
|
}
|
|
163
164
|
async upgradePackage(id, packageName = `agent-bridge-${id}`, version) {
|
|
165
|
+
const pending = this.upgrades.get(id);
|
|
166
|
+
if (pending) {
|
|
167
|
+
if (pending.packageName === packageName && pending.version === version)
|
|
168
|
+
return pending.task;
|
|
169
|
+
throw new agent_1.AgentError("插件正在升级,请等待完成后再请求其他版本");
|
|
170
|
+
}
|
|
171
|
+
const task = this.performUpgrade(id, packageName, version);
|
|
172
|
+
this.upgrades.set(id, { packageName, version, task });
|
|
173
|
+
try {
|
|
174
|
+
return await task;
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
this.upgrades.delete(id);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async performUpgrade(id, packageName, version) {
|
|
164
181
|
if (!/^agent-bridge-[a-z0-9-]+$/.test(packageName))
|
|
165
182
|
throw new agent_1.AgentError("仅支持 agent-bridge-* npm 包名");
|
|
166
183
|
if (version && !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version))
|
|
@@ -72,6 +72,41 @@ const channel_sessions_1 = require("../src-ts/channel-sessions");
|
|
|
72
72
|
await (0, promises_1.rm)(root, { recursive: true, force: true });
|
|
73
73
|
}
|
|
74
74
|
});
|
|
75
|
+
(0, node_test_1.default)("相同插件版本的并发升级只下载和重载一次", async () => {
|
|
76
|
+
const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-concurrent-update-"));
|
|
77
|
+
const name = "agent-bridge-demo";
|
|
78
|
+
const manager = new plugin_manager_1.PluginManager({ pluginsDir: (0, node_path_1.join)(root, "plugins"), stateDir: (0, node_path_1.join)(root, "state"),
|
|
79
|
+
versions: new version_manager_1.VersionManager({ stateDir: (0, node_path_1.join)(root, "state"), npm: async () => '"1.0.1"' }), publisherResolver: async () => "m2lihui" });
|
|
80
|
+
let release;
|
|
81
|
+
const gate = new Promise(resolve => { release = resolve; });
|
|
82
|
+
let downloads = 0;
|
|
83
|
+
let results;
|
|
84
|
+
try {
|
|
85
|
+
for (const [dir, version] of [[(0, node_path_1.join)(root, "plugins", "demo"), "1.0.0"], [(0, node_path_1.join)(root, "download", "node_modules", name), "1.0.1"]]) {
|
|
86
|
+
await (0, promises_1.mkdir)(dir, { recursive: true });
|
|
87
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(dir, "package.json"), JSON.stringify({ name, version, agentBridge: { plugin: "./plugin.json" } }));
|
|
88
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(dir, "plugin.json"), JSON.stringify({ id: "demo", version, entry: "index.js", apiVersion: "1", capabilities: [] }));
|
|
89
|
+
await (0, promises_1.writeFile)((0, node_path_1.join)(dir, "index.js"), "module.exports={activate:async()=>{},deactivate:async()=>{},health:async()=>({status:'ready'})};");
|
|
90
|
+
}
|
|
91
|
+
await manager.discover();
|
|
92
|
+
await manager.start("demo");
|
|
93
|
+
manager.downloadPackage = async () => { downloads++; await gate; return (0, node_path_1.join)(root, "download"); };
|
|
94
|
+
results = Promise.allSettled([manager.upgradePackage("demo", name, "1.0.1"), manager.upgradePackage("demo", name, "1.0.1")]);
|
|
95
|
+
for (let i = 0; i < 100 && downloads === 0; i++)
|
|
96
|
+
await new Promise(resolve => setTimeout(resolve, 1));
|
|
97
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
98
|
+
strict_1.default.equal(downloads, 1);
|
|
99
|
+
release();
|
|
100
|
+
strict_1.default.ok((await results).every(result => result.status === "fulfilled"));
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
release();
|
|
104
|
+
await results;
|
|
105
|
+
if (manager.get("demo")?.instance)
|
|
106
|
+
await manager.stop("demo");
|
|
107
|
+
await (0, promises_1.rm)(root, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
75
110
|
(0, node_test_1.default)("随包插件检查独立 npm 更新,升级后重启仍选新副本,失败恢复内置版本", async () => {
|
|
76
111
|
const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-bundled-update-"));
|
|
77
112
|
const stateDir = (0, node_path_1.join)(root, "state");
|
|
@@ -378,6 +378,26 @@ const cdp_client_1 = require("../src-ts/plugins/qwenwork/cdp-client");
|
|
|
378
378
|
await plugin.deactivate("stop");
|
|
379
379
|
}
|
|
380
380
|
});
|
|
381
|
+
(0, node_test_1.default)("千问停用等待进行中的健康检查,避免重载重复发送心跳", async () => {
|
|
382
|
+
let release;
|
|
383
|
+
const plugin = new qwenwork_1.QwenworkPlugin({ httpPort: 0, client: {
|
|
384
|
+
connect: async () => undefined, close: () => undefined, connected: () => true,
|
|
385
|
+
send: async () => undefined, direct: async () => "回复",
|
|
386
|
+
healthCheck: async () => undefined, chatHeartbeat: async () => { await new Promise(resolve => { release = resolve; }); },
|
|
387
|
+
} });
|
|
388
|
+
await plugin.activate({ config: {}, dataDir: "." });
|
|
389
|
+
const internal = plugin;
|
|
390
|
+
for (let index = 0; index < 100 && !internal.checking; index++)
|
|
391
|
+
await new Promise(resolve => setTimeout(resolve, 1));
|
|
392
|
+
const stopping = plugin.deactivate("reload");
|
|
393
|
+
let finished = false;
|
|
394
|
+
void stopping.then(() => { finished = true; });
|
|
395
|
+
await new Promise(resolve => setTimeout(resolve, 5));
|
|
396
|
+
strict_1.default.equal(finished, false);
|
|
397
|
+
release();
|
|
398
|
+
await stopping;
|
|
399
|
+
strict_1.default.equal(finished, true);
|
|
400
|
+
});
|
|
381
401
|
(0, node_test_1.default)("定时健康检查失败时只执行一次桌面恢复并重新连接", async () => {
|
|
382
402
|
let healthy = false;
|
|
383
403
|
let connected = true;
|
package/package.json
CHANGED