mu-agent-bridge 1.0.45 → 1.0.47
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/cli.js +20 -6
- package/dist/src-ts/plugin-manager.js +2 -0
- package/dist/src-ts/plugins/aun/index.js +15 -4
- package/dist/tests-ts/aun-plugin.test.js +33 -0
- package/dist/tests-ts/plugin-manager.test.js +5 -4
- package/package.json +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +15 -4
package/dist/src-ts/cli.js
CHANGED
|
@@ -173,18 +173,28 @@ async function startDaemon() {
|
|
|
173
173
|
child.unref();
|
|
174
174
|
}
|
|
175
175
|
async function startUpdatedDaemon() {
|
|
176
|
-
|
|
176
|
+
let child;
|
|
177
|
+
const env = { ...process.env, AGENT_BRIDGE_CORE_UPDATE_CHILD: "1" };
|
|
178
|
+
if (process.platform === "win32" && !env.AGENT_BRIDGE_TOKEN) {
|
|
179
|
+
try {
|
|
180
|
+
env.AGENT_BRIDGE_TOKEN = (await (0, promises_1.readFile)((0, node_path_1.resolve)(process.env.USERPROFILE ?? (0, node_os_1.homedir)(), ".mab", "token.txt"), "utf8")).trim();
|
|
181
|
+
env.AGENT_BRIDGE_HOST ??= "0.0.0.0";
|
|
182
|
+
env.AGENT_BRIDGE_PORT ??= "26107";
|
|
183
|
+
}
|
|
184
|
+
catch { /* 当前 Windows 安装未使用 mab 启动器 */ }
|
|
185
|
+
}
|
|
186
|
+
child = (0, node_child_process_1.spawn)(process.execPath, [(0, node_path_1.resolve)(__dirname, "cli.js"), "start"], { detached: true, stdio: "ignore", windowsHide: true, env });
|
|
177
187
|
let launchFailed = false;
|
|
178
|
-
child
|
|
179
|
-
child
|
|
188
|
+
child?.once("error", () => { launchFailed = true; });
|
|
189
|
+
child?.unref();
|
|
180
190
|
const healthy = async () => {
|
|
181
191
|
for (let attempt = 0; attempt < 30; attempt++) {
|
|
182
|
-
if (launchFailed || child.exitCode !== null || child.signalCode !== null)
|
|
192
|
+
if (launchFailed || child && (child.exitCode !== null || child.signalCode !== null))
|
|
183
193
|
return false;
|
|
184
194
|
const address = await daemonAddress();
|
|
185
195
|
try {
|
|
186
196
|
const response = await fetch(`http://127.0.0.1:${address.port}/api/health`, {
|
|
187
|
-
headers:
|
|
197
|
+
headers: env.AGENT_BRIDGE_TOKEN ? { authorization: `Bearer ${env.AGENT_BRIDGE_TOKEN}` } : undefined,
|
|
188
198
|
signal: AbortSignal.timeout(1000),
|
|
189
199
|
});
|
|
190
200
|
if (response.ok && (await response.json()).status === "ok")
|
|
@@ -195,7 +205,7 @@ async function startUpdatedDaemon() {
|
|
|
195
205
|
}
|
|
196
206
|
return false;
|
|
197
207
|
};
|
|
198
|
-
return {
|
|
208
|
+
return { healthy };
|
|
199
209
|
}
|
|
200
210
|
async function stop() {
|
|
201
211
|
await stopDaemon();
|
|
@@ -371,6 +381,10 @@ async function main() {
|
|
|
371
381
|
if (args[0] === "--self-test")
|
|
372
382
|
return selfTest(args[1] ?? "hi");
|
|
373
383
|
if (args[0] === "--serve" || args[0] === "start") {
|
|
384
|
+
if (process.env.AGENT_BRIDGE_CORE_UPDATE_CHILD === "1") {
|
|
385
|
+
delete process.env.AGENT_BRIDGE_CORE_UPDATE_CHILD;
|
|
386
|
+
return serve();
|
|
387
|
+
}
|
|
374
388
|
const logs = new log_store_1.LogStore((0, node_path_1.resolve)(stateDirectory(), "logs"));
|
|
375
389
|
let launched;
|
|
376
390
|
const updated = await (0, core_updater_1.updateCoreAtStartup)({
|
|
@@ -386,6 +386,8 @@ class PluginManager {
|
|
|
386
386
|
const trustedChannel = loaded.record.type === "channel" && (id === "aun" || id === "feishu");
|
|
387
387
|
if (trustedChannel && (typeof message.peerId !== "string" || !message.peerId.trim() || [message.peerId, message.recipientId, message.chatId, message.threadId].some(value => value !== undefined && (typeof value !== "string" || value.length > 512 || /[\u0000-\u001f*]/.test(value)))))
|
|
388
388
|
throw new agent_1.AgentError("通道来源标识无效");
|
|
389
|
+
if (id === "aun" && !message.agentPlugin)
|
|
390
|
+
throw new agent_1.AgentError("AUN 消息必须明确关联 Agent");
|
|
389
391
|
const target = message.agentPlugin || "qwenwork";
|
|
390
392
|
if (/^\/sessions(?:\s|$)/.test(message.text.trimStart()) || (0, session_management_1.isDailySummaryRequest)(message.text)) {
|
|
391
393
|
if (!trustedChannel)
|
|
@@ -771,7 +771,8 @@ class AunPlugin {
|
|
|
771
771
|
}
|
|
772
772
|
};
|
|
773
773
|
async onMessage(payload, aid) {
|
|
774
|
-
|
|
774
|
+
const agentPlugin = this.bindings[aid];
|
|
775
|
+
if (!this.acceptingMessages || !agentPlugin)
|
|
775
776
|
return;
|
|
776
777
|
const message = (0, messages_1.normalizeAunMessage)(payload, aid, typeof this.config.allow_from === "string" ? this.config.allow_from : undefined);
|
|
777
778
|
if (!message || !this.seen.add(message.messageId ? `${aid}:${message.messageId}` : ""))
|
|
@@ -787,7 +788,7 @@ class AunPlugin {
|
|
|
787
788
|
}
|
|
788
789
|
return;
|
|
789
790
|
}
|
|
790
|
-
const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid));
|
|
791
|
+
const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid, agentPlugin));
|
|
791
792
|
this.pendingMessages.set(sourceKey, current);
|
|
792
793
|
try {
|
|
793
794
|
await current;
|
|
@@ -797,12 +798,22 @@ class AunPlugin {
|
|
|
797
798
|
this.pendingMessages.delete(sourceKey);
|
|
798
799
|
}
|
|
799
800
|
}
|
|
800
|
-
async processMessage(message, aid) {
|
|
801
|
+
async processMessage(message, aid, agentPlugin) {
|
|
801
802
|
let replyAttempted = false;
|
|
802
803
|
const client = this.clients.get(aid)?.client;
|
|
803
804
|
let attachments = [];
|
|
804
805
|
let uploadedItems;
|
|
805
806
|
let uploadedRefs;
|
|
807
|
+
if (this.context?.pluginStatus(agentPlugin) !== "ready") {
|
|
808
|
+
this.context?.logger.warn("AUN 关联 Agent 尚未就绪,消息未转发", { aid, agentPlugin, status: this.context?.pluginStatus(agentPlugin) });
|
|
809
|
+
try {
|
|
810
|
+
await this.sendText(aid, message.peerId, `绑定的 Agent 插件 ${agentPlugin} 尚未就绪,消息未转发;AID 关联保持不变。`, message.threadId);
|
|
811
|
+
}
|
|
812
|
+
catch (error) {
|
|
813
|
+
this.context?.logger.warn("AUN Agent 未就绪提示发送失败", { aid, agentPlugin, error: safeError(error) });
|
|
814
|
+
}
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
806
817
|
try {
|
|
807
818
|
if (message.attachmentError)
|
|
808
819
|
throw new Error(message.attachmentError);
|
|
@@ -823,7 +834,7 @@ class AunPlugin {
|
|
|
823
834
|
const channelMessage = {
|
|
824
835
|
channel: "aun", recipientId: aid, peerId: message.peerId, messageId: message.messageId, text: message.text, threadId: message.threadId,
|
|
825
836
|
...(attachments.length ? { attachments } : {}),
|
|
826
|
-
agentPlugin
|
|
837
|
+
agentPlugin,
|
|
827
838
|
reply: async (text) => {
|
|
828
839
|
if (!text.trim())
|
|
829
840
|
return;
|
|
@@ -74,6 +74,37 @@ async function runAunCommand(routes, response, argv) {
|
|
|
74
74
|
await (0, promises_1.rm)(root, { recursive: true, force: true });
|
|
75
75
|
}
|
|
76
76
|
});
|
|
77
|
+
(0, node_test_1.default)("AUN 绑定目标未就绪时不调用插件且不改动其它 AID", async () => {
|
|
78
|
+
const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "aun-unready-binding-"));
|
|
79
|
+
let receive;
|
|
80
|
+
const sent = [];
|
|
81
|
+
let published = false;
|
|
82
|
+
const sdk = {
|
|
83
|
+
AIDStore: class {
|
|
84
|
+
list() { return { ok: true, data: { identities: [{ aid: "selected.agentid.cn" }, { aid: "other.agentid.cn" }] } }; }
|
|
85
|
+
load(aid) { return { ok: true, data: { aid } }; }
|
|
86
|
+
close() { }
|
|
87
|
+
},
|
|
88
|
+
AUNClient: class {
|
|
89
|
+
async connect() { }
|
|
90
|
+
async close() { }
|
|
91
|
+
on(_event, handler) { receive = handler; return { unsubscribe() { } }; }
|
|
92
|
+
async call(_method, params) { sent.push(params); }
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
const plugin = new aun_1.AunPlugin({ sdk, ingress: { publish: async () => { published = true; } } });
|
|
96
|
+
try {
|
|
97
|
+
await plugin.activate({ dataDir: root, config: { bindings: { "selected.agentid.cn": "qwenwork", "other.agentid.cn": "workbuddy" } }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "discovered", http: { register() { }, unregister() { } } });
|
|
98
|
+
await receive({ from: "peer.agentid.cn", message_id: "unready-1", payload: { text: "不要转发" } });
|
|
99
|
+
strict_1.default.equal(published, false);
|
|
100
|
+
strict_1.default.deepEqual(sent, [{ to: "peer.agentid.cn", payload: { type: "text", text: "绑定的 Agent 插件 qwenwork 尚未就绪,消息未转发;AID 关联保持不变。" }, encrypt: true }]);
|
|
101
|
+
strict_1.default.deepEqual((await plugin.health()).details?.bindings, { "selected.agentid.cn": "qwenwork", "other.agentid.cn": "workbuddy" });
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
await plugin.deactivate("stop");
|
|
105
|
+
await (0, promises_1.rm)(root, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
77
108
|
async function withAunAttachmentPlugin(run, publish, config = { allow_from: "peer.agentid.cn", bindings: { "local.agentid.cn": "qwenwork" } }) {
|
|
78
109
|
const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "aun-attachments-plugin-"));
|
|
79
110
|
let handler;
|
|
@@ -405,6 +436,8 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
|
|
|
405
436
|
const plugin = new aun_1.AunPlugin({ sdk, ingress: { publish: async () => undefined } });
|
|
406
437
|
try {
|
|
407
438
|
await plugin.activate({ dataDir: root, config: { bindings: { [aids[0]]: "qwenwork", [aids[1]]: "qwenwork" }, binding_times: { [aids[0]]: "old-1", [aids[1]]: "old-2" } }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready", http: { register() { }, unregister() { } } });
|
|
439
|
+
await plugin.identity.bind("qwenwork", aids[0], 26107);
|
|
440
|
+
strict_1.default.deepEqual((await plugin.health()).details?.bindings, { [aids[0]]: "qwenwork", [aids[1]]: "qwenwork" });
|
|
408
441
|
await plugin.identity.bind("workbuddy", aids[0], 26107);
|
|
409
442
|
strict_1.default.deepEqual((await plugin.health()).details?.bindings, { [aids[0]]: "workbuddy", [aids[1]]: "qwenwork" });
|
|
410
443
|
await plugin.identity.unbind("workbuddy", aids[0], 26107);
|
|
@@ -605,16 +605,17 @@ const channel_sessions_1 = require("../src-ts/channel-sessions");
|
|
|
605
605
|
await manager.start("aun");
|
|
606
606
|
let reply = "";
|
|
607
607
|
const ingress = globalThis.testIngress;
|
|
608
|
-
await ingress.publish({ channel: "aun", peerId: "peer.agentid.pub", text: "
|
|
608
|
+
await strict_1.default.rejects(ingress.publish({ channel: "aun", peerId: "peer.agentid.pub", text: "无目标", reply: async () => { } }), /必须明确关联 Agent/);
|
|
609
|
+
await ingress.publish({ channel: "aun", agentPlugin: "qwenwork", peerId: "peer.agentid.pub", text: "你好", reply: async (text) => { reply = text; } });
|
|
609
610
|
strict_1.default.equal(reply, "回复:你好");
|
|
610
611
|
const forwarded = [];
|
|
611
|
-
await ingress.publish({ channel: "aun", peerId: "peer.agentid.pub", text: "中间消息", reply: async (text) => { forwarded.push(text); } });
|
|
612
|
+
await ingress.publish({ channel: "aun", agentPlugin: "qwenwork", peerId: "peer.agentid.pub", text: "中间消息", reply: async (text) => { forwarded.push(text); } });
|
|
612
613
|
strict_1.default.deepEqual(forwarded, ["请补充视频时长", "已完成"]);
|
|
613
614
|
const images = [];
|
|
614
|
-
await ingress.publish({ channel: "aun", peerId: "peer.agentid.pub", text: "画猫", reply: async () => { strict_1.default.fail("图片不应作为纯文本丢弃"); }, replyAttachments: async (files, text) => { images.push(...files); strict_1.default.equal(text, "回复:画猫"); } });
|
|
615
|
+
await ingress.publish({ channel: "aun", agentPlugin: "qwenwork", peerId: "peer.agentid.pub", text: "画猫", reply: async () => { strict_1.default.fail("图片不应作为纯文本丢弃"); }, replyAttachments: async (files, text) => { images.push(...files); strict_1.default.equal(text, "回复:画猫"); } });
|
|
615
616
|
strict_1.default.equal(images[0].filename, "cat.png");
|
|
616
617
|
strict_1.default.deepEqual([...images[0].data], [137, 80, 78, 71, 13, 10, 26, 10]);
|
|
617
|
-
await strict_1.default.rejects(ingress.publish({ channel: "aun", peerId: "peer", text: "画猫", reply: async (text) => { strict_1.default.match(text, /不支持附件回传/); } }), /不支持附件回传/);
|
|
618
|
+
await strict_1.default.rejects(ingress.publish({ channel: "aun", agentPlugin: "qwenwork", peerId: "peer", text: "画猫", reply: async (text) => { strict_1.default.match(text, /不支持附件回传/); } }), /不支持附件回传/);
|
|
618
619
|
await manager.stop("aun");
|
|
619
620
|
await manager.stop("qwenwork");
|
|
620
621
|
}
|
package/package.json
CHANGED
|
@@ -771,7 +771,8 @@ class AunPlugin {
|
|
|
771
771
|
}
|
|
772
772
|
};
|
|
773
773
|
async onMessage(payload, aid) {
|
|
774
|
-
|
|
774
|
+
const agentPlugin = this.bindings[aid];
|
|
775
|
+
if (!this.acceptingMessages || !agentPlugin)
|
|
775
776
|
return;
|
|
776
777
|
const message = (0, messages_1.normalizeAunMessage)(payload, aid, typeof this.config.allow_from === "string" ? this.config.allow_from : undefined);
|
|
777
778
|
if (!message || !this.seen.add(message.messageId ? `${aid}:${message.messageId}` : ""))
|
|
@@ -787,7 +788,7 @@ class AunPlugin {
|
|
|
787
788
|
}
|
|
788
789
|
return;
|
|
789
790
|
}
|
|
790
|
-
const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid));
|
|
791
|
+
const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid, agentPlugin));
|
|
791
792
|
this.pendingMessages.set(sourceKey, current);
|
|
792
793
|
try {
|
|
793
794
|
await current;
|
|
@@ -797,12 +798,22 @@ class AunPlugin {
|
|
|
797
798
|
this.pendingMessages.delete(sourceKey);
|
|
798
799
|
}
|
|
799
800
|
}
|
|
800
|
-
async processMessage(message, aid) {
|
|
801
|
+
async processMessage(message, aid, agentPlugin) {
|
|
801
802
|
let replyAttempted = false;
|
|
802
803
|
const client = this.clients.get(aid)?.client;
|
|
803
804
|
let attachments = [];
|
|
804
805
|
let uploadedItems;
|
|
805
806
|
let uploadedRefs;
|
|
807
|
+
if (this.context?.pluginStatus(agentPlugin) !== "ready") {
|
|
808
|
+
this.context?.logger.warn("AUN 关联 Agent 尚未就绪,消息未转发", { aid, agentPlugin, status: this.context?.pluginStatus(agentPlugin) });
|
|
809
|
+
try {
|
|
810
|
+
await this.sendText(aid, message.peerId, `绑定的 Agent 插件 ${agentPlugin} 尚未就绪,消息未转发;AID 关联保持不变。`, message.threadId);
|
|
811
|
+
}
|
|
812
|
+
catch (error) {
|
|
813
|
+
this.context?.logger.warn("AUN Agent 未就绪提示发送失败", { aid, agentPlugin, error: safeError(error) });
|
|
814
|
+
}
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
806
817
|
try {
|
|
807
818
|
if (message.attachmentError)
|
|
808
819
|
throw new Error(message.attachmentError);
|
|
@@ -823,7 +834,7 @@ class AunPlugin {
|
|
|
823
834
|
const channelMessage = {
|
|
824
835
|
channel: "aun", recipientId: aid, peerId: message.peerId, messageId: message.messageId, text: message.text, threadId: message.threadId,
|
|
825
836
|
...(attachments.length ? { attachments } : {}),
|
|
826
|
-
agentPlugin
|
|
837
|
+
agentPlugin,
|
|
827
838
|
reply: async (text) => {
|
|
828
839
|
if (!text.trim())
|
|
829
840
|
return;
|