mu-agent-bridge 1.0.71 → 1.0.78
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/README.md +17 -10
- package/dist/src-ts/agent.js +35 -0
- package/dist/src-ts/channel-sessions.js +3 -2
- package/dist/src-ts/cli.js +67 -37
- package/dist/src-ts/control-server.js +12 -30
- package/dist/src-ts/core-updater.js +141 -33
- package/dist/src-ts/daemon-process.js +2 -1
- package/dist/src-ts/dsh-agent.js +31 -13
- package/dist/src-ts/http-agent.js +36 -0
- package/dist/src-ts/plugin-catalog.js +11 -2
- package/dist/src-ts/plugin-manager.js +351 -50
- package/dist/src-ts/plugins/abweb/index.js +1 -1
- package/dist/src-ts/plugins/abweb/public/app.js +73 -5
- package/dist/src-ts/plugins/abweb/public/index.html +15 -0
- package/dist/src-ts/plugins/abweb/public/style.css +4 -0
- package/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/dist/src-ts/plugins/aun/index.js +155 -21
- package/dist/src-ts/plugins/aun/messages.js +4 -1
- package/dist/src-ts/plugins/aun/public/app.js +2 -1
- package/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
- package/dist/src-ts/plugins/chatgpt/index.js +80 -19
- package/dist/src-ts/plugins/claude-cli/index.js +19 -2
- package/dist/src-ts/plugins/codex-cli/index.js +19 -2
- package/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
- package/dist/src-ts/plugins/doubao/index.js +152 -8
- package/dist/src-ts/plugins/doubao/native-task.js +134 -0
- package/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
- package/dist/src-ts/plugins/dsh/index.js +172 -21
- package/dist/src-ts/plugins/dsh/session-history.js +31 -3
- package/dist/src-ts/plugins/feishu/index.js +1 -1
- package/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
- package/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
- package/dist/src-ts/plugins/personal-assistant/index.js +263 -0
- package/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
- package/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
- package/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
- package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
- package/dist/src-ts/plugins/qwenwork/index.js +139 -24
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
- package/dist/src-ts/plugins/workbuddy/index.js +124 -41
- package/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
- package/dist/src-ts/plugins/zcode/index.js +97 -0
- package/dist/src-ts/session-management.js +132 -14
- package/dist/src-ts/version-manager.js +6 -3
- package/dist/tests-ts/abweb-cli.test.js +10 -2
- package/dist/tests-ts/abweb-model-config.test.js +73 -0
- package/dist/tests-ts/agent.test.js +27 -0
- package/dist/tests-ts/aun-attachments.test.js +19 -17
- package/dist/tests-ts/aun-default-owner-page.test.js +2 -1
- package/dist/tests-ts/aun-messages.test.js +20 -0
- package/dist/tests-ts/aun-plugin.test.js +292 -19
- package/dist/tests-ts/bundled-package.test.js +12 -2
- package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
- package/dist/tests-ts/channel-sessions.test.js +27 -0
- package/dist/tests-ts/chatgpt-plugin.test.js +118 -0
- package/dist/tests-ts/control-server.test.js +56 -0
- package/dist/tests-ts/core-updater.test.js +215 -46
- package/dist/tests-ts/daemon-process.test.js +126 -1
- package/dist/tests-ts/desktop-launch.test.js +73 -0
- package/dist/tests-ts/doubao-native-task.test.js +237 -0
- package/dist/tests-ts/doubao-plugin.test.js +773 -12
- package/dist/tests-ts/dsh-agent.test.js +157 -8
- package/dist/tests-ts/dsh-cdp.test.js +148 -0
- package/dist/tests-ts/dsh-history.test.js +37 -0
- package/dist/tests-ts/feishu-plugin.test.js +1 -1
- package/dist/tests-ts/http-agent.test.js +18 -0
- package/dist/tests-ts/installer-assets.test.js +1 -1
- package/dist/tests-ts/local-cli.test.js +146 -1
- package/dist/tests-ts/model-provider-cancellation.test.js +152 -0
- package/dist/tests-ts/native-session-management.test.js +8 -7
- package/dist/tests-ts/personal-assistant-coordination.test.js +92 -0
- package/dist/tests-ts/personal-assistant-extensions.test.js +35 -0
- package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
- package/dist/tests-ts/personal-assistant-owner-context.test.js +107 -0
- package/dist/tests-ts/personal-assistant-remote-ability.test.js +123 -0
- package/dist/tests-ts/personal-assistant.test.js +663 -0
- package/dist/tests-ts/platform-integration.test.js +183 -1
- package/dist/tests-ts/plugin-catalog.test.js +12 -2
- package/dist/tests-ts/plugin-manager.test.js +356 -4
- package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
- package/dist/tests-ts/publish-packages.test.js +42 -0
- package/dist/tests-ts/qwenwork-plugin.test.js +135 -1
- package/dist/tests-ts/qwenwork-reconnect.test.js +195 -0
- package/dist/tests-ts/qwenwork-sse.test.js +33 -0
- package/dist/tests-ts/session-access-defaults.test.js +112 -0
- package/dist/tests-ts/session-management.test.js +159 -4
- package/dist/tests-ts/version-manager.test.js +40 -0
- package/dist/tests-ts/workbuddy-cdp-recovery.test.js +311 -0
- package/dist/tests-ts/workbuddy-plugin.test.js +499 -4
- package/dist/tests-ts/zcode-cdp.test.js +894 -0
- package/dist/tests-ts/zcode-plugin.test.js +396 -0
- package/package.json +1 -1
- package/plugins/abweb/dist/src-ts/agent.js +35 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +73 -5
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +57 -5
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +15 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +4 -0
- package/plugins/abweb/package.json +1 -1
- package/plugins/abweb/plugin.json +1 -1
- package/plugins/aun/config.schema.json +1 -1
- package/plugins/aun/dist/src-ts/agent.js +35 -0
- package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +155 -21
- package/plugins/aun/dist/src-ts/plugins/aun/messages.js +4 -1
- package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +2 -1
- package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/plugins/aun/package.json +1 -1
- package/plugins/aun/plugin.json +1 -1
- package/plugins/chatgpt/dist/src-ts/agent.js +35 -0
- package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +80 -19
- package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/chatgpt/package.json +1 -1
- package/plugins/chatgpt/plugin.json +1 -1
- package/plugins/claude-cli/dist/src-ts/agent.js +35 -0
- package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +19 -2
- package/plugins/claude-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/plugins/claude-cli/package.json +1 -1
- package/plugins/claude-cli/plugin.json +3 -2
- package/plugins/codex-cli/dist/src-ts/agent.js +35 -0
- package/plugins/codex-cli/dist/src-ts/plugins/codex-cli/index.js +19 -2
- package/plugins/codex-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/plugins/codex-cli/package.json +1 -1
- package/plugins/codex-cli/plugin.json +3 -2
- package/plugins/doubao/config.schema.json +2 -1
- package/plugins/doubao/dist/src-ts/agent.js +35 -0
- package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +152 -8
- package/plugins/doubao/dist/src-ts/plugins/doubao/native-task.js +134 -0
- package/plugins/doubao/package.json +1 -1
- package/plugins/doubao/plugin.json +3 -2
- package/plugins/dsh/config.schema.json +5 -2
- package/plugins/dsh/dist/src-ts/agent.js +35 -0
- package/plugins/dsh/dist/src-ts/dsh-agent.js +31 -13
- package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/dsh/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
- package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +172 -21
- package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +31 -3
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/plugin.json +3 -2
- package/plugins/feishu/dist/src-ts/agent.js +35 -0
- package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +1 -1
- package/plugins/feishu/package.json +1 -1
- package/plugins/feishu/plugin.json +1 -1
- package/plugins/personal-assistant/config.schema.json +23 -0
- package/plugins/personal-assistant/dist/src-ts/agent.js +69 -0
- package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
- package/plugins/personal-assistant/dist/src-ts/plugin.js +13 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +263 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
- package/plugins/personal-assistant/package.json +39 -0
- package/plugins/personal-assistant/plugin.json +15 -0
- package/plugins/qwenwork/dist/src-ts/agent.js +35 -0
- package/plugins/qwenwork/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +139 -24
- package/plugins/qwenwork/package.json +1 -1
- package/plugins/qwenwork/plugin.json +3 -2
- package/plugins/workbuddy/config.schema.json +1 -1
- package/plugins/workbuddy/dist/src-ts/agent.js +35 -0
- package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +124 -41
- package/plugins/workbuddy/package.json +1 -1
- package/plugins/workbuddy/plugin.json +3 -2
- package/plugins/zcode/config.schema.json +13 -0
- package/plugins/zcode/dist/src-ts/agent.js +69 -0
- package/plugins/zcode/dist/src-ts/attachments.js +62 -0
- package/plugins/zcode/dist/src-ts/plugin.js +13 -0
- package/plugins/zcode/dist/src-ts/plugins/desktop-launch.js +238 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/cdp-client.js +896 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/index.js +567 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
- package/plugins/zcode/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
- package/plugins/zcode/dist/src-ts/plugins/zcode/index.js +97 -0
- package/plugins/zcode/package.json +16 -0
- package/plugins/zcode/plugin.json +11 -0
|
@@ -59,6 +59,87 @@ const node_test_1 = __importDefault(require("node:test"));
|
|
|
59
59
|
await strict_1.default.rejects(() => client.createTask(), /尚未登录.*桌面端登录/);
|
|
60
60
|
strict_1.default.equal(calls, 1);
|
|
61
61
|
});
|
|
62
|
+
(0, node_test_1.default)("WorkBuddy 恢复会话等待输入组件切换,不只检查侧栏选中", async () => {
|
|
63
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
64
|
+
let clicked = false;
|
|
65
|
+
let checks = 0;
|
|
66
|
+
const button = { className: "_selected_", matches: () => true, getAttribute: () => "saved", click() { clicked = true; } };
|
|
67
|
+
const editor = { __reactFiberTest: { memoizedProps: { get viewConversationId() { return checks >= 3 ? "saved" : "previous"; } } } };
|
|
68
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document: { querySelectorAll: () => [button], querySelector: () => editor } });
|
|
69
|
+
client.waitFor = async (expression) => {
|
|
70
|
+
strict_1.default.equal(clicked, true);
|
|
71
|
+
for (checks = 1; checks <= 3; checks++)
|
|
72
|
+
if (await client.evaluate(expression))
|
|
73
|
+
break;
|
|
74
|
+
};
|
|
75
|
+
await client.activateConversation("saved");
|
|
76
|
+
strict_1.default.equal(checks, 3);
|
|
77
|
+
});
|
|
78
|
+
(0, node_test_1.default)("WorkBuddy 旧版输入组件没有会话属性时仍能恢复侧栏任务", async () => {
|
|
79
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
80
|
+
const button = { className: "_selected_", matches: () => true, getAttribute: () => "saved", click() { } };
|
|
81
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document: { querySelectorAll: () => [button], querySelector: () => ({}) } });
|
|
82
|
+
client.waitFor = async (expression) => { strict_1.default.equal(await client.evaluate(expression), true); };
|
|
83
|
+
await client.activateConversation("saved");
|
|
84
|
+
});
|
|
85
|
+
(0, node_test_1.default)("WorkBuddy 预期会话与侧栏或输入组件不符时拒绝输入", async () => {
|
|
86
|
+
for (const [expected, selected, input] of [["saved", "other", "saved"], ["saved", "saved", "other"], [null, "other", "other"]]) {
|
|
87
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
88
|
+
let inputs = 0;
|
|
89
|
+
const editor = { __reactFiberTest: { memoizedProps: { viewConversationId: input } } };
|
|
90
|
+
client.evaluate = async (expression) => expression.includes("_selected_")
|
|
91
|
+
? (0, node_vm_1.runInNewContext)(expression, { document: { querySelectorAll: () => [{ className: "_selected_", getAttribute: () => selected }], querySelector: () => editor } })
|
|
92
|
+
: expression.includes("JSON.stringify") ? "[]" : true;
|
|
93
|
+
client.cdp = async () => { inputs++; };
|
|
94
|
+
client.readInteraction = async () => undefined;
|
|
95
|
+
client.waitForReply = async () => ({ text: "不应发送" });
|
|
96
|
+
await strict_1.default.rejects(client.complete({ task: "在线不?", sessionId: "logical" }, undefined, false, expected), /桌面任务|会话/);
|
|
97
|
+
strict_1.default.equal(inputs, 0);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
(0, node_test_1.default)("WorkBuddy 输入后会话切换时不点击发送", async () => {
|
|
101
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
102
|
+
let selected = "saved";
|
|
103
|
+
let clicks = 0;
|
|
104
|
+
const editor = { innerText: "在线不?", focus() { }, __reactFiberTest: { memoizedProps: { get viewConversationId() { return selected; } } } };
|
|
105
|
+
const button = { disabled: false, click() { clicks++; } };
|
|
106
|
+
client.evaluate = async (expression) => {
|
|
107
|
+
if (expression.includes("_selected_") || expression.includes('button[aria-label="发送"]'))
|
|
108
|
+
return (0, node_vm_1.runInNewContext)(expression, { window: { getSelection() { } }, document: {
|
|
109
|
+
querySelectorAll: () => [{ className: "_selected_", getAttribute: () => selected }],
|
|
110
|
+
querySelector: (selector) => selector.includes("contenteditable") ? editor : button,
|
|
111
|
+
createRange: () => ({ selectNodeContents() { } }),
|
|
112
|
+
} });
|
|
113
|
+
return expression.includes("JSON.stringify") ? "[]" : true;
|
|
114
|
+
};
|
|
115
|
+
client.cdp = async (method) => { if (method === "Input.insertText")
|
|
116
|
+
selected = "other"; };
|
|
117
|
+
client.readInteraction = async () => undefined;
|
|
118
|
+
client.waitForReply = async () => ({ text: "不应发送" });
|
|
119
|
+
await strict_1.default.rejects(client.complete({ task: "在线不?", sessionId: "logical" }, undefined, false, "saved"), /桌面任务|会话|未发送/);
|
|
120
|
+
strict_1.default.equal(clicks, 0);
|
|
121
|
+
});
|
|
122
|
+
(0, node_test_1.default)("WorkBuddy 续接选项点击前再次核对桌面会话", async () => {
|
|
123
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
124
|
+
let selected = "saved";
|
|
125
|
+
let clicks = 0;
|
|
126
|
+
const editor = { __reactFiberTest: { memoizedProps: { get viewConversationId() { return selected; } } } };
|
|
127
|
+
const option = { disabled: false, getAttribute: () => null, click() { clicks++; } };
|
|
128
|
+
client.pendingInteraction = { prompt: "确认?", options: ["确认"], sessionId: "logical" };
|
|
129
|
+
client.evaluate = async (expression) => {
|
|
130
|
+
if (expression.includes("option.click()"))
|
|
131
|
+
selected = "other";
|
|
132
|
+
if (expression.includes("_selected_") || expression.includes("option.click()"))
|
|
133
|
+
return (0, node_vm_1.runInNewContext)(expression, { document: {
|
|
134
|
+
querySelectorAll: (selector) => selector === "[data-conversation-id]" ? [{ className: "_selected_", getAttribute: () => selected }] : [option],
|
|
135
|
+
querySelector: () => editor,
|
|
136
|
+
} });
|
|
137
|
+
return "[]";
|
|
138
|
+
};
|
|
139
|
+
client.waitForReply = async () => ({ text: "不应确认" });
|
|
140
|
+
await strict_1.default.rejects(client.complete({ task: "1", sessionId: "logical" }, undefined, false, "saved"), /桌面任务|会话|未确认/);
|
|
141
|
+
strict_1.default.equal(clicks, 0);
|
|
142
|
+
});
|
|
62
143
|
const node_vm_1 = require("node:vm");
|
|
63
144
|
const workbuddy_1 = require("../src-ts/plugins/workbuddy");
|
|
64
145
|
const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
@@ -256,6 +337,25 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
256
337
|
strict_1.default.deepEqual(calls, [{ request, expert: undefined, newTask: false }]);
|
|
257
338
|
await plugin.deactivate("stop");
|
|
258
339
|
});
|
|
340
|
+
(0, node_test_1.default)("WorkBuddy 模型来源复用独立会话并传递取消信号", async () => {
|
|
341
|
+
const controller = new AbortController();
|
|
342
|
+
const calls = [];
|
|
343
|
+
const plugin = new workbuddy_1.WorkbuddyPlugin({ client: {
|
|
344
|
+
connect: async () => undefined, close() { }, connected: () => true,
|
|
345
|
+
createTask: async () => { calls.push("new-task"); },
|
|
346
|
+
complete: async (request) => { calls.push(request); return { text: "结构化模型回复" }; },
|
|
347
|
+
direct: async () => "不应使用普通入口",
|
|
348
|
+
} });
|
|
349
|
+
await plugin.activate({ config: { transport: "cdp" }, logger: { info() { }, warn() { }, error() { } } });
|
|
350
|
+
try {
|
|
351
|
+
strict_1.default.deepEqual(await plugin.modelProvider.complete({ prompt: "模型提示", sessionId: "pa-thread", timeoutMs: 5000, signal: controller.signal }), { text: "结构化模型回复" });
|
|
352
|
+
strict_1.default.equal(calls[0], "new-task");
|
|
353
|
+
strict_1.default.deepEqual(calls[1], { task: "模型提示", sessionId: "pa-thread", timeoutMs: 5000, signal: controller.signal });
|
|
354
|
+
}
|
|
355
|
+
finally {
|
|
356
|
+
await plugin.deactivate("stop");
|
|
357
|
+
}
|
|
358
|
+
});
|
|
259
359
|
(0, node_test_1.default)("workbuddy 结构化输出接口不会隐式声明支持附件输入", async () => {
|
|
260
360
|
let calls = 0;
|
|
261
361
|
const client = {
|
|
@@ -366,7 +466,7 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
366
466
|
strict_1.default.deepEqual(fetched, ['file:///C:/movie.bin', 'file:///C:/image.png']);
|
|
367
467
|
strict_1.default.equal(cancelled, true);
|
|
368
468
|
});
|
|
369
|
-
(0, node_test_1.default)("workbuddy
|
|
469
|
+
(0, node_test_1.default)("workbuddy 最多等待二十分钟并遵守较短的显式超时", async () => {
|
|
370
470
|
const timeouts = [];
|
|
371
471
|
const plugin = new workbuddy_1.WorkbuddyPlugin({ client: {
|
|
372
472
|
connect: async () => undefined, close() { }, connected: () => true,
|
|
@@ -376,7 +476,8 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
376
476
|
try {
|
|
377
477
|
await plugin.handle({ task: '生成视频' });
|
|
378
478
|
await plugin.handle({ task: '继续', timeoutMs: 1234 });
|
|
379
|
-
|
|
479
|
+
await plugin.handle({ task: '旧版长等待', timeoutMs: 3_600_000 });
|
|
480
|
+
strict_1.default.deepEqual(timeouts, [1_200_000, 1234, 1_200_000]);
|
|
380
481
|
}
|
|
381
482
|
finally {
|
|
382
483
|
await plugin.deactivate('stop');
|
|
@@ -488,6 +589,118 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
488
589
|
strict_1.default.deepEqual(await client.complete({ task: "继续", timeoutMs: 2_200 }), { text: "视频已停止" });
|
|
489
590
|
strict_1.default.ok(polls >= 2);
|
|
490
591
|
});
|
|
592
|
+
(0, node_test_1.default)("WorkBuddy 隐藏正文回退读取 textContent 且排除隐藏思考区", async () => {
|
|
593
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
594
|
+
const content = { innerText: "", textContent: "内部推理已完成 10s在线。有事直接说。", querySelectorAll: () => [{ innerText: "", textContent: "内部推理" }, { innerText: "", textContent: "已完成 10s" }] };
|
|
595
|
+
const message = { getAttribute: () => "reply", querySelector: (selector) => selector === "[data-assistant-content]" ? content : {} };
|
|
596
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document: { querySelectorAll: () => [message], querySelector: () => null } });
|
|
597
|
+
client.readInteraction = async () => undefined;
|
|
598
|
+
client.readArtifacts = async () => [];
|
|
599
|
+
strict_1.default.deepEqual(await client.waitForReply(new Set(), 1200), { text: "在线。有事直接说。" });
|
|
600
|
+
});
|
|
601
|
+
(0, node_test_1.default)("WorkBuddy 等待期间会话切换立即拒绝其他任务的正文和产物", async () => {
|
|
602
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
603
|
+
const forwarded = [];
|
|
604
|
+
let artifacts = 0;
|
|
605
|
+
client.conversationId = async () => "other";
|
|
606
|
+
const message = { getAttribute: () => "foreign", querySelector: (selector) => selector === "[data-assistant-content]" ? { innerText: "其他任务结果" } : {} };
|
|
607
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document: {
|
|
608
|
+
querySelectorAll: (selector) => selector === "[data-conversation-id]" ? [{ className: "_selected_", getAttribute: () => "other" }] : [message],
|
|
609
|
+
querySelector: () => ({}),
|
|
610
|
+
} });
|
|
611
|
+
client.readInteraction = async () => undefined;
|
|
612
|
+
client.readArtifacts = async () => { artifacts++; return []; };
|
|
613
|
+
await strict_1.default.rejects(client.waitForReply(new Set(), 2000, undefined, undefined, "logical", async (text) => { forwarded.push(text); }, undefined, "saved"), (error) => error.code === "WORKBUDDY_CONVERSATION_CHANGED");
|
|
614
|
+
strict_1.default.deepEqual(forwarded, []);
|
|
615
|
+
strict_1.default.equal(artifacts, 0);
|
|
616
|
+
});
|
|
617
|
+
(0, node_test_1.default)("WorkBuddy 缺少新任务标识时不能把首次选中的旧会话当成本次任务", async () => {
|
|
618
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
619
|
+
client.conversationId = async () => "other";
|
|
620
|
+
const message = { getAttribute: () => "foreign", querySelector: (selector) => selector === "[data-assistant-content]" ? { innerText: "其他任务结果" } : {} };
|
|
621
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document: {
|
|
622
|
+
querySelectorAll: (selector) => selector === "[data-conversation-id]" ? [{ className: "_selected_", getAttribute: () => "other" }] : [message],
|
|
623
|
+
querySelector: () => ({}),
|
|
624
|
+
} });
|
|
625
|
+
client.readInteraction = async () => undefined;
|
|
626
|
+
client.readArtifacts = async () => [];
|
|
627
|
+
await strict_1.default.rejects(client.waitForReply(new Set(), 1200, undefined, undefined, "logical", undefined, undefined, null), (error) => error.code === "WORKBUDDY_CONVERSATION_CHANGED");
|
|
628
|
+
});
|
|
629
|
+
for (const [mode, label] of [["created", "等待侧栏及输入组件同步后绑定新 ID"], ["navigation", "提交后立即点击旧任务时拒绝绑定"], ["timeout", "分配 ID 超时后清理监听"], ["changed", "输入组件就绪前再切换时拒绝绑定"]]) {
|
|
630
|
+
(0, node_test_1.default)(`WorkBuddy 首页${label}`, async () => {
|
|
631
|
+
const client = new cdp_client_1.WorkbuddyCdpClient({ timeoutMs: 150 });
|
|
632
|
+
let selected = null;
|
|
633
|
+
let input = null;
|
|
634
|
+
const listeners = new Map();
|
|
635
|
+
const bound = [];
|
|
636
|
+
const editor = { innerText: "", focus() { }, __reactFiberTest: { memoizedProps: { get viewConversationId() { return input; } } } };
|
|
637
|
+
const button = { disabled: false, click() {
|
|
638
|
+
listeners.get("click")?.();
|
|
639
|
+
if (mode === "created" || mode === "changed")
|
|
640
|
+
selected = "created";
|
|
641
|
+
if (mode === "created")
|
|
642
|
+
setTimeout(() => { input = "created"; }, 0);
|
|
643
|
+
if (mode === "navigation")
|
|
644
|
+
setTimeout(() => { listeners.get("click")?.(); selected = input = "other"; }, 0);
|
|
645
|
+
if (mode === "changed")
|
|
646
|
+
setTimeout(() => { selected = input = "other"; }, 75);
|
|
647
|
+
} };
|
|
648
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { setTimeout, clearTimeout, setInterval, clearInterval, window: { getSelection() { } }, document: {
|
|
649
|
+
querySelectorAll: (selector) => selector === "[data-conversation-id]" ? ["other", ...(selected === "created" ? ["created"] : [])].map(id => ({ className: selected === id ? "_selected_" : "", getAttribute: () => id })) : [],
|
|
650
|
+
querySelector: (selector) => selector.includes("contenteditable") ? editor : selector.includes('button[aria-label="发送"]') ? button : null,
|
|
651
|
+
createRange: () => ({ selectNodeContents() { } }),
|
|
652
|
+
addEventListener: (name, callback) => { listeners.set(name, callback); },
|
|
653
|
+
removeEventListener: (name) => { listeners.delete(name); },
|
|
654
|
+
} });
|
|
655
|
+
client.cdp = async (method, params) => { if (method === "Input.insertText")
|
|
656
|
+
editor.innerText = params.text; };
|
|
657
|
+
client.waitForReply = async (...args) => { bound.push(args[7]); return { text: "已完成" }; };
|
|
658
|
+
const response = client.complete({ task: "在线不?", sessionId: "logical" }, undefined, false, null);
|
|
659
|
+
if (mode === "created") {
|
|
660
|
+
strict_1.default.deepEqual(await response, { text: "已完成" });
|
|
661
|
+
strict_1.default.deepEqual(bound, ["created"]);
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
await strict_1.default.rejects(response, /会话|任务标识/);
|
|
665
|
+
strict_1.default.deepEqual(bound, []);
|
|
666
|
+
}
|
|
667
|
+
strict_1.default.equal(listeners.size, 0);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
(0, node_test_1.default)("WorkBuddy 正文快照之后切换会话也不能读取其他任务的确认选项", async () => {
|
|
671
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
672
|
+
let selected = "saved";
|
|
673
|
+
const editor = { __reactFiberTest: { memoizedProps: { get viewConversationId() { return selected; } } } };
|
|
674
|
+
const message = { getAttribute: () => "reply", querySelector: (selector) => selector === "[data-assistant-content]" ? { innerText: "本轮正文" } : {} };
|
|
675
|
+
const option = { innerText: "其他会话选项", closest: () => ({ innerText: "其他会话确认?", getAttribute: () => "foreign", querySelector: () => ({ innerText: "其他会话正文" }) }) };
|
|
676
|
+
client.evaluate = async (expression) => {
|
|
677
|
+
if (expression.includes("conversation-input-area"))
|
|
678
|
+
selected = "other";
|
|
679
|
+
return (0, node_vm_1.runInNewContext)(expression, { document: {
|
|
680
|
+
querySelectorAll: (selector) => selector === "[data-conversation-id]" ? [{ className: "_selected_", getAttribute: () => selected }] : selector === ".cr-agent[data-message-id]" ? [message] : [option],
|
|
681
|
+
querySelector: (selector) => selector.includes("contenteditable") ? editor : null,
|
|
682
|
+
} });
|
|
683
|
+
};
|
|
684
|
+
client.readArtifacts = async () => [];
|
|
685
|
+
await strict_1.default.rejects(client.waitForReply(new Set(), 1200, undefined, undefined, "logical", undefined, undefined, "saved"), (error) => error.code === "WORKBUDDY_CONVERSATION_CHANGED");
|
|
686
|
+
strict_1.default.equal(client.pendingInteraction, undefined);
|
|
687
|
+
});
|
|
688
|
+
(0, node_test_1.default)("WorkBuddy 产物读取前及异步读取期间切换会话均拒绝成功回传", async () => {
|
|
689
|
+
for (const initial of ["other", "saved"]) {
|
|
690
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
691
|
+
let selected = initial;
|
|
692
|
+
const editor = { __reactFiberTest: { memoizedProps: { get viewConversationId() { return selected; } } } };
|
|
693
|
+
const message = { getAttribute: () => "reply", querySelector: () => null, querySelectorAll: () => [{ getAttribute: () => "file:///result.txt" }] };
|
|
694
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { URL, btoa, fetch: async () => {
|
|
695
|
+
selected = "other";
|
|
696
|
+
return new Response("结果", { headers: { "content-type": "text/plain" } });
|
|
697
|
+
}, document: {
|
|
698
|
+
querySelectorAll: (selector) => selector === "[data-conversation-id]" ? [{ className: "_selected_", getAttribute: () => selected }] : [message],
|
|
699
|
+
querySelector: () => editor,
|
|
700
|
+
} });
|
|
701
|
+
await strict_1.default.rejects(client.readArtifacts(new Set(["reply"]), "saved"), (error) => error.code === "WORKBUDDY_CONVERSATION_CHANGED");
|
|
702
|
+
}
|
|
703
|
+
});
|
|
491
704
|
(0, node_test_1.default)("workbuddy CDP 选择后仍在处理时不能把工具状态当成最终回复", async () => {
|
|
492
705
|
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
493
706
|
let polls = 0;
|
|
@@ -827,7 +1040,7 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
827
1040
|
});
|
|
828
1041
|
(0, node_test_1.default)("workbuddy 清单使用 CDP 插件能力并保持发布版本一致", async () => {
|
|
829
1042
|
strict_1.default.equal(workbuddy_1.workbuddyManifest.id, "workbuddy");
|
|
830
|
-
strict_1.default.deepEqual(workbuddy_1.workbuddyManifest.capabilities, ["chat", "health"]);
|
|
1043
|
+
strict_1.default.deepEqual(workbuddy_1.workbuddyManifest.capabilities, ["chat", "health", "model-provider"]);
|
|
831
1044
|
for (const file of ["packages/workbuddy/package.json", "packages/workbuddy/plugin.json", "plugins/workbuddy/plugin.json"]) {
|
|
832
1045
|
const metadata = JSON.parse(await (0, promises_1.readFile)(file, "utf8"));
|
|
833
1046
|
strict_1.default.equal(workbuddy_1.workbuddyManifest.version, metadata.version, file);
|
|
@@ -1028,7 +1241,7 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
1028
1241
|
querySelectorAll: (selector) => selector === '.cr-agent[data-message-id]' ? Array(messages).fill({}) : selector === '[data-conversation-id]' ? conversationId ? [{ className: "_selected_old", getAttribute: () => conversationId }] : [] : [button],
|
|
1029
1242
|
querySelector: () => editor,
|
|
1030
1243
|
};
|
|
1031
|
-
const client = new cdp_client_1.WorkbuddyCdpClient({ timeoutMs:
|
|
1244
|
+
const client = new cdp_client_1.WorkbuddyCdpClient({ timeoutMs: 1000 });
|
|
1032
1245
|
const internal = client;
|
|
1033
1246
|
internal.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document, location: { href: "file:///D:/WorkBuddy/index.html?login=private#state" } });
|
|
1034
1247
|
await client.createTask();
|
|
@@ -1050,6 +1263,127 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
1050
1263
|
strict_1.default.equal(selected, false);
|
|
1051
1264
|
strict_1.default.equal(messages, 0);
|
|
1052
1265
|
});
|
|
1266
|
+
(0, node_test_1.default)("workbuddy CDP 新建时立即分配会话 ID 后允许发送首条消息", async () => {
|
|
1267
|
+
let conversationId = "old-conversation";
|
|
1268
|
+
let messages = 1;
|
|
1269
|
+
const editor = { innerText: "", focus() { }, querySelector: () => null, __reactFiberTest: { memoizedProps: { get viewConversationId() { return conversationId; } } }, cloneNode: () => ({ textContent: "", querySelectorAll: () => [{ remove() { } }] }) };
|
|
1270
|
+
const createButton = { textContent: "新建任务", disabled: false, getAttribute: () => null, click: () => { conversationId = "new-conversation"; messages = 0; } };
|
|
1271
|
+
const sendButton = { disabled: false, click() { } };
|
|
1272
|
+
const document = {
|
|
1273
|
+
querySelectorAll: (selector) => selector === '.cr-agent[data-message-id]' ? Array(messages).fill({}) : selector === "[data-conversation-id]" ? [{ className: "_selected_", getAttribute: () => conversationId }] : selector.includes("conversation-input-area") ? [] : [createButton],
|
|
1274
|
+
querySelector: (selector) => selector.includes("contenteditable") ? editor : selector.includes('button[aria-label="发送"]') ? sendButton : null,
|
|
1275
|
+
createRange: () => ({ selectNodeContents() { } }),
|
|
1276
|
+
};
|
|
1277
|
+
const client = new cdp_client_1.WorkbuddyCdpClient({ timeoutMs: 500 });
|
|
1278
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document, location: { href: "app://workbuddy/task/old" }, window: { getSelection() { } } });
|
|
1279
|
+
client.cdp = async (_method, params) => { if (params.text)
|
|
1280
|
+
editor.innerText = params.text; };
|
|
1281
|
+
let bound;
|
|
1282
|
+
client.waitForReply = async (...args) => { bound = args[7]; return { text: "已完成" }; };
|
|
1283
|
+
client.connected = () => true;
|
|
1284
|
+
client.connect = async () => undefined;
|
|
1285
|
+
const plugin = new workbuddy_1.WorkbuddyPlugin({ client });
|
|
1286
|
+
await plugin.activate({ config: { transport: "cdp", dailyPoints: false }, logger: { info() { }, warn() { }, error() { } } });
|
|
1287
|
+
strict_1.default.equal((await plugin.handle({ task: "在线不?", sessionId: "logical" })).text, "已完成");
|
|
1288
|
+
strict_1.default.equal(bound, "new-conversation");
|
|
1289
|
+
});
|
|
1290
|
+
for (const mode of ["同步", "微任务", "异步", "切换", "超时", "目录中切换", "搜索延迟", "搜索回退", "搜索失败"]) {
|
|
1291
|
+
(0, node_test_1.default)(`WorkBuddy 新建逻辑会话后专家召唤${mode}分配 ID 时校验专家会话`, async () => {
|
|
1292
|
+
const client = new cdp_client_1.WorkbuddyCdpClient({ timeoutMs: 220 });
|
|
1293
|
+
let selected = "old-conversation";
|
|
1294
|
+
let editorId = selected;
|
|
1295
|
+
let messages = 1;
|
|
1296
|
+
let sentTo;
|
|
1297
|
+
let catalogOpen = false;
|
|
1298
|
+
let summoned = false;
|
|
1299
|
+
let searchReady = mode !== "搜索延迟" && mode !== "搜索回退" && mode !== "搜索失败";
|
|
1300
|
+
const listeners = new Map();
|
|
1301
|
+
const editor = { innerText: "", focus() { }, querySelector: () => null, cloneNode: () => ({ textContent: "", querySelectorAll: () => [] }), __reactFiberTest: { memoizedProps: { get viewConversationId() { return editorId; } } } };
|
|
1302
|
+
const create = { textContent: "新建任务", disabled: false, getAttribute: () => null, click() { selected = editorId = "new-conversation"; messages = 0; } };
|
|
1303
|
+
const send = { disabled: false, click() { sentTo = selected; } };
|
|
1304
|
+
const summon = { getAttribute: () => null, click() {
|
|
1305
|
+
strict_1.default.equal(selected, "new-conversation");
|
|
1306
|
+
summoned = true;
|
|
1307
|
+
catalogOpen = false;
|
|
1308
|
+
if (mode === "同步" || mode === "搜索延迟" || mode === "搜索回退")
|
|
1309
|
+
selected = editorId = "expert-created";
|
|
1310
|
+
if (mode === "微任务")
|
|
1311
|
+
void Promise.resolve().then(() => { selected = editorId = "expert-created"; });
|
|
1312
|
+
if (mode === "异步")
|
|
1313
|
+
setTimeout(() => { selected = "expert-created"; setTimeout(() => { editorId = selected; }, 60); }, 20);
|
|
1314
|
+
if (mode === "切换")
|
|
1315
|
+
setTimeout(() => { listeners.get("click")?.(); selected = editorId = "foreign"; }, 0);
|
|
1316
|
+
} };
|
|
1317
|
+
const card = { querySelector: (selector) => selector.includes("_role_") ? { textContent: searchReady ? "测试专家" : "其他专家" } : summon };
|
|
1318
|
+
const input = { value: "测试专家", dispatchEvent() { }, focus() { } };
|
|
1319
|
+
const entry = { textContent: "专家·技能·连接器", click() { catalogOpen = true; if (mode === "目录中切换")
|
|
1320
|
+
selected = "foreign"; if (mode === "搜索延迟")
|
|
1321
|
+
setTimeout(() => { searchReady = true; }, 20); } };
|
|
1322
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { setTimeout, clearTimeout, setInterval, clearInterval, location: { href: "app://workbuddy/task/old" }, HTMLInputElement: { prototype: {} }, Event: class {
|
|
1323
|
+
}, window: { getSelection() { } }, document: {
|
|
1324
|
+
querySelectorAll: (selector) => selector === "[data-conversation-id]" ? [{ className: "_selected_", getAttribute: () => selected }] : selector === '.cr-agent[data-message-id]' ? Array(messages).fill({}) : selector === "article[role=\"button\"]" ? [card] : selector.includes("expert_summon_click") ? [summon] : selector === 'button,[role="button"],a' ? [entry] : selector === 'button,[role="button"],[role="tab"]' ? [create] : [],
|
|
1325
|
+
querySelector: (selector) => selector.includes("contenteditable") ? catalogOpen ? null : editor : selector.includes("搜索专家") ? input : selector.includes('button[aria-label="发送"]') ? send : null,
|
|
1326
|
+
createRange: () => ({ selectNodeContents() { } }),
|
|
1327
|
+
addEventListener: (name, callback) => { listeners.set(name, callback); },
|
|
1328
|
+
removeEventListener: (name) => { listeners.delete(name); },
|
|
1329
|
+
} });
|
|
1330
|
+
client.cdp = async (_method, params) => { if (params.text) {
|
|
1331
|
+
if (catalogOpen) {
|
|
1332
|
+
input.value = params.text;
|
|
1333
|
+
if (mode === "搜索回退" && params.text === "测试")
|
|
1334
|
+
searchReady = true;
|
|
1335
|
+
}
|
|
1336
|
+
else
|
|
1337
|
+
editor.innerText = params.text;
|
|
1338
|
+
} };
|
|
1339
|
+
let bound;
|
|
1340
|
+
client.waitForReply = async (...args) => { bound = args[7]; return { text: "专家已回复" }; };
|
|
1341
|
+
client.connected = () => true;
|
|
1342
|
+
client.connect = async () => undefined;
|
|
1343
|
+
const plugin = new workbuddy_1.WorkbuddyPlugin({ client });
|
|
1344
|
+
await plugin.activate({ config: { transport: "cdp", dailyPoints: false }, logger: { info() { }, warn() { }, error() { } } });
|
|
1345
|
+
const response = plugin.handle({ task: "只要测试专家,帮我处理", sessionId: "logical" });
|
|
1346
|
+
if (mode === "切换" || mode === "超时" || mode === "目录中切换" || mode === "搜索失败") {
|
|
1347
|
+
await strict_1.default.rejects(response, /会话|未输入|未就绪|未找到/);
|
|
1348
|
+
strict_1.default.equal(sentTo, undefined);
|
|
1349
|
+
strict_1.default.equal(bound, undefined);
|
|
1350
|
+
}
|
|
1351
|
+
else {
|
|
1352
|
+
strict_1.default.equal((await response).text, "专家已回复");
|
|
1353
|
+
strict_1.default.equal(sentTo, "expert-created");
|
|
1354
|
+
strict_1.default.equal(bound, "expert-created");
|
|
1355
|
+
strict_1.default.equal(plugin.activeDesktopConversationId, "expert-created");
|
|
1356
|
+
}
|
|
1357
|
+
strict_1.default.equal(summoned, mode !== "目录中切换" && mode !== "搜索失败");
|
|
1358
|
+
strict_1.default.equal(listeners.size, 0);
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
(0, node_test_1.default)("WorkBuddy 新建状态核验后切换到旧会话时不能输入首条消息", async () => {
|
|
1362
|
+
const client = new cdp_client_1.WorkbuddyCdpClient({ timeoutMs: 20 });
|
|
1363
|
+
let selected = "old";
|
|
1364
|
+
let switched = false;
|
|
1365
|
+
let inputs = 0;
|
|
1366
|
+
const editor = { innerText: "", focus() { }, querySelector: () => null, cloneNode: () => ({ textContent: "", querySelectorAll: () => [] }), __reactFiberTest: { memoizedProps: { get viewConversationId() { return selected; } } } };
|
|
1367
|
+
const create = { textContent: "新建任务", disabled: false, getAttribute: () => null, click() { selected = "created"; } };
|
|
1368
|
+
const document = {
|
|
1369
|
+
querySelectorAll: (selector) => selector === "[data-conversation-id]" ? [{ className: "_selected_", getAttribute: () => selected }] : selector.includes("cr-agent") || selector.includes("conversation-input-area") ? [] : [create],
|
|
1370
|
+
querySelector: () => editor,
|
|
1371
|
+
createRange: () => ({ selectNodeContents() { } }),
|
|
1372
|
+
};
|
|
1373
|
+
client.evaluate = async (expression) => {
|
|
1374
|
+
const result = (0, node_vm_1.runInNewContext)(expression, { document, location: { href: "app://task" }, window: { getSelection() { } } });
|
|
1375
|
+
if (expression.includes("clearedToUnselected") && result) {
|
|
1376
|
+
selected = "foreign";
|
|
1377
|
+
switched = true;
|
|
1378
|
+
}
|
|
1379
|
+
return result;
|
|
1380
|
+
};
|
|
1381
|
+
client.cdp = async () => { inputs++; };
|
|
1382
|
+
client.waitForReply = async () => ({ text: "不应发送" });
|
|
1383
|
+
await strict_1.default.rejects(async () => { await client.createTask(); await client.complete({ task: "在线不?", sessionId: "logical" }, undefined, false, null); }, /会话|输入|新任务/);
|
|
1384
|
+
strict_1.default.equal(switched, true);
|
|
1385
|
+
strict_1.default.equal(inputs, 0);
|
|
1386
|
+
});
|
|
1053
1387
|
(0, node_test_1.default)("workbuddy CDP 等待新建任务入口完成渲染后再点击", async () => {
|
|
1054
1388
|
let lookups = 0;
|
|
1055
1389
|
let clicked = false;
|
|
@@ -1385,6 +1719,17 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
1385
1719
|
client.readArtifacts = async () => { throw Object.assign(new Error("文件已删除"), { code: "ENOENT" }); };
|
|
1386
1720
|
await strict_1.default.rejects(client.waitForReply(new Set(), 2000), /WORKBUDDY_ARTIFACT_READ_FAILED.*读取.*ENOENT/);
|
|
1387
1721
|
});
|
|
1722
|
+
(0, node_test_1.default)("WorkBuddy 回复正文排除已完成耗时,保留模型决策 JSON", async () => {
|
|
1723
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
1724
|
+
const decision = JSON.stringify({ role: "assistant", content: "测试标记", tool_calls: [] });
|
|
1725
|
+
const content = { innerText: `已完成 13s\n\n${decision}`, querySelectorAll: (selector) => selector.includes(".cr-process-row") ? [{ innerText: "已完成 13s" }] : [] };
|
|
1726
|
+
const message = { getAttribute: () => "reply", querySelector: (selector) => selector === "[data-assistant-content]" ? content : {} };
|
|
1727
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document: { querySelectorAll: () => [message], querySelector: () => null } });
|
|
1728
|
+
client.readInteraction = async () => undefined;
|
|
1729
|
+
client.readArtifacts = async () => [];
|
|
1730
|
+
const response = await client.waitForReply(new Set(), 2000);
|
|
1731
|
+
strict_1.default.equal(response.text, decision);
|
|
1732
|
+
});
|
|
1388
1733
|
(0, node_test_1.default)("WorkBuddy 停止期间完成的积分检查不重新启动定时器", async () => {
|
|
1389
1734
|
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "wb-stop-points-"));
|
|
1390
1735
|
let started;
|
|
@@ -1468,3 +1813,153 @@ const cdp_client_1 = require("../src-ts/plugins/workbuddy/cdp-client");
|
|
|
1468
1813
|
return true;
|
|
1469
1814
|
});
|
|
1470
1815
|
});
|
|
1816
|
+
(0, node_test_1.default)("WorkBuddy 模型排队取消不新建任务,断线不恢复桌面", async () => {
|
|
1817
|
+
let connected = true;
|
|
1818
|
+
let creates = 0;
|
|
1819
|
+
let reconnects = 0;
|
|
1820
|
+
let release;
|
|
1821
|
+
const plugin = new workbuddy_1.WorkbuddyPlugin({ client: {
|
|
1822
|
+
connect: async () => undefined, close() { }, connected: () => connected,
|
|
1823
|
+
createTask: async () => { creates++; }, direct: async () => "回复",
|
|
1824
|
+
complete: async (request) => { if (request.task === "用户任务")
|
|
1825
|
+
await new Promise(resolve => { release = resolve; }); return { text: "回复" }; },
|
|
1826
|
+
} });
|
|
1827
|
+
await plugin.activate({ config: { transport: "cdp" }, logger: { info() { }, warn() { }, error() { } } });
|
|
1828
|
+
const running = plugin.handle({ task: "用户任务" });
|
|
1829
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
1830
|
+
const controller = new AbortController();
|
|
1831
|
+
const queued = plugin.modelProvider.complete({ prompt: "取消提示", sessionId: "pa-queued", timeoutMs: 5000, signal: controller.signal }).catch(() => undefined);
|
|
1832
|
+
controller.abort(new Error("排队取消"));
|
|
1833
|
+
release();
|
|
1834
|
+
await Promise.all([running, queued]);
|
|
1835
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
1836
|
+
const afterCancel = creates;
|
|
1837
|
+
plugin.reconnect = async () => { reconnects++; connected = true; };
|
|
1838
|
+
connected = false;
|
|
1839
|
+
const error = await plugin.modelProvider.complete({ prompt: "离线提示", sessionId: "pa-offline", timeoutMs: 5000, signal: new AbortController().signal }).then(() => undefined, (value) => value);
|
|
1840
|
+
await plugin.deactivate("stop");
|
|
1841
|
+
strict_1.default.equal(afterCancel, 0);
|
|
1842
|
+
strict_1.default.ok(error);
|
|
1843
|
+
strict_1.default.equal(reconnects, 0);
|
|
1844
|
+
});
|
|
1845
|
+
(0, node_test_1.default)("WorkBuddy 自动 CDP 模式提供模型能力,模型与用户任务恢复各自原生会话", async () => {
|
|
1846
|
+
let active = "user-chat";
|
|
1847
|
+
let sequence = 0;
|
|
1848
|
+
const calls = [];
|
|
1849
|
+
const plugin = new workbuddy_1.WorkbuddyPlugin({ client: {
|
|
1850
|
+
connect: async () => undefined, close() { }, connected: () => true,
|
|
1851
|
+
conversationId: async () => active,
|
|
1852
|
+
activateConversation: async (id) => { active = id; },
|
|
1853
|
+
createTask: async () => { active = `model-${++sequence}`; },
|
|
1854
|
+
complete: async (request) => { calls.push(`${active}:${request.task}`); return { text: "回复" }; },
|
|
1855
|
+
direct: async () => "回复",
|
|
1856
|
+
} });
|
|
1857
|
+
await plugin.activate({ config: {}, logger: { info() { }, warn() { }, error() { } } });
|
|
1858
|
+
try {
|
|
1859
|
+
const provider = plugin.modelProvider;
|
|
1860
|
+
strict_1.default.ok(provider);
|
|
1861
|
+
for (const prompt of ["调用视频生成专家", "第二轮"]) {
|
|
1862
|
+
await provider.complete({ prompt, sessionId: "pa-thread", timeoutMs: 5000, signal: new AbortController().signal });
|
|
1863
|
+
strict_1.default.equal(active, "user-chat");
|
|
1864
|
+
await plugin.handle({ task: "用户续聊" });
|
|
1865
|
+
}
|
|
1866
|
+
strict_1.default.deepEqual(calls, ["model-1:调用视频生成专家", "user-chat:用户续聊", "model-1:第二轮", "user-chat:用户续聊"]);
|
|
1867
|
+
}
|
|
1868
|
+
finally {
|
|
1869
|
+
await plugin.deactivate("stop");
|
|
1870
|
+
}
|
|
1871
|
+
});
|
|
1872
|
+
(0, node_test_1.default)("WorkBuddy 用户桌面忙碌或有草稿时模型请求不切换会话", async () => {
|
|
1873
|
+
for (const state of [{ generating: true }, { draft: true }, { waiting: true }]) {
|
|
1874
|
+
const calls = [];
|
|
1875
|
+
const plugin = new workbuddy_1.WorkbuddyPlugin({ client: {
|
|
1876
|
+
connect: async () => undefined, close() { }, connected: () => true,
|
|
1877
|
+
historyAvailability: async () => ({ waiting: false, ...state }),
|
|
1878
|
+
createTask: async () => { calls.push("create"); },
|
|
1879
|
+
activateConversation: async () => { calls.push("switch"); },
|
|
1880
|
+
complete: async () => { calls.push("send"); return { text: "不应发送" }; },
|
|
1881
|
+
direct: async () => "不应发送",
|
|
1882
|
+
} });
|
|
1883
|
+
await plugin.activate({ config: { transport: "cdp" }, logger: { info() { }, warn() { }, error() { } } });
|
|
1884
|
+
try {
|
|
1885
|
+
await strict_1.default.rejects(plugin.modelProvider.complete({ prompt: "模型提示", sessionId: "pa-busy", timeoutMs: 5000, signal: new AbortController().signal }), /用户草稿|正在生成|待确认/);
|
|
1886
|
+
strict_1.default.deepEqual(calls, []);
|
|
1887
|
+
}
|
|
1888
|
+
finally {
|
|
1889
|
+
await plugin.deactivate("stop");
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
(0, node_test_1.default)("WorkBuddy 模型在点击切换会话前再次保护新增草稿", async () => {
|
|
1894
|
+
const client = new cdp_client_1.WorkbuddyCdpClient();
|
|
1895
|
+
let clicks = 0;
|
|
1896
|
+
const editor = { cloneNode: () => ({ textContent: "刚输入的草稿", querySelectorAll: () => [] }) };
|
|
1897
|
+
client.evaluate = async (expression) => (0, node_vm_1.runInNewContext)(expression, { document: {
|
|
1898
|
+
querySelector: (selector) => selector.includes("contenteditable") ? editor : null,
|
|
1899
|
+
querySelectorAll: () => [{ matches: () => true, getAttribute: (name) => name === "data-conversation-id" ? "model" : null, click() { clicks++; } }],
|
|
1900
|
+
} });
|
|
1901
|
+
await strict_1.default.rejects(client.activateConversation("model", true), /用户草稿|未发送内容/);
|
|
1902
|
+
strict_1.default.equal(clicks, 0);
|
|
1903
|
+
});
|
|
1904
|
+
(0, node_test_1.default)("WorkBuddy 运行中 CDP 消失后重新启动桥接实例,普通连接和页面错误不启动", async (t) => {
|
|
1905
|
+
const { mock } = await Promise.resolve().then(() => __importStar(require("node:test")));
|
|
1906
|
+
const desktopModule = await Promise.resolve().then(() => __importStar(require("../src-ts/plugins/workbuddy/desktop-launch")));
|
|
1907
|
+
const desktop = desktopModule.launchWorkbuddyDesktop ? desktopModule : desktopModule.default;
|
|
1908
|
+
if (typeof desktop?.launchWorkbuddyDesktop !== "function") {
|
|
1909
|
+
t.skip("当前测试加载器未暴露可替换的桌面启动函数");
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
let connected = true;
|
|
1913
|
+
let launches = 0;
|
|
1914
|
+
let pageError = false;
|
|
1915
|
+
const plugin = new workbuddy_1.WorkbuddyPlugin();
|
|
1916
|
+
plugin.client = {
|
|
1917
|
+
connect: async () => { if (!connected)
|
|
1918
|
+
throw Object.assign(new Error("connect ECONNREFUSED 127.0.0.1:9229"), { code: "ECONNREFUSED" }); },
|
|
1919
|
+
close() { }, connected: () => connected,
|
|
1920
|
+
direct: async () => { if (pageError)
|
|
1921
|
+
throw new Error("页面入口不可用"); return "恢复成功"; },
|
|
1922
|
+
};
|
|
1923
|
+
let stub;
|
|
1924
|
+
try {
|
|
1925
|
+
stub = mock.method(desktop, "launchWorkbuddyDesktop", (file, args, profile, port) => {
|
|
1926
|
+
launches++;
|
|
1927
|
+
strict_1.default.equal(file, process.execPath);
|
|
1928
|
+
strict_1.default.ok(args.includes("--remote-debugging-port=9229"));
|
|
1929
|
+
strict_1.default.match(profile, /desktop$/);
|
|
1930
|
+
strict_1.default.equal(port, 9229);
|
|
1931
|
+
connected = true;
|
|
1932
|
+
return { unref() { }, exitCode: 0 };
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
1935
|
+
catch {
|
|
1936
|
+
t.skip("当前测试加载器不支持替换桌面启动模块");
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
try {
|
|
1940
|
+
await plugin.connectOrStart({ executablePath: process.execPath }, { info() { }, warn() { }, error() { } }, 9229);
|
|
1941
|
+
strict_1.default.equal(launches, 0);
|
|
1942
|
+
connected = false;
|
|
1943
|
+
strict_1.default.equal((await plugin.handle({ task: "恢复测试" })).text, "恢复成功");
|
|
1944
|
+
strict_1.default.equal(launches, 1);
|
|
1945
|
+
connected = false;
|
|
1946
|
+
strict_1.default.equal((await plugin.handle({ task: "再次恢复" })).text, "恢复成功");
|
|
1947
|
+
strict_1.default.equal(launches, 2);
|
|
1948
|
+
pageError = true;
|
|
1949
|
+
await strict_1.default.rejects(plugin.handle({ task: "页面失败" }), /页面入口不可用/);
|
|
1950
|
+
strict_1.default.equal(launches, 2);
|
|
1951
|
+
connected = false;
|
|
1952
|
+
await plugin.connectOrStart({ executablePath: process.execPath, autoStart: false }, { info() { }, warn() { }, error() { } }, 9229).catch(() => { });
|
|
1953
|
+
await strict_1.default.rejects(plugin.handle({ task: "禁止自动启动" }), /ECONNREFUSED/);
|
|
1954
|
+
strict_1.default.equal(launches, 2);
|
|
1955
|
+
}
|
|
1956
|
+
finally {
|
|
1957
|
+
stub.mock.restore();
|
|
1958
|
+
await plugin.deactivate("stop");
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
(0, node_test_1.default)("WorkBuddy 升级后 CDP 端口未监听时提示现有桌面恢复操作", async () => {
|
|
1962
|
+
const failure = new Error("fetch failed", { cause: Object.assign(new Error("拒绝连接"), { code: "ECONNREFUSED" }) });
|
|
1963
|
+
const client = new cdp_client_1.WorkbuddyCdpClient({ port: 19229, fetchImpl: async () => { throw failure; } });
|
|
1964
|
+
await strict_1.default.rejects(() => client.connect(), (error) => /19229/.test(error.message) && /重启桌面/.test(error.message) && error.cause === failure);
|
|
1965
|
+
});
|