mu-agent-bridge 1.0.73 → 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 +12 -8
- package/dist/src-ts/cli.js +67 -37
- package/dist/src-ts/control-server.js +7 -28
- 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 +7 -2
- package/dist/src-ts/http-agent.js +36 -0
- package/dist/src-ts/plugin-manager.js +185 -58
- package/dist/src-ts/plugins/abweb/index.js +1 -1
- package/dist/src-ts/plugins/abweb/public/app.js +8 -6
- package/dist/src-ts/plugins/abweb/public/index.html +3 -3
- package/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/dist/src-ts/plugins/aun/index.js +89 -11
- package/dist/src-ts/plugins/aun/messages.js +1 -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/index.js +3 -2
- package/dist/src-ts/plugins/claude-cli/index.js +1 -1
- package/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
- package/dist/src-ts/plugins/doubao/index.js +119 -7
- 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 +102 -23
- 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/personal-assistant/index.js +29 -8
- package/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
- package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/dist/src-ts/plugins/personal-assistant/tools.js +4 -3
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +43 -7
- package/dist/src-ts/plugins/qwenwork/index.js +52 -18
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
- package/dist/src-ts/plugins/workbuddy/index.js +24 -12
- 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 +27 -8
- package/dist/src-ts/version-manager.js +6 -3
- package/dist/tests-ts/abweb-model-config.test.js +44 -41
- 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 +14 -0
- package/dist/tests-ts/aun-plugin.test.js +207 -19
- package/dist/tests-ts/bundled-package.test.js +1 -1
- package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
- package/dist/tests-ts/chatgpt-plugin.test.js +19 -0
- package/dist/tests-ts/control-server.test.js +20 -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 +688 -19
- package/dist/tests-ts/dsh-agent.test.js +91 -19
- 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/local-cli.test.js +29 -2
- package/dist/tests-ts/model-provider-cancellation.test.js +2 -0
- package/dist/tests-ts/native-session-management.test.js +8 -7
- package/dist/tests-ts/personal-assistant-coordination.test.js +16 -0
- package/dist/tests-ts/personal-assistant-extensions.test.js +7 -1
- package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
- package/dist/tests-ts/personal-assistant.test.js +277 -3
- package/dist/tests-ts/platform-integration.test.js +60 -1
- package/dist/tests-ts/plugin-manager.test.js +78 -6
- package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
- package/dist/tests-ts/publish-packages.test.js +1 -1
- package/dist/tests-ts/qwenwork-plugin.test.js +30 -0
- 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 +8 -8
- 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 +356 -13
- 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/plugins/abweb/index.js +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +8 -6
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +7 -5
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +3 -3
- 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/plugins/aun/attachments.js +3 -21
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +89 -11
- package/plugins/aun/dist/src-ts/plugins/aun/messages.js +1 -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/plugins/cdp-draft.js +5 -2
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +3 -2
- package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +1 -1
- package/plugins/claude-cli/package.json +1 -1
- package/plugins/claude-cli/plugin.json +1 -1
- package/plugins/doubao/config.schema.json +2 -1
- package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +231 -45
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +119 -7
- 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 +1 -1
- package/plugins/dsh/config.schema.json +5 -2
- package/plugins/dsh/dist/src-ts/dsh-agent.js +7 -2
- 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 +102 -23
- 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 +1 -1
- 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 +3 -2
- package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +29 -8
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +20 -6
- 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 +4 -3
- package/plugins/personal-assistant/package.json +2 -2
- package/plugins/personal-assistant/plugin.json +1 -1
- 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 +43 -7
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +52 -18
- package/plugins/qwenwork/package.json +1 -1
- package/plugins/qwenwork/plugin.json +1 -1
- package/plugins/workbuddy/config.schema.json +1 -1
- package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +349 -83
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +24 -12
- package/plugins/workbuddy/package.json +1 -1
- package/plugins/workbuddy/plugin.json +1 -1
- 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
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.doubaoHistoryReadPageScript = exports.doubaoHistoryPageScript = exports.DoubaoCdpClient = exports.currentResponseArtifactsScript = exports.responseArtifactsScript = exports.responseImagesScript = exports.responseTextScript = void 0;
|
|
3
|
+
exports.doubaoRequestUrlScript = exports.doubaoHistoryReadPageScript = exports.doubaoHistoryPageScript = exports.DoubaoCdpClient = exports.currentResponseArtifactsScript = exports.responseArtifactsScript = exports.responseImagesScript = exports.responseTextScript = void 0;
|
|
4
4
|
exports.doubaoStreamErrorMessage = doubaoStreamErrorMessage;
|
|
5
5
|
exports.requestBody = requestBody;
|
|
6
6
|
exports.mergeDoubaoReply = mergeDoubaoReply;
|
|
7
7
|
exports.cleanDoubaoReply = cleanDoubaoReply;
|
|
8
|
+
exports.buildLocalComputerInjection = buildLocalComputerInjection;
|
|
8
9
|
exports.downloadDoubaoArtifact = downloadDoubaoArtifact;
|
|
9
10
|
exports.downloadDoubaoImage = downloadDoubaoImage;
|
|
10
11
|
const node_crypto_1 = require("node:crypto");
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const promises_1 = require("node:fs/promises");
|
|
14
|
+
const node_path_1 = require("node:path");
|
|
15
|
+
const agent_1 = require("../../agent");
|
|
11
16
|
const attachments_1 = require("../../attachments");
|
|
12
17
|
const attachments_2 = require("./attachments");
|
|
18
|
+
const native_task_1 = require("./native-task");
|
|
13
19
|
function doubaoStreamErrorMessage(data) {
|
|
14
20
|
let code = "";
|
|
15
21
|
let message = "";
|
|
@@ -38,12 +44,20 @@ function doubaoStreamErrorMessage(data) {
|
|
|
38
44
|
return `豆包平台返回流式错误${code ? `(错误码 ${code})` : ""}`;
|
|
39
45
|
}
|
|
40
46
|
const defaults = { port: Number(process.env.DOUBAO_CDP_PORT ?? "9227"), conversationId: process.env.DOUBAO_CONVERSATION_ID ?? "", botId: process.env.DOUBAO_BOT_ID ?? "7338286299411103781", timeoutMs: Number(process.env.DOUBAO_TIMEOUT_MS ?? "180000") };
|
|
47
|
+
const MAX_PREALLOCATION_ATTEMPTS = 3;
|
|
48
|
+
const CHAT_AGENT_MODE = 2;
|
|
49
|
+
const LOCAL_COMPUTER_AGENT_MODE = 1;
|
|
41
50
|
function sleep(ms, signal) {
|
|
42
51
|
if (signal?.aborted)
|
|
43
52
|
return Promise.reject(new Error("请求已取消"));
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const abort = () => { clearTimeout(timer); reject(new Error("请求已取消")); };
|
|
55
|
+
const timer = setTimeout(() => { signal?.removeEventListener("abort", abort); resolve(); }, ms);
|
|
56
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
57
|
+
});
|
|
45
58
|
}
|
|
46
|
-
function requestBody(text, conversationId, botId, pluginList = [], attachments = []) {
|
|
59
|
+
function requestBody(text, conversationId, botId, pluginList = [], attachments = [], mode = "chat") {
|
|
60
|
+
const agentMode = mode === "local-computer" ? LOCAL_COMPUTER_AGENT_MODE : CHAT_AGENT_MODE;
|
|
47
61
|
const fresh = !conversationId;
|
|
48
62
|
const general = fresh ? { server_do_compare: true } : { runtime_type: 0, agent_task_param: {}, agent_task_param_change: { runtime_changed: false, device_changed: false, sandbox_auth_type_changed: false }, project_id: "", need_modify_conversation: false };
|
|
49
63
|
const block = { block_type: 10000, content: { text_block: { text, icon_url: "", icon_url_dark: "", summary: "" }, pc_event_block: "" }, block_id: (0, node_crypto_1.randomUUID)(), parent_id: "", meta_info: [], append_fields: [] };
|
|
@@ -56,7 +70,7 @@ function requestBody(text, conversationId, botId, pluginList = [], attachments =
|
|
|
56
70
|
parse_state: item.type === "image" ? 0 : 1, review_state: 1, upload_status: 1, progress: 100, src: "",
|
|
57
71
|
})) }, pc_event_block: "" } }], message_status: 0 });
|
|
58
72
|
const localConversationId = `local_${Date.now()}${Math.floor(Math.random() * 1000).toString().padStart(3, "0")}`;
|
|
59
|
-
return JSON.stringify({ client_meta: { ...(fresh ? { local_conversation_id: localConversationId } : {}), conversation_id: conversationId, bot_id: botId, last_section_id: "", last_message_index: null, local_permissions: [{ permission_name: "ACCESS_COARSE_LOCATION", status: 1 }, { permission_name: "ACCESS_FINE_LOCATION", status: 1 }, { permission_name: "ACCESS_BACKGROUND_LOCATION", status: 1 }] }, messages, option: { send_message_scene: "", create_time_ms: Date.now(), collect_id: "", is_audio: false, answer_with_suggest: false, agent_mode:
|
|
73
|
+
return JSON.stringify({ client_meta: { ...(fresh ? { local_conversation_id: localConversationId } : {}), conversation_id: conversationId, bot_id: botId, last_section_id: "", last_message_index: null, local_permissions: [{ permission_name: "ACCESS_COARSE_LOCATION", status: 1 }, { permission_name: "ACCESS_FINE_LOCATION", status: 1 }, { permission_name: "ACCESS_BACKGROUND_LOCATION", status: 1 }] }, messages, option: { send_message_scene: "", create_time_ms: Date.now(), collect_id: "", is_audio: false, answer_with_suggest: false, agent_mode: agentMode, tts_switch: false, need_deep_think: 0, click_clear_context: false, from_suggest: false, is_regen: false, is_replace: false, is_from_click_option: false, is_from_click_softlink: false, disable_sse_cache: false, select_text_action: "", is_select_text: false, resend_for_regen: false, scene_type: 0, unique_key: (0, node_crypto_1.randomUUID)(), start_seq: 0, need_create_conversation: fresh, ...(fresh ? { conversation_init_option: { need_ack_conversation: true }, conversation_init_ext: { model_item_key: "0", reasoning_effort: "3", mode_id: "1" } } : {}), regen_query_id: [], edit_query_id: [], regen_instruction: "", no_replace_for_regen: false, message_from: 0, shared_app_name: "", shared_app_id: "", sse_recv_event_options: { support_chunk_delta: true }, support_lazy_fetch_stream: true, is_ai_playground: false, is_old_user: true, general_task_param: general, recovery_option: { is_recovery: false, req_create_time_sec: Math.floor(Date.now() / 1000), append_sse_event_scene: 0 }, message_storage_type: 0, related_deleted_message_ids: {}, connector_info_list: [], model_config: { model_item_key: "0", model_extra_params: {}, reasoning_effort: fresh ? 3 : 5 }, aggregate_params: { mention_skill_list: "[]", mention_plugin_list: JSON.stringify(pluginList), mention_ext: "[{}]", conversation_mode: fresh ? "1" : "", mode_id: "1", model_item_key: "0", agent_mode: String(agentMode), reasoning_effort: fresh ? "3" : "5", provider_id: "" }, ...(fresh ? { conversation_mode: 1 } : {}) }, user_context: [], ext: { ...(fresh ? { sub_conv_firstmet_type: "1", conversation_init_option: JSON.stringify({ need_ack_conversation: true }) } : {}), agent_mode: String(agentMode), use_deep_think: "0", general_task_param: JSON.stringify(general), collection_id: "", is_finish: "1", commerce_credit_config_enable: "0" } });
|
|
60
74
|
}
|
|
61
75
|
exports.responseTextScript = `(value) => { const texts = []; const visit = (item) => { if (Array.isArray(item)) { for (const child of item) visit(child); return; } if (!item || typeof item !== 'object') return; if (item.text_block && typeof item.text_block.text === 'string') texts.push(item.text_block.text); for (const child of Object.values(item)) visit(child); }; visit(value); return texts; }`;
|
|
62
76
|
function mergeDoubaoReply(current, next) {
|
|
@@ -95,16 +109,17 @@ exports.responseImagesScript = `(value) => {
|
|
|
95
109
|
};
|
|
96
110
|
visit(value); return [...urls];
|
|
97
111
|
}`;
|
|
98
|
-
//
|
|
99
|
-
exports.responseArtifactsScript = `(value) => {
|
|
112
|
+
// 只读取平台原生产物块;本地文件必须来自当前任务工作区。
|
|
113
|
+
exports.responseArtifactsScript = `(value, workspace) => {
|
|
100
114
|
const artifacts = [], seen = new Set(); let pending = false, failed = false;
|
|
101
115
|
const add = (item) => { const key = JSON.stringify(item); if (!seen.has(key)) { seen.add(key); artifacts.push(item); } };
|
|
102
116
|
const visit = (item) => {
|
|
103
117
|
if (Array.isArray(item)) { for (const child of item) visit(child); return; }
|
|
104
118
|
if (!item || typeof item !== 'object') return;
|
|
105
119
|
const type = Number(item.block_type), content = item.content || item.content_obj;
|
|
106
|
-
if (type === 2074) {
|
|
107
|
-
|
|
120
|
+
if (type === 2074 || type === 10084) {
|
|
121
|
+
const creations = type === 10084 ? (content?.rich_media_layout_block?.media || []).map(media => media?.creation).filter(Boolean) : (content?.creation_block || content)?.creations || [];
|
|
122
|
+
for (const creation of creations) {
|
|
108
123
|
const image = creation.image, video = creation.video;
|
|
109
124
|
if (image?.status === 2 && item.is_finish === true && image.image_ori?.url) add({ kind: 'image', url: image.image_ori.url });
|
|
110
125
|
if (image && (image.status === 1 || item.is_finish === false)) pending = true;
|
|
@@ -121,10 +136,20 @@ exports.responseArtifactsScript = `(value) => {
|
|
|
121
136
|
else pending = true;
|
|
122
137
|
}
|
|
123
138
|
}
|
|
139
|
+
if (type === 10019) {
|
|
140
|
+
const file = content?.file_operation_block;
|
|
141
|
+
if ((file?.file_type === 'file' || file?.file_type === 'diff') && typeof file.path === 'string' && file.path && typeof workspace === 'string' && workspace) {
|
|
142
|
+
if (item.is_finish === true) add({ kind: 'file', filename: file.path.split(/[\\/]/).pop(), path: file.path, workspace });
|
|
143
|
+
else pending = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
124
146
|
for (const child of Object.values(item)) visit(child);
|
|
125
147
|
};
|
|
126
148
|
visit(value); return { artifacts, pending, ...(failed ? { failed: true } : {}) };
|
|
127
149
|
}`;
|
|
150
|
+
function buildLocalComputerInjection(body) {
|
|
151
|
+
return `(${native_task_1.nativeTaskScript})(${body}, ${exports.responseTextScript}, ${exports.responseArtifactsScript}, ${exports.doubaoRequestUrlScript})`;
|
|
152
|
+
}
|
|
128
153
|
// 异步生成会另发消息,必须通过本轮问题引用关联,不能拿当前页面最近的任意媒体。
|
|
129
154
|
exports.currentResponseArtifactsScript = String.raw `(conversationId, questionIds, messageIds) => {
|
|
130
155
|
if (location.pathname !== '/chat/' + conversationId) return { artifacts: [], pending: true };
|
|
@@ -147,6 +172,37 @@ exports.currentResponseArtifactsScript = String.raw `(conversationId, questionId
|
|
|
147
172
|
}`;
|
|
148
173
|
async function downloadDoubaoArtifact(item, fetchImpl = fetch, signal, budget = attachments_1.MAX_ATTACHMENT_BYTES) {
|
|
149
174
|
const label = item.kind === "image" ? "图片" : item.kind === "video" ? "视频" : "文件";
|
|
175
|
+
const limit = Math.min(attachments_1.MAX_ATTACHMENT_BYTES, budget);
|
|
176
|
+
if (limit <= 0)
|
|
177
|
+
throw new Error("豆包附件总大小过大(上限 64 MiB)");
|
|
178
|
+
if (item.path) {
|
|
179
|
+
if (!item.workspace)
|
|
180
|
+
throw new Error("豆包本地产物缺少任务工作区");
|
|
181
|
+
const workspace = await (0, promises_1.realpath)(item.workspace);
|
|
182
|
+
const candidate = (0, node_path_1.isAbsolute)(item.path) ? item.path : (0, node_path_1.resolve)(workspace, item.path);
|
|
183
|
+
const filePath = await (0, promises_1.realpath)(candidate);
|
|
184
|
+
const relativePath = (0, node_path_1.relative)(workspace, filePath);
|
|
185
|
+
if (!relativePath || relativePath === ".." || relativePath.startsWith(`..${node_path_1.sep}`) || (0, node_path_1.isAbsolute)(relativePath))
|
|
186
|
+
throw new Error("豆包文件不在任务工作区内");
|
|
187
|
+
const info = await (0, promises_1.stat)(filePath);
|
|
188
|
+
if (!info.isFile())
|
|
189
|
+
throw new Error("豆包本地产物不是普通文件");
|
|
190
|
+
if (info.size > limit)
|
|
191
|
+
throw new Error("豆包文件过大(单项上限 32 MiB,总计上限 64 MiB)");
|
|
192
|
+
const chunks = [];
|
|
193
|
+
let size = 0;
|
|
194
|
+
for await (const chunk of (0, node_fs_1.createReadStream)(filePath)) {
|
|
195
|
+
size += chunk.byteLength;
|
|
196
|
+
if (size > limit)
|
|
197
|
+
throw new Error("豆包文件过大(单项上限 32 MiB,总计上限 64 MiB)");
|
|
198
|
+
chunks.push(chunk);
|
|
199
|
+
}
|
|
200
|
+
if (!size || size !== info.size)
|
|
201
|
+
throw new Error("豆包本地产物读取不完整");
|
|
202
|
+
const attachment = { kind: "file", data: Buffer.concat(chunks), filename: (0, attachments_1.attachmentFilename)(item.filename ?? filePath.split(/[\\/]/).pop() ?? ""), contentType: "application/octet-stream" };
|
|
203
|
+
(0, attachments_1.validateAttachments)([attachment]);
|
|
204
|
+
return attachment;
|
|
205
|
+
}
|
|
150
206
|
let url;
|
|
151
207
|
try {
|
|
152
208
|
url = new URL(item.url ?? "");
|
|
@@ -160,9 +216,6 @@ async function downloadDoubaoArtifact(item, fetchImpl = fetch, signal, budget =
|
|
|
160
216
|
if (url.protocol !== "https:" || !trusted || url.username || url.password || (url.port && url.port !== "443"))
|
|
161
217
|
throw new Error(`豆包${label}地址无效`);
|
|
162
218
|
const filename = item.kind === "file" ? (0, attachments_1.attachmentFilename)(item.filename ?? "") : "";
|
|
163
|
-
const limit = Math.min(attachments_1.MAX_ATTACHMENT_BYTES, budget);
|
|
164
|
-
if (limit <= 0)
|
|
165
|
-
throw new Error("豆包附件总大小过大(上限 64 MiB)");
|
|
166
219
|
const response = await fetchImpl(url.href, { redirect: "error", signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(60_000)]) : AbortSignal.timeout(60_000) });
|
|
167
220
|
if (!response.ok)
|
|
168
221
|
throw new Error(`下载豆包${label} HTTP ${response.status}`);
|
|
@@ -220,7 +273,7 @@ class DoubaoCdpClient {
|
|
|
220
273
|
closeController = new AbortController();
|
|
221
274
|
pending = new Map();
|
|
222
275
|
options;
|
|
223
|
-
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 }; this.conversationId = this.options.conversationId; }
|
|
276
|
+
constructor(options = {}) { this.options = { port: options.port ?? defaults.port, conversationId: options.conversationId ?? defaults.conversationId, botId: options.botId ?? defaults.botId, pluginList: options.pluginList ?? [], mode: options.mode ?? "local-computer", timeoutMs: options.timeoutMs ?? defaults.timeoutMs, fetchImpl: options.fetchImpl, logger: options.logger, recoverCdp: options.recoverCdp }; this.conversationId = this.options.conversationId; }
|
|
224
277
|
connected() { return this.socket?.readyState === 1; }
|
|
225
278
|
async connect() {
|
|
226
279
|
if (this.closeController.signal.aborted)
|
|
@@ -229,18 +282,34 @@ class DoubaoCdpClient {
|
|
|
229
282
|
return;
|
|
230
283
|
if (this.connecting)
|
|
231
284
|
return this.connecting;
|
|
232
|
-
const opening =
|
|
285
|
+
const opening = (async () => {
|
|
286
|
+
for (let attempt = 1;; attempt++) {
|
|
287
|
+
try {
|
|
288
|
+
await this.openConnection();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
if (attempt >= 3 || !isRecoverableCdpError(error) || this.closeController.signal.aborted)
|
|
293
|
+
throw error;
|
|
294
|
+
this.options.logger?.warn(`豆包 CDP ${this.options.port} 建连失败,准备第 ${attempt + 1}/3 次重试:${(0, agent_1.errorDetails)(error)}`);
|
|
295
|
+
await sleep(1000, this.closeController.signal);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
})();
|
|
233
299
|
this.connecting = opening;
|
|
234
300
|
try {
|
|
235
301
|
await opening;
|
|
236
302
|
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
throw new agent_1.AgentOperationError("DOUBAO_CDP_UNAVAILABLE", `连接豆包 CDP(端口 ${this.options.port})`, error);
|
|
305
|
+
}
|
|
237
306
|
finally {
|
|
238
307
|
if (this.connecting === opening)
|
|
239
308
|
this.connecting = undefined;
|
|
240
309
|
}
|
|
241
310
|
}
|
|
242
311
|
async openConnection() {
|
|
243
|
-
const response = await (this.options.fetchImpl ?? fetch)(`http://127.0.0.1:${this.options.port}/json
|
|
312
|
+
const response = await (this.options.fetchImpl ?? fetch)(`http://127.0.0.1:${this.options.port}/json`, { signal: AbortSignal.any([this.closeController.signal, AbortSignal.timeout(Math.min(this.options.timeoutMs, 10_000))]) });
|
|
244
313
|
if (!response.ok)
|
|
245
314
|
throw new Error(`豆包 CDP /json HTTP ${response.status}`);
|
|
246
315
|
const targets = await response.json();
|
|
@@ -254,7 +323,9 @@ class DoubaoCdpClient {
|
|
|
254
323
|
this.socket = socket;
|
|
255
324
|
socket.onmessage = event => this.onMessage(String(event.data));
|
|
256
325
|
await new Promise((resolve, reject) => {
|
|
326
|
+
const timer = setTimeout(() => { disconnect(new Error("豆包 CDP WebSocket 连接超时")); socket.close(); }, Math.min(this.options.timeoutMs, 10_000));
|
|
257
327
|
const disconnect = (error) => {
|
|
328
|
+
clearTimeout(timer);
|
|
258
329
|
if (this.socket === socket) {
|
|
259
330
|
this.socket = undefined;
|
|
260
331
|
for (const pending of this.pending.values())
|
|
@@ -263,7 +334,7 @@ class DoubaoCdpClient {
|
|
|
263
334
|
}
|
|
264
335
|
reject(error);
|
|
265
336
|
};
|
|
266
|
-
socket.onopen = () => resolve();
|
|
337
|
+
socket.onopen = () => { clearTimeout(timer); resolve(); };
|
|
267
338
|
socket.onclose = () => disconnect(new Error("豆包 CDP 已断开"));
|
|
268
339
|
socket.onerror = () => { disconnect(new Error("豆包 CDP WebSocket 连接失败")); socket.close(); };
|
|
269
340
|
});
|
|
@@ -353,32 +424,130 @@ class DoubaoCdpClient {
|
|
|
353
424
|
throw new Error("请求已取消");
|
|
354
425
|
const target = conversationId ?? this.conversationId;
|
|
355
426
|
const uploaded = attachments?.length ? await this.uploadAttachments(attachments, requestSignal) : [];
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
427
|
+
const body = requestBody(text, target, this.options.botId, this.options.pluginList, uploaded, this.options.mode);
|
|
428
|
+
let attempts = 1;
|
|
429
|
+
const requestId = JSON.parse(body).option.unique_key;
|
|
430
|
+
let deadline = Date.now() + this.options.timeoutMs;
|
|
431
|
+
let progressSequence = 0;
|
|
432
|
+
let recovered = false;
|
|
433
|
+
const recover = async (error) => {
|
|
434
|
+
this.options.logger?.warn(`豆包 CDP ${this.options.port} 任务连接断开,重连并读取原任务 ${requestId},不会重复提交:${(0, agent_1.errorDetails)(error)}`);
|
|
435
|
+
// Runtime.evaluate 超时不一定触发 WebSocket close;先丢弃失效 socket,避免 connect() 误判为已连接。
|
|
436
|
+
const staleSocket = this.socket;
|
|
437
|
+
if (staleSocket && this.connected()) {
|
|
438
|
+
this.socket = undefined;
|
|
439
|
+
for (const pending of this.pending.values())
|
|
440
|
+
pending.reject(new Error("豆包 CDP 已断开,正在恢复连接"));
|
|
441
|
+
this.pending.clear();
|
|
442
|
+
try {
|
|
443
|
+
staleSocket.close();
|
|
444
|
+
}
|
|
445
|
+
catch { /* 旧连接关闭失败时继续重新发现页面 */ }
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
await this.connect();
|
|
449
|
+
}
|
|
450
|
+
catch (cause) {
|
|
451
|
+
if (!this.options.recoverCdp || !(0, agent_1.errorDetails)(cause).includes("ECONNREFUSED"))
|
|
452
|
+
throw new agent_1.AgentOperationError("DOUBAO_CDP_UNAVAILABLE", "恢复豆包 CDP(原任务结果未确认)", cause);
|
|
453
|
+
this.options.logger?.warn(`豆包 CDP ${this.options.port} 在读取原任务时拒绝连接,调用受控桌面恢复:${(0, agent_1.errorDetails)(cause)}`);
|
|
454
|
+
try {
|
|
455
|
+
await this.options.recoverCdp();
|
|
456
|
+
if (!this.connected())
|
|
457
|
+
await this.connect();
|
|
458
|
+
}
|
|
459
|
+
catch (recoveryError) {
|
|
460
|
+
throw new agent_1.AgentOperationError("DOUBAO_CDP_UNAVAILABLE", "受控恢复豆包桌面进程后的 CDP 重连(原任务结果未确认)", recoveryError);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
let restored;
|
|
464
|
+
try {
|
|
465
|
+
restored = await this.evaluate("window.__mab_doubao_request_id");
|
|
466
|
+
}
|
|
467
|
+
catch (cause) {
|
|
468
|
+
throw new agent_1.AgentOperationError("DOUBAO_RESULT_UNKNOWN", "确认豆包原任务(未重复提交)", cause);
|
|
469
|
+
}
|
|
470
|
+
if (restored !== requestId)
|
|
471
|
+
throw new agent_1.AgentOperationError("DOUBAO_RESULT_UNKNOWN", "恢复豆包原任务", new Error("原页面上下文或任务标识已丢失,结果未知;保留原会话,未重复提交"));
|
|
472
|
+
};
|
|
473
|
+
const submit = async () => {
|
|
474
|
+
try {
|
|
475
|
+
await this.evaluate(this.options.mode === "chat" ? buildInjection(body) : buildLocalComputerInjection(body));
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
if (isRecoverableCdpError(error)) {
|
|
479
|
+
await recover(error);
|
|
480
|
+
recovered = true;
|
|
481
|
+
}
|
|
482
|
+
else
|
|
483
|
+
throw new agent_1.AgentOperationError("DOUBAO_SUBMIT_UNKNOWN", "通过 CDP 提交豆包任务(接收状态未知,未重复提交)", error);
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
await submit();
|
|
487
|
+
while (Date.now() < deadline || signal?.aborted || recovered) {
|
|
359
488
|
// 上游取消只结束等待,页面请求结束前继续占用原队列。
|
|
360
|
-
|
|
361
|
-
const
|
|
489
|
+
// 重连耗时可能跨过期限,只额外读取一次,避免漏掉断线期间完成的原任务。
|
|
490
|
+
const finalRead = recovered && Date.now() >= deadline;
|
|
491
|
+
if (recovered)
|
|
492
|
+
recovered = false;
|
|
493
|
+
else
|
|
494
|
+
await sleep(1000, this.closeController.signal);
|
|
495
|
+
let result;
|
|
496
|
+
let progress;
|
|
497
|
+
try {
|
|
498
|
+
result = await this.evaluate("window.__mab_doubao_result");
|
|
499
|
+
if (typeof result !== "string" && this.options.mode === "local-computer")
|
|
500
|
+
progress = await this.evaluate("window.__mab_doubao_progress");
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
if (!isRecoverableCdpError(error))
|
|
504
|
+
throw new agent_1.AgentOperationError("DOUBAO_CDP_READ_FAILED", "通过 CDP 读取豆包任务结果", error);
|
|
505
|
+
if (finalRead && !signal?.aborted)
|
|
506
|
+
break;
|
|
507
|
+
await recover(error);
|
|
508
|
+
recovered = true;
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
// 只按本轮新流事件续期,反复读取同一快照不能延长等待。
|
|
512
|
+
if (progress?.requestId === requestId && typeof progress.sequence === "number" && Number.isSafeInteger(progress.sequence) && progress.sequence > progressSequence) {
|
|
513
|
+
progressSequence = progress.sequence;
|
|
514
|
+
deadline = Date.now() + this.options.timeoutMs;
|
|
515
|
+
}
|
|
362
516
|
if (typeof result !== "string")
|
|
363
517
|
continue;
|
|
364
518
|
const parsed = JSON.parse(result);
|
|
365
|
-
if (parsed.
|
|
366
|
-
|
|
519
|
+
if (parsed.requestId && parsed.requestId !== requestId)
|
|
520
|
+
continue;
|
|
521
|
+
if (parsed.failed) {
|
|
522
|
+
const failure = parsed.reason ?? `豆包任务失败${parsed.errorCode ? `(错误码 ${parsed.errorCode})` : ""}`;
|
|
523
|
+
if (signal?.aborted)
|
|
524
|
+
throw new agent_1.AgentOperationError("DOUBAO_REQUEST_CANCELLED", "等待豆包平台响应", new Error(`${failure};请求已取消,不再重试`));
|
|
525
|
+
const retryable = parsed.errorCode === "718013001" && parsed.retryable === true && parsed.acknowledged === false && parsed.progressed === false;
|
|
526
|
+
if (retryable && attempts < MAX_PREALLOCATION_ATTEMPTS && Date.now() < deadline) {
|
|
527
|
+
attempts++;
|
|
528
|
+
this.options.logger?.warn(`豆包平台 718013001 未接收任务,复用原会话和请求 ${requestId},第 ${attempts}/${MAX_PREALLOCATION_ATTEMPTS} 次尝试`);
|
|
529
|
+
await submit();
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
throw new agent_1.AgentOperationError(parsed.errorCode ? "DOUBAO_PLATFORM_ERROR" : "DOUBAO_STREAM_FAILED", "等待豆包平台流式响应(CDP 提交已完成)", new Error(`${failure}${retryable ? `;已尝试 ${attempts} 次,保留原会话` : ""}`));
|
|
533
|
+
}
|
|
367
534
|
if (!parsed.ok)
|
|
368
535
|
throw new Error(`豆包请求失败: ${parsed.reason ?? "未知错误"}`);
|
|
369
536
|
let artifacts = parsed.artifacts ?? [];
|
|
370
537
|
if (parsed.pending || parsed.expectVideo) {
|
|
371
|
-
const current = await this.evaluate(
|
|
538
|
+
const current = await this.evaluate(this.options.mode === "local-computer"
|
|
539
|
+
? `window.__mab_doubao_read_artifacts(${JSON.stringify(requestId)})`
|
|
540
|
+
: `(${exports.currentResponseArtifactsScript})(${JSON.stringify(parsed.conversationId || target)}, ${JSON.stringify(parsed.questionIds ?? [])}, ${JSON.stringify(parsed.messageIds ?? [])})`, true);
|
|
372
541
|
if (current.failed)
|
|
373
542
|
throw new Error("豆包媒体生成失败");
|
|
374
543
|
if (current.pending || !current.artifacts.length || (parsed.expectVideo && !current.artifacts.some(item => item.kind === "video")))
|
|
375
544
|
continue;
|
|
376
545
|
artifacts = [...artifacts, ...current.artifacts];
|
|
377
546
|
}
|
|
378
|
-
const unique = [...new Map(artifacts.map(item => [`${item.kind}:${item.vid || item.uri || item.url}`, item])).values()];
|
|
547
|
+
const unique = [...new Map(artifacts.map(item => [`${item.kind}:${item.path || item.vid || item.uri || item.url}`, item])).values()];
|
|
379
548
|
if (unique.length > attachments_1.MAX_ATTACHMENTS)
|
|
380
549
|
throw new Error("豆包单条消息最多返回 8 个附件");
|
|
381
|
-
const resolved = unique.some(item => !item.url) ? await this.evaluate(buildResolveArtifactsInjection(unique), true) : unique;
|
|
550
|
+
const resolved = unique.some(item => !item.url && !item.path) ? await this.evaluate(buildResolveArtifactsInjection(unique), true) : unique;
|
|
382
551
|
const output = [];
|
|
383
552
|
let total = 0;
|
|
384
553
|
for (const item of resolved) {
|
|
@@ -399,7 +568,7 @@ class DoubaoCdpClient {
|
|
|
399
568
|
if (signal?.aborted && parsed.pending !== true && !parsed.expectVideo)
|
|
400
569
|
throw new Error("豆包请求已取消,已确认本轮页面请求结束,未返回正文或附件");
|
|
401
570
|
}
|
|
402
|
-
throw new Error("
|
|
571
|
+
throw new agent_1.AgentOperationError("DOUBAO_REPLY_TIMEOUT", "等待豆包回复", new Error("等待豆包回复超时,尚未确认原任务结束;保留原会话,未重复提交"));
|
|
403
572
|
}
|
|
404
573
|
async uploadAttachments(attachments, signal) {
|
|
405
574
|
const token = (0, node_crypto_1.randomUUID)();
|
|
@@ -457,13 +626,16 @@ class DoubaoCdpClient {
|
|
|
457
626
|
throw new Error(`豆包页面 JS 异常: ${JSON.stringify(response.result.exceptionDetails).slice(0, 300)}`); return response.result?.result?.value; }
|
|
458
627
|
}
|
|
459
628
|
exports.DoubaoCdpClient = DoubaoCdpClient;
|
|
629
|
+
function isRecoverableCdpError(error) {
|
|
630
|
+
return /豆包 CDP (?:已断开|尚未连接|WebSocket 连接(?:失败|超时))|CDP Runtime.evaluate 超时|ECONN(?:RESET|REFUSED)|fetch failed|TimeoutError/i.test((0, agent_1.errorDetails)(error));
|
|
631
|
+
}
|
|
460
632
|
function buildResolveArtifactsInjection(artifacts) {
|
|
461
633
|
return `(async () => {
|
|
462
634
|
const controller = window.__mab_doubao_controller;
|
|
463
635
|
const requestUrl = window.__mab_doubao_request_url;
|
|
464
636
|
const result = [];
|
|
465
637
|
for (const item of ${JSON.stringify(artifacts)}) {
|
|
466
|
-
if (item.url) { result.push(item); continue; }
|
|
638
|
+
if (item.url || item.path) { result.push(item); continue; }
|
|
467
639
|
const path = item.kind === 'video' ? '/samantha/video/get_play_info' : '/alice/message/get_file_url';
|
|
468
640
|
const url = new URL(requestUrl);
|
|
469
641
|
if (url.origin !== 'https://www.doubao.com') throw new Error('豆包产物接口地址无效');
|
|
@@ -640,17 +812,8 @@ exports.doubaoHistoryReadPageScript = String.raw `async (conversationId, cursor,
|
|
|
640
812
|
return { error: '豆包会话历史读取失败,请检查桌面端历史接口是否可用' };
|
|
641
813
|
}
|
|
642
814
|
}`;
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
return String.raw `(() => {
|
|
646
|
-
window.__mab_doubao_result = null;
|
|
647
|
-
window.__mab_doubao_controller?.abort();
|
|
648
|
-
const controller = new AbortController();
|
|
649
|
-
window.__mab_doubao_controller = controller;
|
|
650
|
-
const merge = ${mergeDoubaoReply.toString()};
|
|
651
|
-
(async () => {
|
|
652
|
-
try {
|
|
653
|
-
const requestUrl = new URL(${JSON.stringify(url)});
|
|
815
|
+
exports.doubaoRequestUrlScript = String.raw `(() => {
|
|
816
|
+
const requestUrl = new URL("https://www.doubao.com/chat/completion?aid=582478&channel=win&client_platform=pc_client&device_platform=web&doubao_device_platform=desktop&language=zh&pkg_type=release_version&real_aid=582478®ion=CN&runtime=web&samantha_web=1&sys_region=CN&tz_name=Asia%2FShanghai&use-olympus-account=1&version_code=20800&web_platform=desktop");
|
|
654
817
|
const resources = [];
|
|
655
818
|
for (const entry of performance.getEntriesByType('resource')) { try { const u = new URL(entry.name); if (u.origin === requestUrl.origin) resources.push(u); } catch {} }
|
|
656
819
|
resources.reverse();
|
|
@@ -665,6 +828,20 @@ function buildInjection(body) {
|
|
|
665
828
|
const chromium = userAgent.match(/Chrome\/([\d.]+)/i)?.[1];
|
|
666
829
|
if (version) { requestUrl.searchParams.set('pc_version', version); requestUrl.searchParams.set('doubao_pc_version', version); }
|
|
667
830
|
if (chromium) requestUrl.searchParams.set('chromium_version', chromium);
|
|
831
|
+
return requestUrl;
|
|
832
|
+
})`;
|
|
833
|
+
function buildInjection(body) {
|
|
834
|
+
return String.raw `(() => {
|
|
835
|
+
window.__mab_doubao_result = null;
|
|
836
|
+
window.__mab_doubao_controller?.abort();
|
|
837
|
+
const controller = new AbortController();
|
|
838
|
+
window.__mab_doubao_controller = controller;
|
|
839
|
+
window.__mab_doubao_request_id = ${JSON.stringify(JSON.parse(body).option.unique_key)};
|
|
840
|
+
const merge = ${mergeDoubaoReply.toString()};
|
|
841
|
+
(async () => {
|
|
842
|
+
let acknowledged = false, progressed = false, errorCode = '', retryable = false;
|
|
843
|
+
try {
|
|
844
|
+
const requestUrl = (${exports.doubaoRequestUrlScript})();
|
|
668
845
|
window.__mab_doubao_request_url = requestUrl.href;
|
|
669
846
|
const response = await fetch(requestUrl.href, { method: 'POST', headers: { 'content-type': 'application/json', ...(JSON.parse(${JSON.stringify(body)}).client_meta.conversation_id ? {} : { 'Agw-Js-Conv': 'str, str' }) }, credentials: 'include', body: ${JSON.stringify(body)}, signal: controller.signal });
|
|
670
847
|
if (!response.ok) throw new Error('HTTP ' + response.status);
|
|
@@ -675,6 +852,7 @@ function buildInjection(body) {
|
|
|
675
852
|
if (!value || typeof value !== 'object') return;
|
|
676
853
|
if (value.ext?.has_video_gen === '1' || value.ext?.has_video_gen === 1) expectVideo = true;
|
|
677
854
|
if ([2074, 10020].includes(Number(value.block_type))) {
|
|
855
|
+
progressed = true;
|
|
678
856
|
const key = value.block_id || value.id || value.block_type + ':' + JSON.stringify(value.content || value.content_obj);
|
|
679
857
|
const previous = blocks.get(key) || {};
|
|
680
858
|
blocks.set(key, { ...previous, ...value, ...(value.content ? { content: { ...previous.content, ...value.content } } : {}) });
|
|
@@ -684,7 +862,15 @@ function buildInjection(body) {
|
|
|
684
862
|
const consume = (block) => {
|
|
685
863
|
const event = (block.match(/^event:\s*(.+)$/m) || [])[1] || '', data = (block.match(/^data:\s*(.+)$/m) || [])[1] || '';
|
|
686
864
|
if (event) events.push(event);
|
|
687
|
-
if (event === '
|
|
865
|
+
if (event === 'SSE_ACK') acknowledged = true;
|
|
866
|
+
if (['STREAM_CHUNK', 'CHUNK_DELTA', 'STREAM_MSG_NOTIFY', 'FULL_MSG_NOTIFY', 'SSE_REPLY_END'].includes(event)) progressed = true;
|
|
867
|
+
if (event === 'STREAM_ERROR') {
|
|
868
|
+
let parsed; try { parsed = JSON.parse(data); } catch { parsed = {}; }
|
|
869
|
+
errorCode = String(parsed.error_code ?? parsed.code ?? parsed.error?.error_code ?? parsed.error?.code ?? '');
|
|
870
|
+
const reason = (${doubaoStreamErrorMessage.toString()})(data);
|
|
871
|
+
retryable = errorCode === '718013001' && !reason.includes('风控');
|
|
872
|
+
throw new Error(reason);
|
|
873
|
+
}
|
|
688
874
|
if (!data) return;
|
|
689
875
|
let json; try { json = JSON.parse(data); } catch { return; }
|
|
690
876
|
if (event === 'SSE_ACK' && !conversationId && typeof json.ack_client_meta?.conversation_id === 'string' && /^\d+$/.test(json.ack_client_meta.conversation_id)) conversationId = json.ack_client_meta.conversation_id;
|
|
@@ -697,17 +883,17 @@ function buildInjection(body) {
|
|
|
697
883
|
}
|
|
698
884
|
const meta = json.meta || json.message;
|
|
699
885
|
if (meta?.conversation_id === conversationId && meta.user_type === 2 && meta.message_id) messageIds.add(String(meta.message_id));
|
|
700
|
-
if (event === 'STREAM_CHUNK') for (const patch of (json.patch_op || []))
|
|
701
|
-
if (event === 'CHUNK_DELTA' && typeof json.text === 'string') reply = merge(reply, json.text);
|
|
702
|
-
if (event === 'STREAM_MSG_NOTIFY' || event === 'FULL_MSG_NOTIFY') for (const text of (${exports.responseTextScript})(json)) reply = merge(reply, text);
|
|
703
|
-
if (event === 'SSE_REPLY_END')
|
|
886
|
+
if (event === 'STREAM_CHUNK') for (const patch of (json.patch_op || [])) { const text = patch.patch_value?.tts_content || ''; if (text) progressed = true; reply = merge(reply, text); }
|
|
887
|
+
if (event === 'CHUNK_DELTA' && typeof json.text === 'string') { if (json.text) progressed = true; reply = merge(reply, json.text); }
|
|
888
|
+
if (event === 'STREAM_MSG_NOTIFY' || event === 'FULL_MSG_NOTIFY') for (const text of (${exports.responseTextScript})(json)) { if (text) progressed = true; reply = merge(reply, text); }
|
|
889
|
+
if (event === 'SSE_REPLY_END') { const text = json.msg_finish_attr?.brief || json.data?.msg_finish_attr?.brief || ''; if (text) progressed = true; reply = merge(reply, text); }
|
|
704
890
|
};
|
|
705
891
|
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); } }
|
|
706
892
|
if (buffer.trim()) consume(buffer);
|
|
707
893
|
const media = (${exports.responseArtifactsScript})([...blocks.values()]);
|
|
708
894
|
const ok = Boolean(reply.trim() || media.artifacts.length || media.pending || expectVideo);
|
|
709
|
-
if (window.__mab_doubao_controller === controller) window.__mab_doubao_result = JSON.stringify({ ok, reply, conversationId, ...media, expectVideo, questionIds: [...questionIds], messageIds: [...messageIds], reason: ok ? '' : 'SSE 未提取到文本或附件; events=' + events.slice(0, 20).join(',') });
|
|
710
|
-
} catch (error) { if (window.__mab_doubao_controller === controller) window.__mab_doubao_result = JSON.stringify({ ok: false, reason: String(error) }); }
|
|
895
|
+
if (window.__mab_doubao_controller === controller) window.__mab_doubao_result = JSON.stringify({ ok, reply, conversationId, ...media, expectVideo, acknowledged, progressed, errorCode, questionIds: [...questionIds], messageIds: [...messageIds], reason: ok ? '' : 'SSE 未提取到文本或附件; events=' + events.slice(0, 20).join(',') });
|
|
896
|
+
} catch (error) { if (window.__mab_doubao_controller === controller) window.__mab_doubao_result = JSON.stringify({ ok: false, failed: true, acknowledged, progressed, errorCode, retryable, reason: String(error) }); }
|
|
711
897
|
})(); return 'started';
|
|
712
898
|
})()`;
|
|
713
899
|
}
|