agent-bridge-doubao 1.0.8 → 1.0.10

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.
@@ -1,8 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DoubaoCdpClient = exports.responseTextScript = void 0;
4
+ exports.doubaoStreamErrorMessage = doubaoStreamErrorMessage;
4
5
  exports.requestBody = requestBody;
5
6
  const node_crypto_1 = require("node:crypto");
7
+ function doubaoStreamErrorMessage(data) {
8
+ let code = "";
9
+ let subtype = "";
10
+ try {
11
+ const value = JSON.parse(data);
12
+ code = String(value.code ?? value.error?.code ?? "");
13
+ subtype = String(value.subtype ?? value.error?.subtype ?? "").toLowerCase();
14
+ }
15
+ catch { /* 平台可能返回非 JSON 错误 */ }
16
+ if (code === "710022004" || subtype === "slide" || /rate limited|verify|slide/i.test(data)) {
17
+ return `豆包平台触发滑块验证${code ? `(错误码 ${code})` : ""},请在豆包客户端完成验证后重试`;
18
+ }
19
+ return `豆包平台返回流式错误${code ? `(错误码 ${code})` : ""},请稍后重试`;
20
+ }
6
21
  const defaults = { port: Number(process.env.DOUBAO_CDP_PORT ?? "9227"), conversationId: process.env.DOUBAO_CONVERSATION_ID ?? "759450172544514", botId: process.env.DOUBAO_BOT_ID ?? "7338286299411103781", timeoutMs: Number(process.env.DOUBAO_TIMEOUT_MS ?? "180000") };
