mu-agent-bridge 1.0.46 → 1.0.53
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/packages/mu-agent-bridge-1.0.53.tgz +0 -0
- package/dist/src-ts/cli.js +1 -1
- package/dist/src-ts/plugin-manager.js +2 -0
- package/dist/src-ts/plugins/aun/index.js +15 -4
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +1 -1
- package/dist/tests-ts/installer-assets.test.js +7 -0
- package/dist/tests-ts/plugin-manager.test.js +5 -4
- package/dist/tests-ts/workbuddy-plugin.test.js +16 -0
- package/package.json +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +15 -4
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +1 -1
- package/plugins/workbuddy/package.json +1 -1
|
Binary file
|
package/dist/src-ts/cli.js
CHANGED
|
@@ -194,7 +194,7 @@ async function startUpdatedDaemon() {
|
|
|
194
194
|
const address = await daemonAddress();
|
|
195
195
|
try {
|
|
196
196
|
const response = await fetch(`http://127.0.0.1:${address.port}/api/health`, {
|
|
197
|
-
headers:
|
|
197
|
+
headers: env.AGENT_BRIDGE_TOKEN ? { authorization: `Bearer ${env.AGENT_BRIDGE_TOKEN}` } : undefined,
|
|
198
198
|
signal: AbortSignal.timeout(1000),
|
|
199
199
|
});
|
|
200
200
|
if (response.ok && (await response.json()).status === "ok")
|
|
@@ -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;
|
|
@@ -108,7 +108,7 @@ class WorkbuddyCdpClient {
|
|
|
108
108
|
const before = await this.evaluate(`(() => { const state = ${snapshot}; const loginRequired = [...document.querySelectorAll('a,button,[role="button"]')].some(item => /^(?:登录|立即登录|sign in)$/i.test(item.textContent?.trim() ?? '')); return { ...state, loginRequired }; })()`);
|
|
109
109
|
if (before.loginRequired)
|
|
110
110
|
throw new Error("WorkBuddy 尚未登录,请先在桌面端登录后重试");
|
|
111
|
-
if (!await this.evaluate(`(() => { const button = [...document.querySelectorAll('button,[role="button"]')].find(
|
|
111
|
+
if (!await this.evaluate(`(() => { const button = [...document.querySelectorAll('button,[role="button"]')].find(item => { const label = [item.innerText, item.textContent, item.getAttribute('aria-label'), item.getAttribute('title'), item.getAttribute('data-testid')].filter(Boolean).map(value => String(value).normalize('NFKC').replace(/\\s+/g, ' ').trim().toLowerCase()); return label.some(value => /^(?:新建任务|新建会话|新建对话|开始新任务|new (?:task|chat|conversation))$/.test(value)) && (!item.getClientRects || item.getClientRects().length > 0); }); if (!button || button.disabled || button.getAttribute('aria-disabled') === 'true') return false; button.click(); return true; })()`))
|
|
112
112
|
throw new Error("WorkBuddy 新建任务入口未找到或不可用");
|
|
113
113
|
// WorkBuddy 首页不分配任务 ID;此时仅接受无选中任务的空白首页。
|
|
114
114
|
await this.waitFor(`(() => { const state = ${snapshot}; const blank = state.editor !== null && state.editor.trim() === '' && state.messages === 0; return blank && ((state.conversationId && state.conversationId !== ${JSON.stringify(before.conversationId)}) || state.url !== ${JSON.stringify(before.url)} || (state.home && !state.selected)); })()`, "WorkBuddy 新任务状态校验失败");
|
|
@@ -52,6 +52,9 @@ const node_util_1 = require("node:util");
|
|
|
52
52
|
strict_1.default.match(setup, /\[void\]\$grid\.EndEdit\(\)/);
|
|
53
53
|
strict_1.default.match(setup, /\$grid\.Rows\.Add\(\$true, \$aid, \$null\)/);
|
|
54
54
|
strict_1.default.match(setup, /显示详细信息/);
|
|
55
|
+
strict_1.default.match(setup, /WizardBusyTimer/);
|
|
56
|
+
strict_1.default.match(setup, /timer\.Interval = 120/);
|
|
57
|
+
strict_1.default.match(setup, /⟳/);
|
|
55
58
|
strict_1.default.match(setup, /环境与桥接服务/);
|
|
56
59
|
strict_1.default.match(setup, /Get-ChildItem -LiteralPath \$root -Filter \$fileName -File -Recurse/);
|
|
57
60
|
strict_1.default.match(setup, /api\/plugins\/\$agent\/config/);
|
|
@@ -105,6 +108,10 @@ const node_util_1 = require("node:util");
|
|
|
105
108
|
strict_1.default.match(setup, /taskkill\.exe \/PID/);
|
|
106
109
|
strict_1.default.match(setup, /function Stop-BridgeDaemons/);
|
|
107
110
|
strict_1.default.match(setup, /api\/plugins\/aun\/commands/);
|
|
111
|
+
const bindingFlow = setup.slice(setup.indexOf('function Invoke-GuiBindingPlan'), setup.indexOf('\ntry {\n Initialize-WizardGui'));
|
|
112
|
+
strict_1.default.match(bindingFlow, /api\/plugins\/aun\/identities/);
|
|
113
|
+
strict_1.default.match(bindingFlow, /Method Patch -Uri "http:\/\/127\.0\.0\.1:\$port\/api\/plugins\/\$agent\/aid"/);
|
|
114
|
+
strict_1.default.doesNotMatch(bindingFlow, /Invoke-AunCommand @\('(?:list|bind)'/);
|
|
108
115
|
strict_1.default.doesNotMatch(setup, /Invoke-Mab/);
|
|
109
116
|
strict_1.default.match(setup, /Read-Host '输入 YES 关闭 WorkBuddy 并以 CDP 模式重启'\)\.Trim\(\) -ne 'YES'/);
|
|
110
117
|
strict_1.default.match(setup, /确认改为 \$agent?输入 YES 继续\"\)\.Trim\(\) -ne 'YES'/);
|
|
@@ -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
|
}
|
|
@@ -956,6 +956,22 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
956
956
|
await client.createTask();
|
|
957
957
|
}
|
|
958
958
|
});
|
|
959
|
+
(0, node_test_1.default)("workbuddy CDP 通过无障碍名称识别无可见文字的新建任务图标", async () => {
|
|
960
|
+
let conversationId = "old-conversation";
|
|
961
|
+
let messages = 1;
|
|
962
|
+
const button = { textContent: "", getAttribute: (name) => name === "aria-label" ? "新建任务" : null, disabled: false, click: () => { conversationId = null; messages = 0; }, getClientRects: () => [{}] };
|
|
963
|
+
const editor = { innerText: "今天帮你做些什么?", querySelector: () => conversationId === null ? {} : null, cloneNode: () => ({ textContent: "", querySelectorAll: () => [] }) };
|
|
964
|
+
const document = {
|
|
965
|
+
querySelectorAll: (selector) => selector === '.cr-agent[data-message-id]' ? Array(messages).fill({}) : selector === '[data-conversation-id]' ? conversationId ? [{ className: "_selected_old", getAttribute: () => conversationId }] : [] : [button],
|
|
966
|
+
querySelector: () => editor,
|
|
967
|
+
};
|
|
968
|
+
const client = new cdp_client_1.WorkbuddyCdpClient({ timeoutMs: 1 });
|
|
969
|
+
const internal = client;
|
|
970
|
+
internal.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document, location: { href: "app://workbuddy/task/old" } });
|
|
971
|
+
await client.createTask();
|
|
972
|
+
strict_1.default.equal(conversationId, null);
|
|
973
|
+
strict_1.default.equal(messages, 0);
|
|
974
|
+
});
|
|
959
975
|
(0, node_test_1.default)("workbuddy CDP 新建不接受缺少任务身份的旧消息界面直接清空", async () => {
|
|
960
976
|
let messages = 1;
|
|
961
977
|
const button = { textContent: "新建任务", disabled: false, getAttribute: () => null, click: () => { messages = 0; } };
|
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;
|
|
@@ -108,7 +108,7 @@ class WorkbuddyCdpClient {
|
|
|
108
108
|
const before = await this.evaluate(`(() => { const state = ${snapshot}; const loginRequired = [...document.querySelectorAll('a,button,[role="button"]')].some(item => /^(?:登录|立即登录|sign in)$/i.test(item.textContent?.trim() ?? '')); return { ...state, loginRequired }; })()`);
|
|
109
109
|
if (before.loginRequired)
|
|
110
110
|
throw new Error("WorkBuddy 尚未登录,请先在桌面端登录后重试");
|
|
111
|
-
if (!await this.evaluate(`(() => { const button = [...document.querySelectorAll('button,[role="button"]')].find(
|
|
111
|
+
if (!await this.evaluate(`(() => { const button = [...document.querySelectorAll('button,[role="button"]')].find(item => { const label = [item.innerText, item.textContent, item.getAttribute('aria-label'), item.getAttribute('title'), item.getAttribute('data-testid')].filter(Boolean).map(value => String(value).normalize('NFKC').replace(/\\s+/g, ' ').trim().toLowerCase()); return label.some(value => /^(?:新建任务|新建会话|新建对话|开始新任务|new (?:task|chat|conversation))$/.test(value)) && (!item.getClientRects || item.getClientRects().length > 0); }); if (!button || button.disabled || button.getAttribute('aria-disabled') === 'true') return false; button.click(); return true; })()`))
|
|
112
112
|
throw new Error("WorkBuddy 新建任务入口未找到或不可用");
|
|
113
113
|
// WorkBuddy 首页不分配任务 ID;此时仅接受无选中任务的空白首页。
|
|
114
114
|
await this.waitFor(`(() => { const state = ${snapshot}; const blank = state.editor !== null && state.editor.trim() === '' && state.messages === 0; return blank && ((state.conversationId && state.conversationId !== ${JSON.stringify(before.conversationId)}) || state.url !== ${JSON.stringify(before.url)} || (state.home && !state.selected)); })()`, "WorkBuddy 新任务状态校验失败");
|