mu-agent-bridge 1.0.21 → 1.0.22

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.
@@ -5,6 +5,7 @@ const node_child_process_1 = require("node:child_process");
5
5
  exports.editorSelector = '[data-codex-composer="true"][contenteditable="true"]';
6
6
  exports.turnSelector = '[data-chatgpt-conversation-turn="true"]';
7
7
  exports.userBubbleSelector = "[data-user-message-bubble]";
8
+ const assistantMessageSelector = '[data-markdown-text-style="assistant-message"]';
8
9
  const turnIdAttribute = "data-chatgpt-conversation-turn-id";
9
10
  const contentUnitSelector = "[data-content-search-unit-key]";
10
11
  const sendButtonSelector = 'button[aria-label="Send"], button[aria-label="发送"]';
@@ -88,7 +89,8 @@ class ChatgptCdpClient {
88
89
  if (!response.ok)
89
90
  throw new Error(`ChatGPT CDP /json HTTP ${response.status}`);
90
91
  const targets = await response.json();
91
- const page = targets.find((target) => target.type === "page" && /chatgpt|codex/i.test(`${target.title} ${target.url}`));
92
+ const page = targets.find((target) => target.type === "page" && (/chatgpt|codex/i.test(`${target.title} ${target.url}`)
93
+ || target.url === "app://-/index.html"));
92
94
  if (page?.webSocketDebuggerUrl)
93
95
  return page;
94
96
  last = new Error(`未找到 ChatGPT/Codex 页面 target(端口 ${this.port})`);
@@ -111,6 +113,7 @@ class ChatgptCdpClient {
111
113
  await this.waitFor(`Boolean(document.querySelector(${JSON.stringify(exports.editorSelector)}))`, "ChatGPT 桌面端输入框未就绪");
112
114
  const beforeTurnsJson = await this.evaluate(`JSON.stringify([...document.querySelectorAll(${JSON.stringify(exports.turnSelector)})].map(turn => ({ id: turn.getAttribute(${JSON.stringify(turnIdAttribute)}), users: turn.querySelectorAll(${JSON.stringify(exports.userBubbleSelector)}).length })))`);
113
115
  const beforeTurns = JSON.parse(String(beforeTurnsJson || "[]"));
116
+ const beforeAssistantCount = Number(await this.evaluate(`document.querySelectorAll(${JSON.stringify(assistantMessageSelector)}).length`));
114
117
  const ready = await this.evaluate(`(() => {
115
118
  const editor = document.querySelector(${JSON.stringify(exports.editorSelector)});
116
119
  if (!editor || editor.innerText.trim()) return false;
@@ -164,7 +167,13 @@ class ChatgptCdpClient {
164
167
  const fresh = oldIndex < 0 || index >= before.length || users.length > (old?.users ?? 0);
165
168
  return fresh && users.item(users.length - 1)?.innerText.trim() === ${JSON.stringify(text.trim())};
166
169
  });
167
- if (!turn) return JSON.stringify({ reply: "", active: false });
170
+ if (!turn) {
171
+ const assistants = [...document.querySelectorAll(${JSON.stringify(assistantMessageSelector)})];
172
+ const fallback = assistants.length > ${beforeAssistantCount}
173
+ ? assistants.at(-1)?.innerText.trim() ?? ""
174
+ : "";
175
+ return JSON.stringify({ reply: fallback, active: Boolean(document.querySelector(${JSON.stringify(stopButtonSelector)})) });
176
+ }
168
177
  const user = turn.querySelectorAll(${JSON.stringify(exports.userBubbleSelector)}).item(turn.querySelectorAll(${JSON.stringify(exports.userBubbleSelector)}).length - 1);
169
178
  const unit = user?.closest(${JSON.stringify(contentUnitSelector)});
170
179
  const units = [...turn.querySelectorAll(${JSON.stringify(contentUnitSelector)})];
@@ -35,6 +35,20 @@ const chatgpt_1 = require("../src-ts/plugins/chatgpt");
35
35
  strict_1.default.equal(client.connected(), true);
36
36
  client.close();
37
37
  });
38
+ (0, node_test_1.default)("ChatGPT/Codex 桌面端使用 app 页面 target 时可以连接", async () => {
39
+ let connectedUrl = "";
40
+ const client = new chatgpt_1.ChatgptCdpClient({
41
+ timeoutMs: 100,
42
+ fetchImpl: async () => new Response(JSON.stringify([{ type: "page", title: "请只回复:CDP连通测试成功", url: "app://-/index.html", webSocketDebuggerUrl: "ws://127.0.0.1:9230/devtools/page/2" }])),
43
+ webSocketFactory: (url) => {
44
+ connectedUrl = url.toString();
45
+ return { set onmessage(_) { }, set onclose(_) { }, set onerror(_) { }, set onopen(handler) { queueMicrotask(handler); }, close() { }, send() { } };
46
+ },
47
+ });
48
+ await client.connect();
49
+ strict_1.default.equal(connectedUrl, "ws://127.0.0.1:9230/devtools/page/2");
50
+ client.close();
51
+ });
38
52
  (0, node_test_1.default)("ChatGPT CDP 只返回本轮新用户消息之后的回复", async () => {
39
53
  let messageHandler;
40
54
  let sentText = "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mu-agent-bridge",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "将 AUN Agent 连接到本地桌面 AI 客户端的插件平台。",
5
5
  "main": "dist/src-ts/cli.js",
6
6
  "bin": {
@@ -5,6 +5,7 @@ const node_child_process_1 = require("node:child_process");
5
5
  exports.editorSelector = '[data-codex-composer="true"][contenteditable="true"]';
6
6
  exports.turnSelector = '[data-chatgpt-conversation-turn="true"]';
7
7
  exports.userBubbleSelector = "[data-user-message-bubble]";
8
+ const assistantMessageSelector = '[data-markdown-text-style="assistant-message"]';
8
9
  const turnIdAttribute = "data-chatgpt-conversation-turn-id";
9
10
  const contentUnitSelector = "[data-content-search-unit-key]";
10
11
  const sendButtonSelector = 'button[aria-label="Send"], button[aria-label="发送"]';
@@ -88,7 +89,8 @@ class ChatgptCdpClient {
88
89
  if (!response.ok)
89
90
  throw new Error(`ChatGPT CDP /json HTTP ${response.status}`);
90
91
  const targets = await response.json();
91
- const page = targets.find((target) => target.type === "page" && /chatgpt|codex/i.test(`${target.title} ${target.url}`));
92
+ const page = targets.find((target) => target.type === "page" && (/chatgpt|codex/i.test(`${target.title} ${target.url}`)
93
+ || target.url === "app://-/index.html"));
92
94
  if (page?.webSocketDebuggerUrl)
93
95
  return page;
94
96
  last = new Error(`未找到 ChatGPT/Codex 页面 target(端口 ${this.port})`);
@@ -111,6 +113,7 @@ class ChatgptCdpClient {
111
113
  await this.waitFor(`Boolean(document.querySelector(${JSON.stringify(exports.editorSelector)}))`, "ChatGPT 桌面端输入框未就绪");
112
114
  const beforeTurnsJson = await this.evaluate(`JSON.stringify([...document.querySelectorAll(${JSON.stringify(exports.turnSelector)})].map(turn => ({ id: turn.getAttribute(${JSON.stringify(turnIdAttribute)}), users: turn.querySelectorAll(${JSON.stringify(exports.userBubbleSelector)}).length })))`);
113
115
  const beforeTurns = JSON.parse(String(beforeTurnsJson || "[]"));
116
+ const beforeAssistantCount = Number(await this.evaluate(`document.querySelectorAll(${JSON.stringify(assistantMessageSelector)}).length`));
114
117
  const ready = await this.evaluate(`(() => {
115
118
  const editor = document.querySelector(${JSON.stringify(exports.editorSelector)});
116
119
  if (!editor || editor.innerText.trim()) return false;
@@ -164,7 +167,13 @@ class ChatgptCdpClient {
164
167
  const fresh = oldIndex < 0 || index >= before.length || users.length > (old?.users ?? 0);
165
168
  return fresh && users.item(users.length - 1)?.innerText.trim() === ${JSON.stringify(text.trim())};
166
169
  });
167
- if (!turn) return JSON.stringify({ reply: "", active: false });
170
+ if (!turn) {
171
+ const assistants = [...document.querySelectorAll(${JSON.stringify(assistantMessageSelector)})];
172
+ const fallback = assistants.length > ${beforeAssistantCount}
173
+ ? assistants.at(-1)?.innerText.trim() ?? ""
174
+ : "";
175
+ return JSON.stringify({ reply: fallback, active: Boolean(document.querySelector(${JSON.stringify(stopButtonSelector)})) });
176
+ }
168
177
  const user = turn.querySelectorAll(${JSON.stringify(exports.userBubbleSelector)}).item(turn.querySelectorAll(${JSON.stringify(exports.userBubbleSelector)}).length - 1);
169
178
  const unit = user?.closest(${JSON.stringify(contentUnitSelector)});
170
179
  const units = [...turn.querySelectorAll(${JSON.stringify(contentUnitSelector)})];