7
22
  function sleep(ms, signal) {
8
23
  if (signal?.aborted)
@@ -18,6 +33,7 @@ exports.responseTextScript = `(value) => { const texts = []; const visit = (item
18
33
  class DoubaoCdpClient {
19
34
  socket;
20
35
  sequence = 0;
36
+ closeController = new AbortController();
21
37
  pending = new Map();
22
38
  options;
23
39
  constructor(options = {}) { this.options = { port: options.port ?? defaults.port, conversationId: options.conversationId ?? defaults.conversationId, botId: options.botId ?? defaults.botId, pluginList: options.pluginList ?? [], timeoutMs: options.timeoutMs ?? defaults.timeoutMs, fetchImpl: options.fetchImpl }; }
@@ -37,13 +53,29 @@ class DoubaoCdpClient {
37
53
  socket.onmessage = event => this.onMessage(String(event.data));
38
54
  await new Promise((resolve, reject) => { socket.onopen = () => resolve(); socket.onerror = () => reject(new Error("豆包 CDP WebSocket 连接失败")); });
39
55
  }
40
- close() { this.socket?.close(); this.socket = undefined; for (const pending of this.pending.values())
41
- pending.reject(new Error("豆包 CDP 已关闭")); this.pending.clear(); }
56
+ async cancel() {
57
+ this.closeController.abort();
58
+ if (this.socket) {
59
+ try {
60
+ await this.evaluate("window.__mab_doubao_controller?.abort()");
61
+ }
62
+ catch { /* CDP 已断开时无需等待页面确认 */ }
63
+ }
64
+ }
65
+ async close() {
66
+ await this.cancel();
67
+ this.socket?.close();
68
+ this.socket = undefined;
69
+ for (const pending of this.pending.values())
70
+ pending.reject(new Error("豆包 CDP 已关闭"));
71
+ this.pending.clear();
72
+ }
42
73
  async direct(text, signal) {
43
74
  await this.evaluate(buildInjection(requestBody(text, this.options.conversationId, this.options.botId, this.options.pluginList)));
44
75
  const deadline = Date.now() + this.options.timeoutMs;
76
+ const requestSignal = signal ? AbortSignal.any([signal, this.closeController.signal]) : this.closeController.signal;
45
77
  while (Date.now() < deadline) {
46
- await sleep(1000, signal);
78
+ await sleep(1000, requestSignal);
47
79
  const result = await this.evaluate("window.__mab_doubao_result");
48
80
  if (typeof result !== "string")
49
81
  continue;
@@ -64,7 +96,13 @@ class DoubaoCdpClient {
64
96
  return; const pending = this.pending.get(message.id); if (!pending)
65
97
  return; this.pending.delete(message.id); pending.resolve(message); }
66
98
  async cdp(method, params = {}) { if (!this.socket)
67
- throw new Error("豆包 CDP 尚未连接"); const id = ++this.sequence; const result = new Promise((resolve, reject) => this.pending.set(id, { resolve, reject })); this.socket.send(JSON.stringify({ id, method, params })); return Promise.race([result, sleep(this.options.timeoutMs).then(() => { throw new Error(`CDP ${method} 超时`); })]); }
99
+ throw new Error("豆包 CDP 尚未连接"); const id = ++this.sequence; const result = new Promise((resolve, reject) => this.pending.set(id, { resolve, reject })); let timer; try {
100
+ this.socket.send(JSON.stringify({ id, method, params }));
101
+ return await Promise.race([result, new Promise((_, reject) => { timer = setTimeout(() => { this.pending.delete(id); reject(new Error(`CDP ${method} 超时`)); }, this.options.timeoutMs); })]);
102
+ }
103
+ finally {
104
+ clearTimeout(timer);
105
+ } }
68
106
  async evaluate(expression) { const response = await this.cdp("Runtime.evaluate", { expression, returnByValue: true, awaitPromise: false }); if (response.result?.exceptionDetails)
69
107
  throw new Error(`豆包页面 JS 异常: ${JSON.stringify(response.result.exceptionDetails).slice(0, 300)}`); return response.result?.result?.value; }
70
108
  }
@@ -73,6 +111,8 @@ function buildInjection(body) {
73
111
  const url = "https://www.doubao.com/chat/completion?aid=582478&channel=win&chromium_version=147.0.7727.149&client_platform=pc_client&device_id=2750296938275162&device_platform=web&doubao_device_platform=desktop&doubao_pc_version=2.30.4&fp=verify_2750296938275162&language=zh&pc_version=2.30.4&pkg_type=release_version&real_aid=582478&region=CN&runtime=web&runtime_version=3.37.7&samantha_web=1&sys_region=CN&tea_uuid=2750296938275162&tz_name=Asia%2FShanghai&use-olympus-account=1&version_code=20800&web_platform=desktop";
74
112
  return String.raw `(() => {
75
113
  window.__mab_doubao_result = null;
114
+ window.__mab_doubao_controller?.abort();
115
+ window.__mab_doubao_controller = new AbortController();
76
116
  const merge = (a, b) => { if (!b || a.endsWith(b)) return a; const n = Math.min(a.length, b.length); let k = 0; for (let i = 1; i <= n; i++) if (a.endsWith(b.slice(0, i))) k = i; return a + (k >= 4 ? b.slice(k) : b); };
77
117
  (async () => {
78
118
  try {
@@ -81,11 +121,11 @@ function buildInjection(body) {
81
121
  webId ||= '7689385646880933426'; tabId ||= '98f6fc8e-a1f7-4585-aa08-89b9a10aa451';
82
122
  let requestUrl = ${JSON.stringify(url)} + '&web_id=' + encodeURIComponent(webId) + '&web_tab_id=' + encodeURIComponent(tabId);
83
123
  for (const entry of performance.getEntriesByType('resource')) if (entry.name.includes('/chat/completion?')) requestUrl = entry.name;
84
- const response = await fetch(requestUrl, { method: 'POST', headers: { 'content-type': 'application/json' }, credentials: 'include', body: ${JSON.stringify(body)} });
124
+ const response = await fetch(requestUrl, { method: 'POST', headers: { 'content-type': 'application/json' }, credentials: 'include', body: ${JSON.stringify(body)}, signal: window.__mab_doubao_controller.signal });
85
125
  if (!response.ok) throw new Error('HTTP ' + response.status);
86
126
  const reader = response.body?.getReader(); if (!reader) throw new Error('响应无 body');
87
127
  const decoder = new TextDecoder(); let buffer = '', reply = '', events = [];
88
- const consume = (block) => { const event = (block.match(/^event:\s*(.+)$/m) || [])[1] || ''; const data = (block.match(/^data:\s*(.+)$/m) || [])[1] || ''; if (event) events.push(event); if (event === 'STREAM_ERROR') throw new Error('STREAM_ERROR ' + data.slice(0, 500)); if (!data) return; try { const json = JSON.parse(data); if (event === 'STREAM_CHUNK') for (const patch of (json.patch_op || [])) reply = merge(reply, patch.patch_value?.tts_content || ''); if (event === 'CHUNK_DELTA' && typeof json.text === 'string') reply = merge(reply, json.text); if (event === 'STREAM_MSG_NOTIFY' || event === 'FULL_MSG_NOTIFY') for (const text of (${exports.responseTextScript})(json)) reply = merge(reply, text); if (event === 'SSE_REPLY_END') reply = merge(reply, json.msg_finish_attr?.brief || json.data?.msg_finish_attr?.brief || ''); } catch {} };
128
+ const consume = (block) => { const event = (block.match(/^event:\s*(.+)$/m) || [])[1] || ''; const data = (block.match(/^data:\s*(.+)$/m) || [])[1] || ''; if (event) events.push(event); if (event === 'STREAM_ERROR') throw new Error((${doubaoStreamErrorMessage.toString()})(data)); if (!data) return; try { const json = JSON.parse(data); if (event === 'STREAM_CHUNK') for (const patch of (json.patch_op || [])) reply = merge(reply, patch.patch_value?.tts_content || ''); if (event === 'CHUNK_DELTA' && typeof json.text === 'string') reply = merge(reply, json.text); if (event === 'STREAM_MSG_NOTIFY' || event === 'FULL_MSG_NOTIFY') for (const text of (${exports.responseTextScript})(json)) reply = merge(reply, text); if (event === 'SSE_REPLY_END') reply = merge(reply, json.msg_finish_attr?.brief || json.data?.msg_finish_attr?.brief || ''); } catch {} };
89
129
  for (;;) { const item = await reader.read(); if (item.done) break; buffer += decoder.decode(item.value, { stream: true }).replace(/\r\n/g, '\n'); let index; while ((index = buffer.indexOf('\n\n')) >= 0) { consume(buffer.slice(0, index)); buffer = buffer.slice(index + 2); } }
90
130
  if (buffer.trim()) consume(buffer);
91
131
  window.__mab_doubao_result = JSON.stringify({ ok: Boolean(reply.trim()), reply, reason: reply.trim() ? '' : 'SSE 未提取到文本; events=' + events.slice(0, 20).join(',') });
@@ -6,7 +6,7 @@ const plugin_1 = require("../../plugin");
6
6
  const desktop_launch_1 = require("../desktop-launch");
7
7
  exports.doubaoManifest = {
8
8
  id: "doubao",
9
- version: "1.0.8",
9
+ version: "1.0.10",
10
10
  displayName: "豆包",
11
11
  entry: "dist/index.js",
12
12
  apiVersion: "1",
@@ -67,7 +67,7 @@ class DoubaoPlugin {
67
67
  async deactivate() {
68
68
  this.disposer?.();
69
69
  this.disposer = undefined;
70
- this.client?.close();
70
+ await this.client?.close();
71
71
  this.client = undefined;
72
72
  this.state = "stopped";
73
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-doubao",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "agent-bridge 的豆包插件。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "doubao",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "type": "agent",
5
5
  "displayName": "豆包",
6
6
  "entry": "dist/src-ts/plugins/doubao/index.js",