mu-agent-bridge 1.0.34 → 1.0.39
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 +4 -2
- package/dist/src-ts/channel-sessions.js +90 -24
- package/dist/src-ts/control-server.js +48 -7
- package/dist/src-ts/dsh-agent.js +141 -46
- package/dist/src-ts/headless-account.patch.yml +8 -1
- package/dist/src-ts/plugin-manager.js +69 -3
- package/dist/src-ts/plugins/aun/attachments.js +44 -13
- package/dist/src-ts/plugins/aun/index.js +5 -6
- package/dist/src-ts/plugins/chatgpt/cdp-client.js +342 -9
- package/dist/src-ts/plugins/chatgpt/index.js +127 -7
- package/dist/src-ts/plugins/desktop-launch.js +19 -0
- package/dist/src-ts/plugins/doubao/attachments.js +42 -0
- package/dist/src-ts/plugins/doubao/cdp-client.js +530 -48
- package/dist/src-ts/plugins/doubao/index.js +114 -25
- package/dist/src-ts/plugins/doubao/session-history.js +123 -0
- package/dist/src-ts/plugins/dsh/index.js +18 -2
- package/dist/src-ts/plugins/dsh/session-history.js +371 -0
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +271 -10
- package/dist/src-ts/plugins/qwenwork/index.js +42 -3
- package/dist/src-ts/plugins/qwenwork/session-history.js +245 -0
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +42 -2
- package/dist/src-ts/plugins/workbuddy/index.js +23 -3
- package/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
- package/dist/src-ts/session-archive.js +509 -0
- package/dist/src-ts/session-history.js +2 -0
- package/dist/src-ts/session-management.js +665 -0
- package/dist/src-ts/session-summary.js +171 -0
- package/dist/tests-ts/aun-attachments.test.js +41 -3
- package/dist/tests-ts/aun-plugin.test.js +28 -5
- package/dist/tests-ts/bundled-package.test.js +3 -1
- package/dist/tests-ts/channel-sessions.test.js +255 -2
- package/dist/tests-ts/chatgpt-history.test.js +197 -0
- package/dist/tests-ts/chatgpt-plugin.test.js +279 -2
- package/dist/tests-ts/control-server.test.js +39 -0
- package/dist/tests-ts/desktop-launch.test.js +41 -0
- package/dist/tests-ts/doubao-history.test.js +172 -0
- package/dist/tests-ts/doubao-plugin.test.js +549 -2
- package/dist/tests-ts/dsh-agent.test.js +199 -0
- package/dist/tests-ts/dsh-history.test.js +165 -0
- package/dist/tests-ts/native-session-management.test.js +195 -0
- package/dist/tests-ts/plugin-manager.test.js +354 -4
- package/dist/tests-ts/qwenwork-history.test.js +339 -0
- package/dist/tests-ts/qwenwork-images.test.js +166 -0
- package/dist/tests-ts/qwenwork-plugin.test.js +40 -1
- package/dist/tests-ts/session-archive.test.js +422 -0
- package/dist/tests-ts/session-management.test.js +323 -0
- package/dist/tests-ts/session-summary.test.js +199 -0
- package/dist/tests-ts/workbuddy-history.test.js +256 -0
- package/dist/tests-ts/workbuddy-plugin.test.js +43 -0
- package/package.json +1 -1
- package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +44 -13
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +5 -6
- package/plugins/aun/package.json +1 -1
- package/plugins/aun/plugin.json +1 -1
- package/plugins/chatgpt/dist/src-ts/attachments.js +62 -0
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +342 -9
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +127 -7
- package/plugins/chatgpt/package.json +1 -1
- package/plugins/chatgpt/plugin.json +14 -1
- package/plugins/doubao/config.schema.json +3 -0
- package/plugins/doubao/dist/src-ts/attachments.js +62 -0
- package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +19 -0
- package/plugins/doubao/dist/src-ts/plugins/doubao/attachments.js +42 -0
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +530 -48
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +114 -25
- package/plugins/doubao/dist/src-ts/plugins/doubao/session-history.js +123 -0
- package/plugins/doubao/package.json +1 -1
- package/plugins/doubao/plugin.json +1 -1
- package/plugins/dsh/config.schema.json +2 -1
- package/plugins/dsh/dist/src-ts/attachments.js +62 -0
- package/plugins/dsh/dist/src-ts/dsh-agent.js +141 -46
- package/plugins/dsh/dist/src-ts/headless-account.patch.yml +8 -1
- package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +18 -2
- package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +371 -0
- package/plugins/dsh/package.json +5 -2
- package/plugins/dsh/plugin.json +1 -1
- package/plugins/qwenwork/dist/src-ts/attachments.js +62 -0
- package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +19 -0
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +271 -10
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +42 -3
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/session-history.js +245 -0
- 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/workbuddy/cdp-client.js +42 -2
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +23 -3
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
- package/plugins/workbuddy/package.json +1 -1
- package/plugins/workbuddy/plugin.json +1 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ChatgptCdpClient = exports.ChatgptPlugin = exports.chatgptManifest = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
4
5
|
const cdp_client_1 = require("./cdp-client");
|
|
5
6
|
const session_store_1 = require("./session-store");
|
|
6
7
|
exports.chatgptManifest = {
|
|
7
8
|
id: "chatgpt",
|
|
8
|
-
version: "1.0.
|
|
9
|
+
version: "1.0.9",
|
|
9
10
|
displayName: "ChatGPT(Codex)",
|
|
10
11
|
type: "agent",
|
|
11
12
|
entry: "dist/index.js",
|
|
@@ -23,15 +24,32 @@ class ChatgptPlugin {
|
|
|
23
24
|
options;
|
|
24
25
|
manifest = exports.chatgptManifest;
|
|
25
26
|
sessionOperations = [];
|
|
27
|
+
get attachmentInput() { return this.client?.attachmentInput === true; }
|
|
26
28
|
client;
|
|
27
29
|
failure;
|
|
28
30
|
remote = false;
|
|
29
31
|
queue = Promise.resolve();
|
|
32
|
+
queuedOperations = 0;
|
|
30
33
|
store;
|
|
31
34
|
logger;
|
|
35
|
+
historyScope = "";
|
|
36
|
+
historyGeneration = 0;
|
|
37
|
+
historyProvider;
|
|
32
38
|
constructor(options = {}) {
|
|
33
39
|
this.options = options;
|
|
34
40
|
}
|
|
41
|
+
get sessionHistory() {
|
|
42
|
+
const client = this.client;
|
|
43
|
+
const unsupported = !client?.listSessions || !client.activeSession || !client.readSessionHistory;
|
|
44
|
+
const generation = this.historyGeneration;
|
|
45
|
+
return this.historyProvider ??= {
|
|
46
|
+
scope: this.historyScope,
|
|
47
|
+
mode: unsupported ? "unsupported" : "sidebar",
|
|
48
|
+
limitation: unsupported ? "当前 ChatGPT 客户端不提供只读历史接口" : "仅列举已加载侧栏;仅读取当前活动会话中已加载且可验证的正文,完整性可能受页面虚拟化影响",
|
|
49
|
+
listSessions: query => generation === this.historyGeneration ? this.listNativeSessions(query) : Promise.resolve({ supported: false, reason: "ChatGPT 插件实例已变化,请重新列举历史" }),
|
|
50
|
+
readSession: query => generation === this.historyGeneration ? this.readNativeSession(query) : Promise.resolve({ supported: false, reason: "ChatGPT 插件实例已变化,请重新列举历史" }),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
35
53
|
async activate(context) {
|
|
36
54
|
const config = context.config;
|
|
37
55
|
const hasSshHost = config.sshHost !== undefined;
|
|
@@ -52,6 +70,11 @@ class ChatgptPlugin {
|
|
|
52
70
|
localPort: port(config.localCdpPort, 9230),
|
|
53
71
|
} : undefined;
|
|
54
72
|
this.remote = Boolean(ssh);
|
|
73
|
+
this.historyGeneration++;
|
|
74
|
+
this.historyProvider = undefined;
|
|
75
|
+
this.historyScope = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(ssh
|
|
76
|
+
? [ssh.host, ssh.user, ssh.port, ssh.remotePort]
|
|
77
|
+
: ["local", port(config.localCdpPort, 9230)])).digest("hex").slice(0, 24);
|
|
55
78
|
this.logger = context.logger;
|
|
56
79
|
this.store = new session_store_1.ChatgptSessionStore(context.dataDir, JSON.stringify(ssh
|
|
57
80
|
? [ssh.host, ssh.user, ssh.port, ssh.remotePort]
|
|
@@ -92,9 +115,9 @@ class ChatgptPlugin {
|
|
|
92
115
|
throw new Error("消息内容不能为空");
|
|
93
116
|
if (!this.client || !this.store)
|
|
94
117
|
throw new Error("ChatGPT 插件未启动");
|
|
95
|
-
const run = this.
|
|
96
|
-
|
|
97
|
-
return { text:
|
|
118
|
+
const run = this.enqueue(() => this.run(request));
|
|
119
|
+
const result = await run;
|
|
120
|
+
return { ...(typeof result === "string" ? { text: result } : result), sessionId: request.sessionId };
|
|
98
121
|
}
|
|
99
122
|
async controlSession(request) {
|
|
100
123
|
if (request.operation !== "new")
|
|
@@ -104,7 +127,7 @@ class ChatgptPlugin {
|
|
|
104
127
|
throw new Error("ChatGPT 新会话缺少 sessionId");
|
|
105
128
|
if (!this.client?.createSession || !this.client.listSessions || !this.store)
|
|
106
129
|
throw new Error("ChatGPT 当前传输不支持新建会话");
|
|
107
|
-
const run = this.
|
|
130
|
+
const run = this.enqueue(async () => {
|
|
108
131
|
const existing = new Set((await this.client.listSessions()).map(item => JSON.stringify([item.host, item.id])));
|
|
109
132
|
const created = await this.client.createSession(request.name);
|
|
110
133
|
if (created) {
|
|
@@ -118,9 +141,106 @@ class ChatgptPlugin {
|
|
|
118
141
|
this.persist();
|
|
119
142
|
return { text: "ChatGPT 已创建新会话", sessionId };
|
|
120
143
|
});
|
|
144
|
+
return run;
|
|
145
|
+
}
|
|
146
|
+
enqueue(work) {
|
|
147
|
+
this.queuedOperations++;
|
|
148
|
+
const run = this.queue.then(work).finally(() => { this.queuedOperations--; });
|
|
121
149
|
this.queue = run.then(() => undefined, () => undefined);
|
|
122
150
|
return run;
|
|
123
151
|
}
|
|
152
|
+
async listNativeSessions(query) {
|
|
153
|
+
const client = this.client;
|
|
154
|
+
const reject = (reason) => ({ supported: false, reason });
|
|
155
|
+
if (!client?.listSessions || !client.activeSession || !client.readSessionHistory)
|
|
156
|
+
return reject("当前 ChatGPT 客户端不支持只读历史");
|
|
157
|
+
const listSessions = client.listSessions.bind(client);
|
|
158
|
+
if (!client.connected())
|
|
159
|
+
return reject("ChatGPT CDP 当前未连接");
|
|
160
|
+
if (this.queuedOperations)
|
|
161
|
+
return reject("ChatGPT 正在处理其他操作,历史读取已拒绝");
|
|
162
|
+
if (!Number.isInteger(query.limit) || query.limit < 1)
|
|
163
|
+
throw new Error("ChatGPT 历史页条数无效");
|
|
164
|
+
if (query.cursor !== undefined && !/^list:[a-f0-9]{64}:\d+$/.test(query.cursor))
|
|
165
|
+
throw new Error("ChatGPT 历史游标无效");
|
|
166
|
+
return this.enqueue(async () => {
|
|
167
|
+
if (!client.connected())
|
|
168
|
+
return reject("ChatGPT CDP 当前未连接");
|
|
169
|
+
if (await client.historyBusy?.())
|
|
170
|
+
return reject("ChatGPT 正在生成回复,暂不读取历史");
|
|
171
|
+
const entries = await listSessions();
|
|
172
|
+
const fingerprint = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify([this.historyScope, this.historyGeneration, entries.map(({ host, id, title }) => [host, id, title])])).digest("hex");
|
|
173
|
+
const offset = query.cursor ? Number(query.cursor.split(":").at(-1)) : 0;
|
|
174
|
+
if (query.cursor !== undefined && !query.cursor.startsWith(`list:${fingerprint}:`))
|
|
175
|
+
throw new Error("ChatGPT 侧栏已变化,历史游标失效");
|
|
176
|
+
if (!Number.isSafeInteger(offset) || (query.cursor !== undefined && offset >= entries.length))
|
|
177
|
+
throw new Error("ChatGPT 历史游标位置无效");
|
|
178
|
+
const limit = Math.min(query.limit, 50);
|
|
179
|
+
const sessions = entries.slice(offset, offset + limit).map(item => ({ id: JSON.stringify([item.host, item.id]), title: item.title, ...(item.updatedAt ? { updatedAt: item.updatedAt } : {}) }));
|
|
180
|
+
return { supported: true, sessions, ...(offset + limit < entries.length ? { nextCursor: `list:${fingerprint}:${offset + limit}` } : {}), complete: false, missingReasons: ["仅包含 ChatGPT 当前已加载的侧栏项目,不代表全部历史会话"], readAt: new Date().toISOString() };
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
async readNativeSession(query) {
|
|
184
|
+
const client = this.client;
|
|
185
|
+
const reject = (reason) => ({ supported: false, reason });
|
|
186
|
+
if (!client?.listSessions || !client.activeSession || !client.readSessionHistory)
|
|
187
|
+
return reject("当前 ChatGPT 客户端不支持只读历史");
|
|
188
|
+
const listSessions = client.listSessions.bind(client);
|
|
189
|
+
const activeSession = client.activeSession.bind(client);
|
|
190
|
+
const readSessionHistory = client.readSessionHistory.bind(client);
|
|
191
|
+
if (!client.connected())
|
|
192
|
+
return reject("ChatGPT CDP 当前未连接");
|
|
193
|
+
if (this.queuedOperations)
|
|
194
|
+
return reject("ChatGPT 正在处理其他操作,历史读取已拒绝");
|
|
195
|
+
if (!Number.isInteger(query.limit) || query.limit < 1)
|
|
196
|
+
throw new Error("ChatGPT 历史页条数无效");
|
|
197
|
+
if (query.cursor !== undefined && !/^read:[a-f0-9]{64}:\d+$/.test(query.cursor))
|
|
198
|
+
throw new Error("ChatGPT 历史游标无效");
|
|
199
|
+
let requested;
|
|
200
|
+
try {
|
|
201
|
+
requested = JSON.parse(query.sessionId);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return reject("ChatGPT 会话 ID 无效");
|
|
205
|
+
}
|
|
206
|
+
if (!Array.isArray(requested) || requested.length !== 2 || requested.some(part => typeof part !== "string" || !part))
|
|
207
|
+
return reject("ChatGPT 会话 ID 无效");
|
|
208
|
+
return this.enqueue(async () => {
|
|
209
|
+
if (!client.connected())
|
|
210
|
+
return reject("ChatGPT CDP 当前未连接");
|
|
211
|
+
if (await client.historyBusy?.())
|
|
212
|
+
return reject("ChatGPT 正在生成回复,暂不读取历史");
|
|
213
|
+
const sessions = await listSessions();
|
|
214
|
+
const session = sessions.find(item => item.host === requested[0] && item.id === requested[1]);
|
|
215
|
+
if (!session)
|
|
216
|
+
return reject("目标会话不在当前 ChatGPT 侧栏中");
|
|
217
|
+
const active = await activeSession();
|
|
218
|
+
if (!active || active.host !== session.host || active.id !== session.id)
|
|
219
|
+
return reject("ChatGPT 只支持读取当前活动会话");
|
|
220
|
+
if (await client.historyBusy?.())
|
|
221
|
+
return reject("ChatGPT 正在生成回复,暂不读取历史");
|
|
222
|
+
const result = await readSessionHistory(session);
|
|
223
|
+
if (!result.supported)
|
|
224
|
+
return reject(result.reason);
|
|
225
|
+
const after = await activeSession();
|
|
226
|
+
if (!after || after.host !== session.host || after.id !== session.id)
|
|
227
|
+
return reject("读取期间活动会话身份发生变化");
|
|
228
|
+
const fingerprint = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify([this.historyScope, this.historyGeneration, session.host, session.id, result.turns])).digest("hex");
|
|
229
|
+
const offset = query.cursor ? Number(query.cursor.split(":").at(-1)) : 0;
|
|
230
|
+
if (query.cursor !== undefined && !query.cursor.startsWith(`read:${fingerprint}:`))
|
|
231
|
+
throw new Error("ChatGPT 会话内容已变化,历史游标失效");
|
|
232
|
+
if (!Number.isSafeInteger(offset) || (query.cursor !== undefined && offset >= result.turns.length))
|
|
233
|
+
throw new Error("ChatGPT 历史游标位置无效");
|
|
234
|
+
const turns = result.turns.map(turn => ({ ...turn }));
|
|
235
|
+
const chars = turns.reduce((total, turn) => total + turn.text.length, 0);
|
|
236
|
+
if (chars > 60_000)
|
|
237
|
+
throw new Error("ChatGPT 会话正文超过 60,000 字符上限");
|
|
238
|
+
const limit = Math.min(query.limit, 100);
|
|
239
|
+
const pageTurns = turns.slice(offset, offset + limit);
|
|
240
|
+
const reasons = [...result.missingReasons, "仅包含当前页面已加载的可验证正文,页面虚拟化或未加载历史导致完整性无法确认"];
|
|
241
|
+
return { supported: true, session: { id: query.sessionId, title: session.title, ...(session.updatedAt ? { updatedAt: session.updatedAt } : {}) }, turns: pageTurns, ...(offset + limit < turns.length ? { nextCursor: `read:${fingerprint}:${offset + limit}` } : {}), complete: false, missingReasons: reasons, readAt: new Date().toISOString() };
|
|
242
|
+
});
|
|
243
|
+
}
|
|
124
244
|
persist() {
|
|
125
245
|
void this.store.save().catch(error => this.logger?.warn("ChatGPT 会话缓存保存失败", { error: String(error) }));
|
|
126
246
|
}
|
|
@@ -204,7 +324,7 @@ class ChatgptPlugin {
|
|
|
204
324
|
await this.client.connect();
|
|
205
325
|
selected ??= await this.client.activeSession?.();
|
|
206
326
|
const message = explicit?.[2]?.trim() || request.task;
|
|
207
|
-
const reply = await this.client.direct(message, request.timeoutMs);
|
|
327
|
+
const reply = await this.client.direct(message, request.timeoutMs, request.attachments);
|
|
208
328
|
const baseline = request.sessionId ? store.pendingBindings[request.sessionId] : undefined;
|
|
209
329
|
if (baseline) {
|
|
210
330
|
try {
|
|
@@ -226,7 +346,7 @@ class ChatgptPlugin {
|
|
|
226
346
|
if (selected) {
|
|
227
347
|
const current = await this.client.activeSession?.().catch(() => undefined);
|
|
228
348
|
const entry = current?.id === selected.id && current.host === selected.host ? current : selected;
|
|
229
|
-
store.remember(entry, message, reply);
|
|
349
|
+
store.remember(entry, message, typeof reply === "string" ? reply : reply.text);
|
|
230
350
|
if (request.sessionId)
|
|
231
351
|
store.bindings[request.sessionId] = entry;
|
|
232
352
|
this.persist();
|
|
@@ -7,10 +7,19 @@ exports.waitFor = waitFor;
|
|
|
7
7
|
const promises_1 = require("node:fs/promises");
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_path_1 = require("node:path");
|
|
10
|
+
const versionDirectory = /^(?:app[-_ ]?)?v?\d+(?:[.-]\d+)+$/i;
|
|
10
11
|
/** 在 Windows 常见安装目录中查找桌面程序,避免依赖单一盘符。 */
|
|
11
12
|
async function findWindowsExecutable(fileName, configured) {
|
|
12
13
|
if (configured?.trim()) {
|
|
13
14
|
const configuredPath = (0, node_path_1.resolve)(configured);
|
|
15
|
+
if (fileName.toLowerCase() === "doubao.exe") {
|
|
16
|
+
let root = (0, node_path_1.dirname)(configuredPath);
|
|
17
|
+
while (/^app$/i.test((0, node_path_1.basename)(root)) || versionDirectory.test((0, node_path_1.basename)(root)))
|
|
18
|
+
root = (0, node_path_1.dirname)(root);
|
|
19
|
+
const latest = await findExecutableInTree(root, fileName, /^/, 3);
|
|
20
|
+
if (latest)
|
|
21
|
+
return latest;
|
|
22
|
+
}
|
|
14
23
|
// 千问升级会保留或删除旧版本目录,从同一安装位置重新定位新版。
|
|
15
24
|
if (fileName.toLowerCase() === "qwenworkcn.exe" && /^v?\d+(?:[.-]\d+)+$/i.test((0, node_path_1.basename)((0, node_path_1.dirname)(configuredPath)))) {
|
|
16
25
|
const latest = await findExecutableInTree((0, node_path_1.dirname)((0, node_path_1.dirname)(configuredPath)), fileName, /^/, 1);
|
|
@@ -42,6 +51,16 @@ async function findExecutableInTree(directory, fileName, pattern, depth) {
|
|
|
42
51
|
async function search(directory, fileName, pattern, depth, insideProduct) {
|
|
43
52
|
try {
|
|
44
53
|
const entries = await (0, promises_1.readdir)(directory, { withFileTypes: true });
|
|
54
|
+
// 豆包上层文件是启动器,优先查找新版目录或 app 中的实际程序。
|
|
55
|
+
if (insideProduct && depth > 0 && fileName.toLowerCase() === "doubao.exe") {
|
|
56
|
+
const versions = entries.filter(entry => entry.isDirectory() && (versionDirectory.test(entry.name) || /^app$/i.test(entry.name)));
|
|
57
|
+
versions.sort((left, right) => right.name.replace(/^\D+/, "").localeCompare(left.name.replace(/^\D+/, ""), "en", { numeric: true }));
|
|
58
|
+
for (const entry of versions) {
|
|
59
|
+
const found = await search((0, node_path_1.join)(directory, entry.name), fileName, pattern, depth - 1, true);
|
|
60
|
+
if (found)
|
|
61
|
+
return found;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
45
64
|
for (const entry of entries)
|
|
46
65
|
if (entry.isFile() && insideProduct && entry.name.toLowerCase() === fileName.toLowerCase())
|
|
47
66
|
return (0, node_path_1.join)(directory, entry.name);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cleanupUploadScript = exports.uploadStateScript = void 0;
|
|
4
|
+
exports.buildUploadInjection = buildUploadInjection;
|
|
5
|
+
// 原生上传状态保存在附件区的 React 属性中,避免复刻平台私有上传签名。
|
|
6
|
+
exports.uploadStateScript = `() => {
|
|
7
|
+
const area = document.querySelector('[data-testid=attachment_area]');
|
|
8
|
+
let fiber = area?.[Object.keys(area).find(key => key.startsWith('__reactFiber'))];
|
|
9
|
+
while (fiber) {
|
|
10
|
+
if (Array.isArray(fiber.memoizedProps?.attachmentStates)) return fiber.memoizedProps.attachmentStates;
|
|
11
|
+
fiber = fiber.return;
|
|
12
|
+
}
|
|
13
|
+
return [];
|
|
14
|
+
}`;
|
|
15
|
+
function buildUploadInjection(items) {
|
|
16
|
+
return `(() => {
|
|
17
|
+
const editor = document.querySelector('[contenteditable=true]');
|
|
18
|
+
if (!editor) return '未找到豆包输入区';
|
|
19
|
+
if (editor.textContent.trim() || document.querySelector('[data-testid=attachment_area]')) return '豆包存在未发送草稿,请先处理草稿';
|
|
20
|
+
const input = document.querySelector('input[type=file][data-testid=upload-file-input]');
|
|
21
|
+
if (!input) return '未找到豆包原生附件上传入口';
|
|
22
|
+
const transfer = new DataTransfer();
|
|
23
|
+
for (const item of ${JSON.stringify(items)}) {
|
|
24
|
+
const bytes = Uint8Array.from(atob(item.base64), char => char.charCodeAt(0));
|
|
25
|
+
transfer.items.add(new File([bytes], item.uploadName, { type: item.type }));
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(input, 'files', { value: transfer.files, configurable: true });
|
|
28
|
+
try { input.dispatchEvent(new Event('change', { bubbles: true })); }
|
|
29
|
+
finally { delete input.files; }
|
|
30
|
+
return 'started';
|
|
31
|
+
})()`;
|
|
32
|
+
}
|
|
33
|
+
// 唯一文件名前缀限定清理范围,用户上传的新附件不会被删除。
|
|
34
|
+
exports.cleanupUploadScript = `(names) => {
|
|
35
|
+
const states = (${exports.uploadStateScript})();
|
|
36
|
+
const area = document.querySelector('[data-testid=attachment_area]');
|
|
37
|
+
const buttons = area?.querySelectorAll('[data-testid=attachment-delete-btn], [data-testid=message_nested_content_file_delete]') || [];
|
|
38
|
+
for (let index = states.length - 1; index >= 0; index--) {
|
|
39
|
+
if (names.includes(states[index].fileName)) buttons[index]?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
40
|
+
}
|
|
41
|
+
return 'cleaned';
|
|
42
|
+
}`;
|