agent-bridge-doubao 1.0.8 → 1.0.9
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,22 @@ 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() {
|
|
41
|
-
|
|
56
|
+
close() {
|
|
57
|
+
this.closeController.abort();
|
|
58
|
+
if (this.socket)
|
|
59
|
+
this.socket.send(JSON.stringify({ id: ++this.sequence, method: "Runtime.evaluate", params: { expression: "window.__mab_doubao_controller?.abort()" } }));
|
|
60
|
+
this.socket?.close();
|
|
61
|
+
this.socket = undefined;
|
|
62
|
+
for (const pending of this.pending.values())
|
|
63
|
+
pending.reject(new Error("豆包 CDP 已关闭"));
|
|
64
|
+
this.pending.clear();
|
|
65
|
+
}
|
|
42
66
|
async direct(text, signal) {
|
|
43
67
|
await this.evaluate(buildInjection(requestBody(text, this.options.conversationId, this.options.botId, this.options.pluginList)));
|
|
44
68
|
const deadline = Date.now() + this.options.timeoutMs;
|
|
69
|
+
const requestSignal = signal ? AbortSignal.any([signal, this.closeController.signal]) : this.closeController.signal;
|
|
45
70
|
while (Date.now() < deadline) {
|
|
46
|
-
await sleep(1000,
|
|
71
|
+
await sleep(1000, requestSignal);
|
|
47
72
|
const result = await this.evaluate("window.__mab_doubao_result");
|
|
48
73
|
if (typeof result !== "string")
|
|
49
74
|
continue;
|
|
@@ -73,6 +98,8 @@ function buildInjection(body) {
|
|
|
73
98
|
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®ion=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
99
|
return String.raw `(() => {
|
|
75
100
|
window.__mab_doubao_result = null;
|
|
101
|
+
window.__mab_doubao_controller?.abort();
|
|
102
|
+
window.__mab_doubao_controller = new AbortController();
|
|
76
103
|
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
104
|
(async () => {
|
|
78
105
|
try {
|
|
@@ -81,11 +108,11 @@ function buildInjection(body) {
|
|
|
81
108
|
webId ||= '7689385646880933426'; tabId ||= '98f6fc8e-a1f7-4585-aa08-89b9a10aa451';
|
|
82
109
|
let requestUrl = ${JSON.stringify(url)} + '&web_id=' + encodeURIComponent(webId) + '&web_tab_id=' + encodeURIComponent(tabId);
|
|
83
110
|
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)} });
|
|
111
|
+
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
112
|
if (!response.ok) throw new Error('HTTP ' + response.status);
|
|
86
113
|
const reader = response.body?.getReader(); if (!reader) throw new Error('响应无 body');
|
|
87
114
|
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(
|
|
115
|
+
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
116
|
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
117
|
if (buffer.trim()) consume(buffer);
|
|
91
118
|
window.__mab_doubao_result = JSON.stringify({ ok: Boolean(reply.trim()), reply, reason: reply.trim() ? '' : 'SSE 未提取到文本; events=' + events.slice(0, 20).join(',') });
|
package/package.json
CHANGED