@huyooo/ai-chat-bridge-electron 0.2.45 → 0.3.6
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/dist/main/asr/client.d.ts +112 -0
- package/dist/main/asr/index.d.ts +35 -0
- package/dist/main/asr/protocol.d.ts +135 -0
- package/dist/main/create-electron-chat.d.ts +100 -0
- package/dist/main/index.d.ts +88 -169
- package/dist/main/index.js +1 -1
- package/dist/preload/index.d.ts +47 -28
- package/dist/preload/index.js +1 -1
- package/dist/renderer/index.d.ts +10 -204
- package/dist/renderer/index.js +2 -1
- package/dist/renderer/logger.d.ts +16 -0
- package/package.json +13 -8
- package/dist/main/index.cjs +0 -364
- package/dist/main/index.d.cts +0 -61
- package/dist/main/index.js.map +0 -1
- package/dist/preload/index.cjs +0 -122
- package/dist/preload/index.d.cts +0 -276
- package/dist/preload/index.js.map +0 -1
- package/dist/renderer/index.cjs +0 -199
- package/dist/renderer/index.d.cts +0 -504
- package/dist/renderer/index.js.map +0 -1
package/dist/main/index.cjs
DELETED
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/main/index.ts
|
|
31
|
-
var main_exports = {};
|
|
32
|
-
__export(main_exports, {
|
|
33
|
-
MODELS: () => import_ai_chat_core.MODELS,
|
|
34
|
-
createElectronBridge: () => createElectronBridge
|
|
35
|
-
});
|
|
36
|
-
module.exports = __toCommonJS(main_exports);
|
|
37
|
-
var import_electron = require("electron");
|
|
38
|
-
var fs = __toESM(require("fs"), 1);
|
|
39
|
-
var path = __toESM(require("path"), 1);
|
|
40
|
-
var import_ai_chat_core = require("@huyooo/ai-chat-core");
|
|
41
|
-
var import_electron2 = require("@huyooo/ai-search/bridge/electron");
|
|
42
|
-
var import_ai_chat_storage = require("@huyooo/ai-chat-storage");
|
|
43
|
-
async function createElectronBridge(options) {
|
|
44
|
-
const {
|
|
45
|
-
channelPrefix = "ai-chat",
|
|
46
|
-
dataDir,
|
|
47
|
-
defaultContext = {},
|
|
48
|
-
...agentConfig
|
|
49
|
-
} = options;
|
|
50
|
-
const resolvedStoragePath = path.join(dataDir, "db.sqlite");
|
|
51
|
-
(0, import_electron2.createSearchElectronBridge)({
|
|
52
|
-
ipcMain: import_electron.ipcMain,
|
|
53
|
-
channelPrefix
|
|
54
|
-
}).init();
|
|
55
|
-
const pendingApprovals = /* @__PURE__ */ new Map();
|
|
56
|
-
const toolApprovalCallback = async (toolCall) => {
|
|
57
|
-
console.log("[Main] \u5DE5\u5177\u6279\u51C6\u56DE\u8C03\u88AB\u8C03\u7528:", toolCall.name);
|
|
58
|
-
const currentWebContents = global.currentWebContents;
|
|
59
|
-
if (!currentWebContents) {
|
|
60
|
-
console.log("[Main] \u8B66\u544A: \u6CA1\u6709 webContents\uFF0C\u9ED8\u8BA4\u6279\u51C6");
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return new Promise((resolve2, reject) => {
|
|
64
|
-
pendingApprovals.set(toolCall.id, {
|
|
65
|
-
resolve: resolve2,
|
|
66
|
-
reject,
|
|
67
|
-
webContents: currentWebContents
|
|
68
|
-
});
|
|
69
|
-
console.log("[Main] \u53D1\u9001\u5DE5\u5177\u6279\u51C6\u8BF7\u6C42\u5230\u524D\u7AEF:", toolCall.name);
|
|
70
|
-
currentWebContents.send(`${channelPrefix}:toolApprovalRequest`, {
|
|
71
|
-
id: toolCall.id,
|
|
72
|
-
name: toolCall.name,
|
|
73
|
-
args: toolCall.args
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
};
|
|
77
|
-
const storage = await (0, import_ai_chat_storage.createStorage)({
|
|
78
|
-
type: "sqlite",
|
|
79
|
-
sqlitePath: resolvedStoragePath
|
|
80
|
-
});
|
|
81
|
-
const getContext = () => defaultContext;
|
|
82
|
-
const getAutoRunConfig = async () => {
|
|
83
|
-
try {
|
|
84
|
-
const configJson = await storage.getUserSetting("autoRunConfig", getContext());
|
|
85
|
-
if (configJson) {
|
|
86
|
-
return JSON.parse(configJson);
|
|
87
|
-
}
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.error("[Main] \u83B7\u53D6 autoRunConfig \u5931\u8D25:", error);
|
|
90
|
-
}
|
|
91
|
-
return void 0;
|
|
92
|
-
};
|
|
93
|
-
const agent = new import_ai_chat_core.HybridAgent({
|
|
94
|
-
...agentConfig,
|
|
95
|
-
// tools 直接传递 ToolConfigItem[],HybridAgent 会在 asyncInit 中解析
|
|
96
|
-
tools: options.tools,
|
|
97
|
-
onToolApprovalRequest: toolApprovalCallback,
|
|
98
|
-
getAutoRunConfig
|
|
99
|
-
});
|
|
100
|
-
import_electron.ipcMain.handle(`${channelPrefix}:models`, () => {
|
|
101
|
-
return import_ai_chat_core.MODELS;
|
|
102
|
-
});
|
|
103
|
-
import_electron.ipcMain.handle(`${channelPrefix}:send`, async (event, params) => {
|
|
104
|
-
const webContents = event.sender;
|
|
105
|
-
const { message, images, options: options2 = {}, sessionId } = params;
|
|
106
|
-
global.currentWebContents = webContents;
|
|
107
|
-
console.log("[AI-Chat] \u6536\u5230\u6D88\u606F:", { message, options: options2, images: images?.length || 0, sessionId });
|
|
108
|
-
console.log("[AI-Chat] autoRunConfig:", JSON.stringify(options2.autoRunConfig, null, 2));
|
|
109
|
-
try {
|
|
110
|
-
for await (const chatEvent of agent.chat(message, options2, images)) {
|
|
111
|
-
console.log(
|
|
112
|
-
"[AI-Chat] \u4E8B\u4EF6:",
|
|
113
|
-
chatEvent.type,
|
|
114
|
-
chatEvent.type === "text_delta" ? chatEvent.data.content.slice(0, 20) : chatEvent.type === "thinking_delta" ? chatEvent.data.content.slice(0, 20) : chatEvent.type === "search_result" ? `\u641C\u7D22\u5B8C\u6210 ${chatEvent.data.results?.length || 0} \u6761` : JSON.stringify(chatEvent.data).slice(0, 100)
|
|
115
|
-
);
|
|
116
|
-
if (!webContents.isDestroyed()) {
|
|
117
|
-
webContents.send(`${channelPrefix}:progress`, { ...chatEvent, sessionId });
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
console.log("[AI-Chat] \u5B8C\u6210");
|
|
121
|
-
} catch (error) {
|
|
122
|
-
console.error("[AI-Chat] \u9519\u8BEF:", error);
|
|
123
|
-
if (error instanceof Error) {
|
|
124
|
-
console.error("[AI-Chat] \u9519\u8BEF\u8BE6\u60C5:", {
|
|
125
|
-
message: error.message,
|
|
126
|
-
stack: error.stack,
|
|
127
|
-
name: error.name
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
if (!webContents.isDestroyed()) {
|
|
131
|
-
const errorData = error instanceof Error ? {
|
|
132
|
-
category: "api",
|
|
133
|
-
message: error.message || String(error),
|
|
134
|
-
cause: error.stack
|
|
135
|
-
} : {
|
|
136
|
-
category: "api",
|
|
137
|
-
message: String(error)
|
|
138
|
-
};
|
|
139
|
-
webContents.send(`${channelPrefix}:progress`, {
|
|
140
|
-
type: "error",
|
|
141
|
-
data: errorData,
|
|
142
|
-
sessionId
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
} finally {
|
|
146
|
-
delete global.currentWebContents;
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
import_electron.ipcMain.handle(`${channelPrefix}:toolApprovalResponse`, (_event, params) => {
|
|
150
|
-
const { id, approved } = params;
|
|
151
|
-
const pending = pendingApprovals.get(id);
|
|
152
|
-
if (pending) {
|
|
153
|
-
pendingApprovals.delete(id);
|
|
154
|
-
pending.resolve(approved);
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
import_electron.ipcMain.handle(`${channelPrefix}:cancel`, () => {
|
|
158
|
-
agent.abort();
|
|
159
|
-
});
|
|
160
|
-
import_electron.ipcMain.handle(`${channelPrefix}:settings:get`, async (_event, key) => {
|
|
161
|
-
return storage.getUserSetting(key, getContext());
|
|
162
|
-
});
|
|
163
|
-
import_electron.ipcMain.handle(`${channelPrefix}:settings:set`, async (_event, key, value) => {
|
|
164
|
-
await storage.setUserSetting(key, value, getContext());
|
|
165
|
-
return { success: true };
|
|
166
|
-
});
|
|
167
|
-
import_electron.ipcMain.handle(`${channelPrefix}:settings:getAll`, async () => {
|
|
168
|
-
return storage.getUserSettings(getContext());
|
|
169
|
-
});
|
|
170
|
-
import_electron.ipcMain.handle(`${channelPrefix}:settings:delete`, async (_event, key) => {
|
|
171
|
-
await storage.deleteUserSetting(key, getContext());
|
|
172
|
-
return { success: true };
|
|
173
|
-
});
|
|
174
|
-
import_electron.ipcMain.handle(`${channelPrefix}:setCwd`, (_event, dir) => {
|
|
175
|
-
agent.setCwd(dir);
|
|
176
|
-
});
|
|
177
|
-
import_electron.ipcMain.handle(`${channelPrefix}:config`, () => {
|
|
178
|
-
return agent.getConfig();
|
|
179
|
-
});
|
|
180
|
-
import_electron.ipcMain.handle(`${channelPrefix}:sessions:list`, async () => {
|
|
181
|
-
return storage.getSessions(getContext());
|
|
182
|
-
});
|
|
183
|
-
import_electron.ipcMain.handle(`${channelPrefix}:sessions:get`, async (_event, id) => {
|
|
184
|
-
return storage.getSession(id, getContext());
|
|
185
|
-
});
|
|
186
|
-
import_electron.ipcMain.handle(`${channelPrefix}:sessions:create`, async (_event, params) => {
|
|
187
|
-
const input = {
|
|
188
|
-
id: params.id || crypto.randomUUID(),
|
|
189
|
-
title: params.title || "\u65B0\u5BF9\u8BDD",
|
|
190
|
-
model: params.model || import_ai_chat_core.DEFAULT_MODEL,
|
|
191
|
-
mode: params.mode || "agent",
|
|
192
|
-
webSearchEnabled: params.webSearchEnabled ?? true,
|
|
193
|
-
thinkingEnabled: params.thinkingEnabled ?? true,
|
|
194
|
-
hidden: params.hidden ?? false
|
|
195
|
-
};
|
|
196
|
-
return storage.createSession(input, getContext());
|
|
197
|
-
});
|
|
198
|
-
import_electron.ipcMain.handle(`${channelPrefix}:sessions:update`, async (_event, id, data) => {
|
|
199
|
-
await storage.updateSession(id, data, getContext());
|
|
200
|
-
return storage.getSession(id, getContext());
|
|
201
|
-
});
|
|
202
|
-
import_electron.ipcMain.handle(`${channelPrefix}:sessions:delete`, async (_event, id) => {
|
|
203
|
-
await storage.deleteSession(id, getContext());
|
|
204
|
-
return { success: true };
|
|
205
|
-
});
|
|
206
|
-
import_electron.ipcMain.handle(`${channelPrefix}:messages:list`, async (_event, sessionId) => {
|
|
207
|
-
return storage.getMessages(sessionId, getContext());
|
|
208
|
-
});
|
|
209
|
-
import_electron.ipcMain.handle(`${channelPrefix}:messages:save`, async (_event, params) => {
|
|
210
|
-
const input = {
|
|
211
|
-
id: params.id || crypto.randomUUID(),
|
|
212
|
-
sessionId: params.sessionId,
|
|
213
|
-
role: params.role,
|
|
214
|
-
content: params.content,
|
|
215
|
-
model: params.model || null,
|
|
216
|
-
mode: params.mode || null,
|
|
217
|
-
webSearchEnabled: params.webSearchEnabled ?? null,
|
|
218
|
-
thinkingEnabled: params.thinkingEnabled ?? null,
|
|
219
|
-
steps: params.steps || null,
|
|
220
|
-
operationIds: params.operationIds || null
|
|
221
|
-
};
|
|
222
|
-
return storage.saveMessage(input, getContext());
|
|
223
|
-
});
|
|
224
|
-
import_electron.ipcMain.handle(`${channelPrefix}:messages:update`, async (_event, params) => {
|
|
225
|
-
await storage.updateMessage(params.id, {
|
|
226
|
-
content: params.content,
|
|
227
|
-
steps: params.steps
|
|
228
|
-
}, getContext());
|
|
229
|
-
return { success: true };
|
|
230
|
-
});
|
|
231
|
-
import_electron.ipcMain.handle(`${channelPrefix}:messages:deleteAfter`, async (_event, sessionId, timestamp) => {
|
|
232
|
-
await storage.deleteMessagesAfter(sessionId, new Date(timestamp), getContext());
|
|
233
|
-
return { success: true };
|
|
234
|
-
});
|
|
235
|
-
import_electron.ipcMain.handle(`${channelPrefix}:operations:list`, async (_event, sessionId) => {
|
|
236
|
-
return storage.getOperations(sessionId, getContext());
|
|
237
|
-
});
|
|
238
|
-
import_electron.ipcMain.handle(`${channelPrefix}:trash:list`, async () => {
|
|
239
|
-
return storage.getTrashItems?.(getContext()) || [];
|
|
240
|
-
});
|
|
241
|
-
import_electron.ipcMain.handle(`${channelPrefix}:trash:restore`, async (_event, id) => {
|
|
242
|
-
return storage.restoreFromTrash?.(id, getContext());
|
|
243
|
-
});
|
|
244
|
-
import_electron.ipcMain.handle(`${channelPrefix}:openExternal`, async (_event, url) => {
|
|
245
|
-
return import_electron.shell.openExternal(url);
|
|
246
|
-
});
|
|
247
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:listDir`, async (_event, dirPath) => {
|
|
248
|
-
try {
|
|
249
|
-
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
250
|
-
const files = [];
|
|
251
|
-
for (const entry of entries) {
|
|
252
|
-
if (entry.name.startsWith(".")) continue;
|
|
253
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
254
|
-
try {
|
|
255
|
-
const stats = fs.statSync(fullPath);
|
|
256
|
-
files.push({
|
|
257
|
-
name: entry.name,
|
|
258
|
-
path: fullPath,
|
|
259
|
-
isDirectory: entry.isDirectory(),
|
|
260
|
-
size: stats.size,
|
|
261
|
-
modifiedAt: stats.mtime,
|
|
262
|
-
extension: entry.isDirectory() ? "" : path.extname(entry.name).toLowerCase()
|
|
263
|
-
});
|
|
264
|
-
} catch {
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return files.sort((a, b) => {
|
|
268
|
-
if (a.isDirectory && !b.isDirectory) return -1;
|
|
269
|
-
if (!a.isDirectory && b.isDirectory) return 1;
|
|
270
|
-
return a.name.localeCompare(b.name);
|
|
271
|
-
});
|
|
272
|
-
} catch (error) {
|
|
273
|
-
console.error("[AI-Chat] \u5217\u51FA\u76EE\u5F55\u5931\u8D25:", error);
|
|
274
|
-
return [];
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:exists`, async (_event, filePath) => {
|
|
278
|
-
return fs.existsSync(filePath);
|
|
279
|
-
});
|
|
280
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:stat`, async (_event, filePath) => {
|
|
281
|
-
try {
|
|
282
|
-
const stats = fs.statSync(filePath);
|
|
283
|
-
return {
|
|
284
|
-
name: path.basename(filePath),
|
|
285
|
-
path: filePath,
|
|
286
|
-
isDirectory: stats.isDirectory(),
|
|
287
|
-
size: stats.size,
|
|
288
|
-
modifiedAt: stats.mtime,
|
|
289
|
-
extension: stats.isDirectory() ? "" : path.extname(filePath).toLowerCase()
|
|
290
|
-
};
|
|
291
|
-
} catch {
|
|
292
|
-
return null;
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:readFile`, async (_event, filePath) => {
|
|
296
|
-
try {
|
|
297
|
-
return fs.readFileSync(filePath, "utf-8");
|
|
298
|
-
} catch {
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:readFileBase64`, async (_event, filePath) => {
|
|
303
|
-
try {
|
|
304
|
-
const buffer = fs.readFileSync(filePath);
|
|
305
|
-
return buffer.toString("base64");
|
|
306
|
-
} catch {
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:homeDir`, async () => {
|
|
311
|
-
return process.env.HOME || process.env.USERPROFILE || "/";
|
|
312
|
-
});
|
|
313
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:resolvePath`, async (_event, inputPath) => {
|
|
314
|
-
if (inputPath.startsWith("~")) {
|
|
315
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "/";
|
|
316
|
-
return path.join(homeDir, inputPath.slice(1));
|
|
317
|
-
}
|
|
318
|
-
return path.resolve(inputPath);
|
|
319
|
-
});
|
|
320
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:parentDir`, async (_event, dirPath) => {
|
|
321
|
-
return path.dirname(dirPath);
|
|
322
|
-
});
|
|
323
|
-
const activeWatchers = /* @__PURE__ */ new Map();
|
|
324
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:watchDir`, async (event, dirPath) => {
|
|
325
|
-
const webContents = event.sender;
|
|
326
|
-
if (activeWatchers.has(dirPath)) {
|
|
327
|
-
activeWatchers.get(dirPath)?.close();
|
|
328
|
-
activeWatchers.delete(dirPath);
|
|
329
|
-
}
|
|
330
|
-
try {
|
|
331
|
-
const watcher = fs.watch(dirPath, { persistent: false }, (eventType, filename) => {
|
|
332
|
-
if (!webContents.isDestroyed()) {
|
|
333
|
-
webContents.send(`${channelPrefix}:fs:dirChange`, {
|
|
334
|
-
dirPath,
|
|
335
|
-
eventType,
|
|
336
|
-
filename
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
watcher.on("error", (error) => {
|
|
341
|
-
console.error("[AI-Chat] Watch error:", error);
|
|
342
|
-
activeWatchers.delete(dirPath);
|
|
343
|
-
});
|
|
344
|
-
activeWatchers.set(dirPath, watcher);
|
|
345
|
-
return true;
|
|
346
|
-
} catch (error) {
|
|
347
|
-
console.error("[AI-Chat] Failed to watch directory:", error);
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
import_electron.ipcMain.handle(`${channelPrefix}:fs:unwatchDir`, async (_event, dirPath) => {
|
|
352
|
-
const watcher = activeWatchers.get(dirPath);
|
|
353
|
-
if (watcher) {
|
|
354
|
-
watcher.close();
|
|
355
|
-
activeWatchers.delete(dirPath);
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
return { agent, storage };
|
|
359
|
-
}
|
|
360
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
361
|
-
0 && (module.exports = {
|
|
362
|
-
MODELS,
|
|
363
|
-
createElectronBridge
|
|
364
|
-
});
|
package/dist/main/index.d.cts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { AgentConfig, ToolConfigItem, HybridAgent } from '@huyooo/ai-chat-core';
|
|
2
|
-
export { AgentConfig, ChatEvent, ChatMode, ChatOptions, MODELS, ProviderType } from '@huyooo/ai-chat-core';
|
|
3
|
-
import { StorageContext, StorageAdapter } from '@huyooo/ai-chat-storage';
|
|
4
|
-
export { MessageRecord, SessionRecord, StorageAdapter, StorageContext } from '@huyooo/ai-chat-storage';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Electron 主进程桥接
|
|
8
|
-
*
|
|
9
|
-
* 在主进程中调用,注册 IPC handlers
|
|
10
|
-
* 集成 SQLite 存储
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/** 文件信息 */
|
|
14
|
-
interface FileInfo {
|
|
15
|
-
name: string;
|
|
16
|
-
path: string;
|
|
17
|
-
isDirectory: boolean;
|
|
18
|
-
size: number;
|
|
19
|
-
modifiedAt: Date;
|
|
20
|
-
extension: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface ElectronBridgeOptions extends Omit<AgentConfig, 'tools'> {
|
|
24
|
-
/** IPC channel 前缀 */
|
|
25
|
-
channelPrefix?: string;
|
|
26
|
-
/**
|
|
27
|
-
* 统一数据目录(必填)
|
|
28
|
-
* 所有数据都存储在此目录下:
|
|
29
|
-
* - db.sqlite: 对话历史
|
|
30
|
-
* - search-data/: 搜索索引(由宿主 tools 注入 searchPlugin 时使用)
|
|
31
|
-
*/
|
|
32
|
-
dataDir: string;
|
|
33
|
-
/** 默认租户上下文 */
|
|
34
|
-
defaultContext?: StorageContext;
|
|
35
|
-
/**
|
|
36
|
-
* 工具列表(Vite 插件风格)
|
|
37
|
-
* 支持:ToolPlugin、Promise<ToolPlugin>
|
|
38
|
-
*/
|
|
39
|
-
tools?: ToolConfigItem[];
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* 创建 Electron IPC 桥接
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* ```ts
|
|
46
|
-
* import { createElectronBridge } from '@huyooo/ai-chat-bridge-electron/main';
|
|
47
|
-
*
|
|
48
|
-
* app.whenReady().then(async () => {
|
|
49
|
-
* const { agent, storage } = await createElectronBridge({
|
|
50
|
-
* arkApiKey: 'xxx',
|
|
51
|
-
* geminiApiKey: 'xxx',
|
|
52
|
-
* });
|
|
53
|
-
* });
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
declare function createElectronBridge(options: ElectronBridgeOptions): Promise<{
|
|
57
|
-
agent: HybridAgent;
|
|
58
|
-
storage: StorageAdapter;
|
|
59
|
-
}>;
|
|
60
|
-
|
|
61
|
-
export { type ElectronBridgeOptions, type FileInfo, createElectronBridge };
|
package/dist/main/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/main/index.ts","../../src/main/asr/protocol.ts","../../src/main/asr/client.ts","../../src/main/asr/index.ts"],"sourcesContent":["/**\n * Electron 主进程桥接\n * \n * 在主进程中调用,注册 IPC handlers\n * 集成 SQLite 存储\n */\n\nimport { ipcMain, shell } from 'electron';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { \n HybridAgent, \n type AgentConfig, \n type ChatEvent,\n type ChatOptions,\n type ChatMode,\n type ProviderType,\n type ToolConfigItem,\n MODELS,\n DEFAULT_MODEL,\n} from '@huyooo/ai-chat-core';\nimport { createSearchElectronBridge } from '@huyooo/ai-search/bridge/electron';\n\n// 注意:工具解析已移至 HybridAgent 内部(asyncInit 方法)\n// 这里不再需要提前解析,直接传递 ToolConfigItem[] 给 HybridAgent\n\n/** 文件信息 */\nexport interface FileInfo {\n name: string;\n path: string;\n isDirectory: boolean;\n size: number;\n modifiedAt: Date;\n extension: string;\n}\nimport {\n createStorage,\n type StorageAdapter,\n type StorageContext,\n type SessionRecord,\n type MessageRecord,\n type CreateSessionInput,\n type CreateMessageInput,\n} from '@huyooo/ai-chat-storage';\n\nexport interface ElectronBridgeOptions extends Omit<AgentConfig, 'tools'> {\n /** IPC channel 前缀 */\n channelPrefix?: string;\n /** Tavily API Key(用于统一 Web Search;可选) */\n tavilyApiKey?: string;\n /** \n * 统一数据目录(必填)\n * 所有数据都存储在此目录下:\n * - db.sqlite: 对话历史\n * - search-data/: 搜索索引(由宿主 tools 注入 searchPlugin 时使用)\n */\n dataDir: string;\n /** 默认租户上下文 */\n defaultContext?: StorageContext;\n /** \n * 工具列表(Vite 插件风格)\n * 支持:ToolPlugin、Promise<ToolPlugin>\n */\n tools?: ToolConfigItem[];\n}\n\n/** 发送消息参数 */\ninterface SendMessageParams {\n message: string;\n images?: string[];\n options?: ChatOptions;\n sessionId?: string;\n}\n\n/** 会话相关参数 */\ninterface SessionParams {\n id?: string;\n title?: string;\n model?: string;\n mode?: 'agent' | 'plan' | 'ask';\n webSearchEnabled?: boolean;\n thinkingEnabled?: boolean;\n hidden?: boolean;\n}\n\n/** 消息相关参数 */\ninterface MessageParams {\n /** 消息 ID(可选,如果不传则自动生成) */\n id?: string;\n sessionId: string;\n role: 'user' | 'assistant';\n content: string;\n /** 用户消息附带的图片(data URL 或 base64) */\n images?: string[];\n /** 生成此消息时使用的模型 */\n model?: string;\n /** 生成此消息时使用的模式 (ask/agent) */\n mode?: string;\n /** 生成此消息时是否启用 web 搜索 */\n webSearchEnabled?: boolean;\n /** 生成此消息时是否启用深度思考 */\n thinkingEnabled?: boolean;\n /** 执行步骤列表 JSON */\n steps?: string;\n operationIds?: string;\n}\n\n/** 更新消息参数 */\ninterface UpdateMessageParams {\n id: string;\n content?: string;\n steps?: string;\n}\n\n/**\n * 创建 Electron IPC 桥接\n * \n * @example\n * ```ts\n * import { createElectronBridge } from '@huyooo/ai-chat-bridge-electron/main';\n * \n * app.whenReady().then(async () => {\n * const { agent, storage } = await createElectronBridge({\n * arkApiKey: 'xxx',\n * geminiApiKey: 'xxx',\n * });\n * });\n * ```\n */\nexport async function createElectronBridge(options: ElectronBridgeOptions) {\n const { \n channelPrefix = 'ai-chat', \n dataDir,\n defaultContext = {},\n ...agentConfig \n } = options;\n \n // 对话历史 SQLite 路径(统一规则:dataDir/db.sqlite)\n const resolvedStoragePath = path.join(dataDir, 'db.sqlite');\n \n // 🔴 重要:在处理任何 tools 之前,先初始化搜索 Electron 桥接\n // 这样当 searchPlugin 开始后台索引时,全局进度监听器已经注册好了\n createSearchElectronBridge({\n ipcMain,\n channelPrefix,\n }).init();\n \n // 工具批准请求的等待队列(key: toolCallId, value: { resolve, reject, webContents })\n const pendingApprovals = new Map<string, {\n resolve: (approved: boolean) => void;\n reject: (error: Error) => void;\n webContents: Electron.WebContents;\n }>();\n \n // 创建工具批准回调\n const toolApprovalCallback = async (toolCall: {\n id: string;\n name: string;\n args: Record<string, unknown>;\n }): Promise<boolean> => {\n console.log('[Main] 工具批准回调被调用:', toolCall.name);\n // 查找当前请求的 webContents(通过最近的 send 调用)\n // 这里我们需要一个更好的方式来获取 webContents\n // 暂时使用一个全局变量存储当前的 webContents\n const currentWebContents = (global as { currentWebContents?: Electron.WebContents }).currentWebContents;\n if (!currentWebContents) {\n console.log('[Main] 警告: 没有 webContents,默认批准');\n // 如果没有 webContents,默认批准(向后兼容)\n return true;\n }\n\n return new Promise<boolean>((resolve, reject) => {\n // 存储 Promise 的 resolve/reject\n pendingApprovals.set(toolCall.id, {\n resolve,\n reject,\n webContents: currentWebContents,\n });\n\n // 发送批准请求到前端\n console.log('[Main] 发送工具批准请求到前端:', toolCall.name);\n currentWebContents.send(`${channelPrefix}:toolApprovalRequest`, {\n id: toolCall.id,\n name: toolCall.name,\n args: toolCall.args,\n });\n \n // 不设置超时,让用户有足够时间思考\n });\n };\n \n // 初始化存储(在 agent 之前,因为 getAutoRunConfig 需要访问 storage)\n const storage = await createStorage({\n type: 'sqlite',\n sqlitePath: resolvedStoragePath,\n });\n\n // 获取上下文\n const getContext = (): StorageContext => defaultContext;\n \n // 动态获取自动运行配置(从数据库实时读取)\n const getAutoRunConfig = async () => {\n try {\n const configJson = await storage.getUserSetting('autoRunConfig', getContext());\n if (configJson) {\n return JSON.parse(configJson) as {\n mode?: 'run-everything' | 'manual';\n };\n }\n } catch (error) {\n console.error('[Main] 获取 autoRunConfig 失败:', error);\n }\n return undefined;\n };\n \n // 创建 Agent(工具会在 HybridAgent 的 asyncInit 中自动解析)\n const agent = new HybridAgent({\n ...agentConfig,\n // tools 直接传递 ToolConfigItem[],HybridAgent 会在 asyncInit 中解析\n tools: options.tools,\n onToolApprovalRequest: toolApprovalCallback,\n getAutoRunConfig,\n });\n\n // ============ AI Chat API ============\n\n // 获取可用模型\n ipcMain.handle(`${channelPrefix}:models`, () => {\n return MODELS;\n });\n\n // 获取所有工具列表(用于设置面板)\n ipcMain.handle(`${channelPrefix}:getAllTools`, async () => {\n // 确保工具已初始化(通过调用 chat 方法触发 asyncInit)\n // 如果工具还未初始化,先触发一次初始化\n console.log('[Main] getAllTools 调用,当前工具数量:', agent['tools'].size);\n console.log('[Main] toolConfig 是否存在:', !!agent['toolConfig']);\n console.log('[Main] toolConfig 内容:', agent['toolConfig'] ? `${agent['toolConfig'].length} 个工具配置` : 'undefined');\n \n // 如果工具未初始化,尝试初始化\n if (agent['tools'].size === 0 && agent['toolConfig']) {\n console.log('[Main] 工具未初始化,开始 asyncInit...');\n await agent['asyncInit']();\n console.log('[Main] asyncInit 完成,工具数量:', agent['tools'].size);\n } else if (!agent['toolConfig']) {\n console.warn('[Main] ⚠️ toolConfig 不存在,工具可能未注入!');\n }\n \n const tools = (agent as any).getAllTools();\n console.log('[Main] getAllTools 返回:', tools.length, '个工具', tools.map((t: any) => t.name));\n return tools;\n });\n\n // 发送消息\n ipcMain.handle(`${channelPrefix}:send`, async (event, params: SendMessageParams) => {\n const webContents = event.sender;\n const { message, images, options = {}, sessionId } = params;\n\n // 存储当前 webContents,供工具批准回调使用\n (global as { currentWebContents?: Electron.WebContents }).currentWebContents = webContents;\n\n console.log('[AI-Chat] 收到消息:', { message, options, images: images?.length || 0, sessionId });\n console.log('[AI-Chat] autoRunConfig:', JSON.stringify(options.autoRunConfig, null, 2));\n\n try {\n for await (const chatEvent of agent.chat(message, options, images)) {\n // 调试:打印事件\n console.log('[AI-Chat] 事件:', chatEvent.type, \n chatEvent.type === 'text_delta' ? (chatEvent.data as { content: string }).content.slice(0, 20) : \n chatEvent.type === 'thinking_delta' ? (chatEvent.data as { content: string }).content.slice(0, 20) :\n chatEvent.type === 'search_result' ? `搜索完成 ${(chatEvent.data as { results?: unknown[] }).results?.length || 0} 条` :\n JSON.stringify(chatEvent.data).slice(0, 100)\n );\n \n // 发送事件到渲染进程(携带 sessionId 以区分不同会话)\n if (!webContents.isDestroyed()) {\n webContents.send(`${channelPrefix}:progress`, { ...chatEvent, sessionId });\n }\n }\n console.log('[AI-Chat] 完成');\n } catch (error) {\n console.error('[AI-Chat] 错误:', error);\n if (error instanceof Error) {\n console.error('[AI-Chat] 错误详情:', {\n message: error.message,\n stack: error.stack,\n name: error.name,\n });\n }\n if (!webContents.isDestroyed()) {\n const errorData = error instanceof Error \n ? { \n category: 'api',\n message: error.message || String(error),\n cause: error.stack,\n }\n : { \n category: 'api',\n message: String(error),\n };\n webContents.send(`${channelPrefix}:progress`, { \n type: 'error', \n data: errorData,\n sessionId\n });\n }\n } finally {\n // 清理当前 webContents\n delete (global as { currentWebContents?: Electron.WebContents }).currentWebContents;\n }\n });\n\n // 工具批准响应\n ipcMain.handle(`${channelPrefix}:toolApprovalResponse`, (_event, params: { id: string; approved: boolean }) => {\n const { id, approved } = params;\n const pending = pendingApprovals.get(id);\n if (pending) {\n pendingApprovals.delete(id);\n pending.resolve(approved);\n }\n });\n\n // 取消请求\n ipcMain.handle(`${channelPrefix}:cancel`, () => {\n agent.abort();\n });\n\n // ============ 用户设置 API ============\n\n // 获取用户设置\n ipcMain.handle(`${channelPrefix}:settings:get`, async (_event, key: string) => {\n return storage.getUserSetting(key, getContext());\n });\n\n // 设置用户设置\n ipcMain.handle(`${channelPrefix}:settings:set`, async (_event, key: string, value: string) => {\n await storage.setUserSetting(key, value, getContext());\n return { success: true };\n });\n\n // 获取所有用户设置\n ipcMain.handle(`${channelPrefix}:settings:getAll`, async () => {\n return storage.getUserSettings(getContext());\n });\n\n // 删除用户设置\n ipcMain.handle(`${channelPrefix}:settings:delete`, async (_event, key: string) => {\n await storage.deleteUserSetting(key, getContext());\n return { success: true };\n });\n\n // 无状态架构:历史通过 ChatOptions.history 传入,不再需要内存历史 API\n\n // 设置当前工作目录\n ipcMain.handle(`${channelPrefix}:setCwd`, (_event, dir: string) => {\n agent.setCwd(dir);\n });\n\n // 获取当前配置\n ipcMain.handle(`${channelPrefix}:config`, () => {\n return agent.getConfig();\n });\n\n // ============ Storage API ============\n\n // 获取会话列表\n ipcMain.handle(`${channelPrefix}:sessions:list`, async () => {\n return storage.getSessions(getContext());\n });\n\n // 获取单个会话\n ipcMain.handle(`${channelPrefix}:sessions:get`, async (_event, id: string) => {\n return storage.getSession(id, getContext());\n });\n\n // 创建会话\n ipcMain.handle(`${channelPrefix}:sessions:create`, async (_event, params: SessionParams) => {\n // 无状态架构:历史通过 ChatOptions.history 传入,不需要清空内存\n \n const input: CreateSessionInput = {\n id: params.id || crypto.randomUUID(),\n title: params.title || '新对话',\n model: params.model || DEFAULT_MODEL,\n mode: (params.mode || 'agent') as 'agent' | 'plan' | 'ask',\n webSearchEnabled: params.webSearchEnabled ?? true,\n thinkingEnabled: params.thinkingEnabled ?? true,\n hidden: params.hidden ?? false,\n };\n return storage.createSession(input, getContext());\n });\n\n // 更新会话\n ipcMain.handle(`${channelPrefix}:sessions:update`, async (_event, id: string, data: Partial<SessionParams>) => {\n await storage.updateSession(id, data, getContext());\n return storage.getSession(id, getContext());\n });\n\n // 删除会话\n ipcMain.handle(`${channelPrefix}:sessions:delete`, async (_event, id: string) => {\n await storage.deleteSession(id, getContext());\n return { success: true };\n });\n\n // 获取消息列表\n ipcMain.handle(`${channelPrefix}:messages:list`, async (_event, sessionId: string) => {\n return storage.getMessages(sessionId, getContext());\n });\n\n // 保存消息\n ipcMain.handle(`${channelPrefix}:messages:save`, async (_event, params: MessageParams) => {\n const input: CreateMessageInput = {\n id: params.id || crypto.randomUUID(),\n sessionId: params.sessionId,\n role: params.role,\n content: params.content,\n images: params.images || [],\n model: params.model || null,\n mode: params.mode || null,\n webSearchEnabled: params.webSearchEnabled ?? null,\n thinkingEnabled: params.thinkingEnabled ?? null,\n steps: params.steps || null,\n operationIds: params.operationIds || null,\n };\n return storage.saveMessage(input, getContext());\n });\n\n // 更新消息(增量保存)\n ipcMain.handle(`${channelPrefix}:messages:update`, async (_event, params: UpdateMessageParams) => {\n await storage.updateMessage(params.id, {\n content: params.content,\n steps: params.steps,\n }, getContext());\n return { success: true };\n });\n\n // 删除指定时间之后的消息(用于分叉)\n ipcMain.handle(`${channelPrefix}:messages:deleteAfter`, async (_event, sessionId: string, timestamp: number) => {\n await storage.deleteMessagesAfter(sessionId, new Date(timestamp), getContext());\n return { success: true };\n });\n\n // 删除指定消息之后的所有消息(用于分叉)\n ipcMain.handle(`${channelPrefix}:messages:deleteAfterMessageId`, async (_event, sessionId: string, messageId: string) => {\n await storage.deleteMessagesAfterMessageId(sessionId, messageId, getContext());\n return { success: true };\n });\n\n // 获取操作日志\n ipcMain.handle(`${channelPrefix}:operations:list`, async (_event, sessionId: string) => {\n return storage.getOperations(sessionId, getContext());\n });\n\n // 获取回收站\n ipcMain.handle(`${channelPrefix}:trash:list`, async () => {\n return storage.getTrashItems?.(getContext()) || [];\n });\n\n // 恢复回收站项目\n ipcMain.handle(`${channelPrefix}:trash:restore`, async (_event, id: string) => {\n return storage.restoreFromTrash?.(id, getContext());\n });\n\n // ============ System API ============\n \n // 在系统默认浏览器中打开链接\n ipcMain.handle(`${channelPrefix}:openExternal`, async (_event, url: string) => {\n return shell.openExternal(url);\n });\n\n // ============ File System API ============\n\n // 列出目录内容\n ipcMain.handle(`${channelPrefix}:fs:listDir`, async (_event, dirPath: string): Promise<FileInfo[]> => {\n try {\n const entries = fs.readdirSync(dirPath, { withFileTypes: true });\n const files: FileInfo[] = [];\n\n for (const entry of entries) {\n // 跳过隐藏文件\n if (entry.name.startsWith('.')) continue;\n\n const fullPath = path.join(dirPath, entry.name);\n try {\n const stats = fs.statSync(fullPath);\n files.push({\n name: entry.name,\n path: fullPath,\n isDirectory: entry.isDirectory(),\n size: stats.size,\n modifiedAt: stats.mtime,\n extension: entry.isDirectory() ? '' : path.extname(entry.name).toLowerCase(),\n });\n } catch {\n // 跳过无法访问的文件\n }\n }\n\n // 排序:目录在前,文件在后,按名称排序\n return files.sort((a, b) => {\n if (a.isDirectory && !b.isDirectory) return -1;\n if (!a.isDirectory && b.isDirectory) return 1;\n return a.name.localeCompare(b.name);\n });\n } catch (error) {\n console.error('[AI-Chat] 列出目录失败:', error);\n return [];\n }\n });\n\n // 检查路径是否存在\n ipcMain.handle(`${channelPrefix}:fs:exists`, async (_event, filePath: string): Promise<boolean> => {\n return fs.existsSync(filePath);\n });\n\n // 获取文件信息\n ipcMain.handle(`${channelPrefix}:fs:stat`, async (_event, filePath: string): Promise<FileInfo | null> => {\n try {\n const stats = fs.statSync(filePath);\n return {\n name: path.basename(filePath),\n path: filePath,\n isDirectory: stats.isDirectory(),\n size: stats.size,\n modifiedAt: stats.mtime,\n extension: stats.isDirectory() ? '' : path.extname(filePath).toLowerCase(),\n };\n } catch {\n return null;\n }\n });\n\n // 读取文件内容(文本文件)\n ipcMain.handle(`${channelPrefix}:fs:readFile`, async (_event, filePath: string): Promise<string | null> => {\n try {\n return fs.readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n });\n\n // 读取文件为 base64(图片等二进制文件)\n ipcMain.handle(`${channelPrefix}:fs:readFileBase64`, async (_event, filePath: string): Promise<string | null> => {\n try {\n const buffer = fs.readFileSync(filePath);\n return buffer.toString('base64');\n } catch {\n return null;\n }\n });\n\n // 获取用户主目录\n ipcMain.handle(`${channelPrefix}:fs:homeDir`, async (): Promise<string> => {\n return process.env.HOME || process.env.USERPROFILE || '/';\n });\n\n // 解析路径(处理 ~)\n ipcMain.handle(`${channelPrefix}:fs:resolvePath`, async (_event, inputPath: string): Promise<string> => {\n if (inputPath.startsWith('~')) {\n const homeDir = process.env.HOME || process.env.USERPROFILE || '/';\n return path.join(homeDir, inputPath.slice(1));\n }\n return path.resolve(inputPath);\n });\n\n // 获取父目录\n ipcMain.handle(`${channelPrefix}:fs:parentDir`, async (_event, dirPath: string): Promise<string> => {\n return path.dirname(dirPath);\n });\n\n // ============ File System Watch API ============\n \n // 存储活跃的 watcher\n const activeWatchers = new Map<string, fs.FSWatcher>();\n \n // 监听目录变化\n ipcMain.handle(`${channelPrefix}:fs:watchDir`, async (event, dirPath: string): Promise<boolean> => {\n const webContents = event.sender;\n \n // 如果已经在监听,先停止\n if (activeWatchers.has(dirPath)) {\n activeWatchers.get(dirPath)?.close();\n activeWatchers.delete(dirPath);\n }\n \n try {\n const watcher = fs.watch(dirPath, { persistent: false }, (eventType, filename) => {\n if (!webContents.isDestroyed()) {\n webContents.send(`${channelPrefix}:fs:dirChange`, {\n dirPath,\n eventType,\n filename,\n });\n }\n });\n \n watcher.on('error', (error) => {\n console.error('[AI-Chat] Watch error:', error);\n activeWatchers.delete(dirPath);\n });\n \n activeWatchers.set(dirPath, watcher);\n return true;\n } catch (error) {\n console.error('[AI-Chat] Failed to watch directory:', error);\n return false;\n }\n });\n \n // 停止监听目录\n ipcMain.handle(`${channelPrefix}:fs:unwatchDir`, async (_event, dirPath: string): Promise<void> => {\n const watcher = activeWatchers.get(dirPath);\n if (watcher) {\n watcher.close();\n activeWatchers.delete(dirPath);\n }\n });\n\n return { agent, storage };\n}\n\n// 导出类型\nexport type { \n AgentConfig, \n ChatEvent,\n ChatOptions,\n ChatMode,\n ProviderType,\n StorageAdapter,\n StorageContext,\n SessionRecord,\n MessageRecord,\n};\n\nexport { MODELS };\n\n// 导出 ASR 模块\nexport { createAsrBridge, type AsrBridgeConfig } from './asr';\nexport type { AsrClientConfig, AsrSessionConfig, AsrCallbacks } from './asr/client';\nexport type { AsrResult, AsrRequestConfig } from './asr/protocol';\n","/**\n * 火山引擎大模型流式语音识别 - 二进制协议编解码\n * \n * 协议结构:\n * - Header (4 bytes)\n * - [Sequence (4 bytes, optional)]\n * - Payload size (4 bytes)\n * - Payload (variable)\n */\n\nimport * as zlib from 'zlib';\n\n// ============ 协议常量 ============\n\n/** 协议版本 */\nconst PROTOCOL_VERSION = 0b0001;\n/** Header 大小 (1 = 4 bytes) */\nconst HEADER_SIZE = 0b0001;\n\n/** 消息类型 */\nexport const MessageType = {\n /** 客户端发送:包含请求参数的完整请求 */\n FULL_CLIENT_REQUEST: 0b0001,\n /** 客户端发送:仅包含音频数据 */\n AUDIO_ONLY_REQUEST: 0b0010,\n /** 服务端响应:包含识别结果 */\n FULL_SERVER_RESPONSE: 0b1001,\n /** 服务端响应:错误消息 */\n ERROR_RESPONSE: 0b1111,\n} as const;\n\n/** 消息类型特定标志 */\nexport const MessageFlags = {\n /** 无特殊标志 */\n NONE: 0b0000,\n /** header 后 4 字节为正 sequence number */\n HAS_SEQUENCE: 0b0001,\n /** 最后一包(负包) */\n LAST_PACKET: 0b0010,\n /** 最后一包且有 sequence number */\n LAST_PACKET_WITH_SEQUENCE: 0b0011,\n} as const;\n\n/** 序列化方法 */\nexport const SerializationMethod = {\n NONE: 0b0000,\n JSON: 0b0001,\n} as const;\n\n/** 压缩方法 */\nexport const CompressionMethod = {\n NONE: 0b0000,\n GZIP: 0b0001,\n} as const;\n\n// ============ 类型定义 ============\n\n/** ASR 请求配置 */\nexport interface AsrRequestConfig {\n /** 用户相关配置 */\n user?: {\n uid?: string;\n did?: string;\n platform?: string;\n sdk_version?: string;\n app_version?: string;\n };\n /** 音频配置 */\n audio: {\n format: 'pcm' | 'wav' | 'ogg' | 'mp3';\n codec?: 'raw' | 'opus';\n rate?: number;\n bits?: number;\n channel?: number;\n /** 语种(仅流式输入模式支持) */\n language?: string;\n };\n /** 请求配置 */\n request: {\n model_name: 'bigmodel';\n /** 启用 ITN(数字规整) */\n enable_itn?: boolean;\n /** 启用标点 */\n enable_punc?: boolean;\n /** 启用语义顺滑 */\n enable_ddc?: boolean;\n /** 输出语音停顿、分句、分词信息 */\n show_utterances?: boolean;\n /** 结果返回方式:full(全量)/single(增量) */\n result_type?: 'full' | 'single';\n /** 启用首字返回加速 */\n enable_accelerate_text?: boolean;\n /** 首字返回加速率 (0-20) */\n accelerate_score?: number;\n /** 语义切句最大静音阈值 (ms) */\n vad_segment_duration?: number;\n /** 强制判停时间 (ms) */\n end_window_size?: number;\n /** 热词等语料配置 */\n corpus?: {\n boosting_table_name?: string;\n boosting_table_id?: string;\n correct_table_name?: string;\n correct_table_id?: string;\n context?: string;\n };\n };\n}\n\n/** ASR 识别结果 */\nexport interface AsrResult {\n result?: {\n text: string;\n utterances?: Array<{\n text: string;\n start_time: number;\n end_time: number;\n definite: boolean;\n words?: Array<{\n text: string;\n start_time: number;\n end_time: number;\n blank_duration: number;\n }>;\n }>;\n };\n audio_info?: {\n duration: number;\n };\n}\n\n/** 服务端响应 */\nexport interface ServerResponse {\n type: 'result' | 'error';\n sequence?: number;\n isLast: boolean;\n data: AsrResult | { code: number; message: string };\n}\n\n// ============ 编码函数 ============\n\n/**\n * 构建 header 字节\n */\nfunction buildHeader(\n messageType: number,\n messageFlags: number,\n serialization: number,\n compression: number\n): number[] {\n return [\n (PROTOCOL_VERSION << 4) | HEADER_SIZE,\n (messageType << 4) | messageFlags,\n (serialization << 4) | compression,\n 0x00, // reserved\n ];\n}\n\n/**\n * 编码 Full Client Request(首包)\n */\nexport function encodeFullClientRequest(\n config: AsrRequestConfig,\n useGzip = true\n): Buffer {\n const header = buildHeader(\n MessageType.FULL_CLIENT_REQUEST,\n MessageFlags.NONE,\n SerializationMethod.JSON,\n useGzip ? CompressionMethod.GZIP : CompressionMethod.NONE\n );\n\n const jsonPayload = JSON.stringify(config);\n const payloadBuffer = Buffer.from(jsonPayload, 'utf-8');\n const compressed = useGzip ? zlib.gzipSync(payloadBuffer) : payloadBuffer;\n\n // 构建最终消息\n const payloadSize = Buffer.alloc(4);\n payloadSize.writeUInt32BE(compressed.length, 0);\n\n return Buffer.concat([\n Buffer.from(header),\n payloadSize,\n compressed,\n ]);\n}\n\n/**\n * 编码 Audio Only Request(音频包)\n */\nexport function encodeAudioOnlyRequest(\n audioData: Buffer,\n isLast = false,\n useGzip = true\n): Buffer {\n const header = buildHeader(\n MessageType.AUDIO_ONLY_REQUEST,\n isLast ? MessageFlags.LAST_PACKET : MessageFlags.NONE,\n SerializationMethod.NONE,\n useGzip ? CompressionMethod.GZIP : CompressionMethod.NONE\n );\n\n const compressed = useGzip ? zlib.gzipSync(audioData) : audioData;\n\n const payloadSize = Buffer.alloc(4);\n payloadSize.writeUInt32BE(compressed.length, 0);\n\n return Buffer.concat([\n Buffer.from(header),\n payloadSize,\n compressed,\n ]);\n}\n\n// ============ 解码函数 ============\n\n/**\n * 解码服务端响应\n */\nexport function decodeServerResponse(data: Buffer): ServerResponse {\n if (data.length < 4) {\n throw new Error('Invalid response: too short');\n }\n\n // 解析 header\n const byte0 = data[0];\n const byte1 = data[1];\n const byte2 = data[2];\n\n const version = (byte0 >> 4) & 0x0f;\n const headerSize = (byte0 & 0x0f) * 4;\n const messageType = (byte1 >> 4) & 0x0f;\n const messageFlags = byte1 & 0x0f;\n const serialization = (byte2 >> 4) & 0x0f;\n const compression = byte2 & 0x0f;\n\n console.log('[ASR Protocol] 解析响应:', {\n version,\n headerSize,\n messageType: messageType.toString(2).padStart(4, '0'),\n messageFlags: messageFlags.toString(2).padStart(4, '0'),\n serialization,\n compression,\n dataLength: data.length,\n headerHex: data.slice(0, 4).toString('hex'),\n });\n\n if (version !== PROTOCOL_VERSION) {\n throw new Error(`Unsupported protocol version: ${version}`);\n }\n\n let offset = headerSize;\n let sequence: number | undefined;\n\n // 检查是否有 sequence number(根据 messageFlags 的最低位判断)\n // 0b0001 或 0b0011 表示有 sequence number\n const hasSequence = (messageFlags & 0x01) === 0x01;\n \n if (hasSequence && data.length >= offset + 4) {\n sequence = data.readUInt32BE(offset);\n offset += 4;\n console.log('[ASR Protocol] sequence:', sequence);\n }\n\n // 0b0010 或 0b0011 表示是最后一包\n const isLast = (messageFlags & 0x02) === 0x02;\n\n // 错误响应\n if (messageType === MessageType.ERROR_RESPONSE) {\n const errorCode = data.readUInt32BE(offset);\n offset += 4;\n const errorMsgSize = data.readUInt32BE(offset);\n offset += 4;\n const errorMsg = data.slice(offset, offset + errorMsgSize).toString('utf-8');\n\n console.log('[ASR Protocol] 错误响应:', { errorCode, errorMsg });\n\n return {\n type: 'error',\n sequence,\n isLast: true,\n data: { code: errorCode, message: errorMsg },\n };\n }\n\n // 正常响应\n if (messageType === MessageType.FULL_SERVER_RESPONSE) {\n const payloadSize = data.readUInt32BE(offset);\n offset += 4;\n\n console.log('[ASR Protocol] payloadSize:', payloadSize, 'offset:', offset, 'remaining:', data.length - offset);\n\n if (payloadSize === 0) {\n // 空响应(比如首包确认)\n return {\n type: 'result',\n sequence,\n isLast,\n data: {},\n };\n }\n\n let payload = data.slice(offset, offset + payloadSize);\n\n // 解压\n if (compression === CompressionMethod.GZIP) {\n try {\n payload = zlib.gunzipSync(payload);\n } catch (e) {\n console.error('[ASR Protocol] gzip 解压失败:', e);\n console.log('[ASR Protocol] 原始 payload 前 32 字节:', payload.slice(0, 32).toString('hex'));\n throw e;\n }\n }\n\n console.log('[ASR Protocol] 解压后 payload 长度:', payload.length);\n\n // 反序列化\n let result: AsrResult;\n if (serialization === SerializationMethod.JSON) {\n const jsonStr = payload.toString('utf-8');\n console.log('[ASR Protocol] JSON 字符串前 200 字符:', jsonStr.slice(0, 200));\n try {\n result = JSON.parse(jsonStr);\n } catch (e) {\n console.error('[ASR Protocol] JSON 解析失败:', e);\n console.log('[ASR Protocol] 完整 JSON:', jsonStr);\n throw e;\n }\n } else {\n result = {};\n }\n\n return {\n type: 'result',\n sequence,\n isLast,\n data: result,\n };\n }\n\n throw new Error(`Unknown message type: ${messageType}`);\n}\n\n","/**\n * 火山引擎大模型流式语音识别 - WebSocket 客户端\n * \n * 在 Electron 主进程中运行,处理与火山引擎 ASR 服务的通信\n */\n\nimport WebSocket from 'ws';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n encodeFullClientRequest,\n encodeAudioOnlyRequest,\n decodeServerResponse,\n type AsrRequestConfig,\n type AsrResult,\n} from './protocol';\n\n// ============ 类型定义 ============\n\n/** ASR 客户端配置 */\nexport interface AsrClientConfig {\n /** 火山引擎 App ID */\n appId: string;\n /** 火山引擎 Access Key */\n accessKey: string;\n /** 资源 ID */\n resourceId?: string;\n /** 使用双向流式优化版 */\n useAsyncMode?: boolean;\n}\n\n/** ASR 事件回调 */\nexport interface AsrCallbacks {\n /** 连接成功 */\n onConnected?: () => void;\n /** 收到识别结果 */\n onResult?: (result: AsrResult, isLast: boolean) => void;\n /** 发生错误 */\n onError?: (error: Error) => void;\n /** 连接关闭 */\n onClose?: () => void;\n}\n\n/** ASR 会话配置 */\nexport interface AsrSessionConfig {\n /** 音频格式 */\n format?: 'pcm' | 'wav';\n /** 采样率 */\n sampleRate?: number;\n /** 启用标点 */\n enablePunc?: boolean;\n /** 启用 ITN(数字规整) */\n enableItn?: boolean;\n /** 启用语义顺滑 */\n enableDdc?: boolean;\n /** 输出分句信息 */\n showUtterances?: boolean;\n}\n\n// ============ ASR 客户端类 ============\n\n/**\n * ASR WebSocket 客户端\n * \n * @example\n * ```ts\n * const client = new AsrClient({\n * appId: 'your-app-id',\n * accessKey: 'your-access-key',\n * });\n * \n * await client.connect({\n * onResult: (result, isLast) => {\n * console.log('识别结果:', result.result?.text);\n * },\n * onError: (error) => {\n * console.error('错误:', error);\n * },\n * });\n * \n * client.sendAudio(audioBuffer);\n * client.finish(); // 发送最后一包\n * ```\n */\nexport class AsrClient {\n private config: AsrClientConfig;\n private ws: WebSocket | null = null;\n private callbacks: AsrCallbacks = {};\n private connectId: string = '';\n private isConnected = false;\n private sessionConfig: AsrSessionConfig = {};\n\n constructor(config: AsrClientConfig) {\n this.config = config;\n }\n\n /**\n * 获取 WebSocket 连接地址\n */\n private getWsUrl(): string {\n // 默认使用双向流式优化版\n if (this.config.useAsyncMode !== false) {\n return 'wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async';\n }\n return 'wss://openspeech.bytedance.com/api/v3/sauc/bigmodel';\n }\n\n /**\n * 获取资源 ID\n */\n private getResourceId(): string {\n return this.config.resourceId || 'volc.bigasr.sauc.duration';\n }\n\n /**\n * 连接到 ASR 服务\n */\n connect(callbacks: AsrCallbacks, sessionConfig: AsrSessionConfig = {}): Promise<void> {\n return new Promise((resolve, reject) => {\n this.callbacks = callbacks;\n this.sessionConfig = sessionConfig;\n this.connectId = uuidv4();\n\n const wsUrl = this.getWsUrl();\n\n this.ws = new WebSocket(wsUrl, {\n headers: {\n 'X-Api-App-Key': this.config.appId,\n 'X-Api-Access-Key': this.config.accessKey,\n 'X-Api-Resource-Id': this.getResourceId(),\n 'X-Api-Connect-Id': this.connectId,\n },\n });\n\n this.ws.on('open', () => {\n console.log('[ASR] WebSocket 连接成功, connectId:', this.connectId);\n this.isConnected = true;\n \n // 发送首包(Full Client Request)\n this.sendFullClientRequest();\n \n this.callbacks.onConnected?.();\n resolve();\n });\n\n this.ws.on('message', (data: Buffer) => {\n try {\n const response = decodeServerResponse(data);\n \n if (response.type === 'error') {\n const errorData = response.data as { code: number; message: string };\n console.error('[ASR] 服务端错误:', errorData);\n this.callbacks.onError?.(new Error(`ASR Error ${errorData.code}: ${errorData.message}`));\n } else {\n const result = response.data as AsrResult;\n this.callbacks.onResult?.(result, response.isLast);\n \n if (response.isLast) {\n console.log('[ASR] 收到最终结果');\n }\n }\n } catch (error) {\n console.error('[ASR] 解析响应失败:', error);\n this.callbacks.onError?.(error instanceof Error ? error : new Error(String(error)));\n }\n });\n\n this.ws.on('error', (error) => {\n console.error('[ASR] WebSocket 错误:', error);\n this.callbacks.onError?.(error);\n reject(error);\n });\n\n this.ws.on('close', () => {\n console.log('[ASR] WebSocket 连接关闭');\n this.isConnected = false;\n this.ws = null;\n this.callbacks.onClose?.();\n });\n });\n }\n\n /**\n * 发送首包(Full Client Request)\n */\n private sendFullClientRequest(): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n console.error('[ASR] WebSocket 未连接');\n return;\n }\n\n const config: AsrRequestConfig = {\n user: {\n uid: 'ai-chat-user',\n },\n audio: {\n format: this.sessionConfig.format || 'pcm',\n rate: this.sessionConfig.sampleRate || 16000,\n bits: 16,\n channel: 1,\n },\n request: {\n model_name: 'bigmodel',\n enable_itn: this.sessionConfig.enableItn ?? true,\n enable_punc: this.sessionConfig.enablePunc ?? true,\n enable_ddc: this.sessionConfig.enableDdc ?? false,\n show_utterances: this.sessionConfig.showUtterances ?? true,\n result_type: 'full',\n },\n };\n\n const packet = encodeFullClientRequest(config);\n this.ws.send(packet);\n console.log('[ASR] 已发送首包');\n }\n\n /**\n * 发送音频数据\n * @param audioData PCM 音频数据(16bit, 16kHz, 单声道)\n */\n sendAudio(audioData: Buffer): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n console.error('[ASR] WebSocket 未连接,无法发送音频');\n return;\n }\n\n const packet = encodeAudioOnlyRequest(audioData, false);\n this.ws.send(packet);\n }\n\n /**\n * 发送最后一包并结束识别\n */\n finish(): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n console.error('[ASR] WebSocket 未连接,无法结束');\n return;\n }\n\n // 发送空的最后一包\n const packet = encodeAudioOnlyRequest(Buffer.alloc(0), true);\n this.ws.send(packet);\n console.log('[ASR] 已发送结束包');\n }\n\n /**\n * 断开连接\n */\n disconnect(): void {\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n this.isConnected = false;\n }\n\n /**\n * 检查是否已连接\n */\n get connected(): boolean {\n return this.isConnected && this.ws?.readyState === WebSocket.OPEN;\n }\n}\n\n/**\n * 创建 ASR 客户端实例\n */\nexport function createAsrClient(config: AsrClientConfig): AsrClient {\n return new AsrClient(config);\n}\n\n","/**\n * ASR 模块导出\n */\n\nexport * from './protocol';\nexport * from './client';\n\nimport { ipcMain, type IpcMainInvokeEvent, type WebContents } from 'electron';\nimport { AsrClient, type AsrClientConfig, type AsrSessionConfig } from './client';\nimport type { AsrResult } from './protocol';\n\n// ============ 类型定义 ============\n\n/** ASR 桥接配置 */\nexport interface AsrBridgeConfig {\n /** IPC channel 前缀 */\n channelPrefix?: string;\n /** 火山引擎 App ID */\n appId: string;\n /** 火山引擎 Access Key */\n accessKey: string;\n /** 资源 ID */\n resourceId?: string;\n}\n\n/** ASR 会话状态 */\ninterface AsrSession {\n client: AsrClient;\n webContents: WebContents;\n}\n\n// ============ ASR IPC Handlers ============\n\n/**\n * 创建 ASR Electron 桥接\n * \n * 在主进程中调用,注册 ASR 相关的 IPC handlers\n * \n * @example\n * ```ts\n * import { createAsrBridge } from '@huyooo/ai-chat-bridge-electron/main';\n * \n * createAsrBridge({\n * appId: process.env.VOLC_APP_ID!,\n * accessKey: process.env.VOLC_ACCESS_KEY!,\n * });\n * ```\n */\nexport function createAsrBridge(config: AsrBridgeConfig) {\n const { channelPrefix = 'ai-chat', appId, accessKey, resourceId } = config;\n \n // 当前活跃的 ASR 会话(一个窗口同时只能有一个会话)\n const sessions = new Map<number, AsrSession>();\n\n /**\n * 获取或创建 ASR 会话\n */\n function getOrCreateSession(webContentsId: number, webContents: WebContents): AsrSession {\n let session = sessions.get(webContentsId);\n \n if (!session) {\n const clientConfig: AsrClientConfig = {\n appId,\n accessKey,\n resourceId,\n useAsyncMode: true, // 使用双向流式优化版\n };\n \n const client = new AsrClient(clientConfig);\n session = { client, webContents };\n sessions.set(webContentsId, session);\n \n // 当 webContents 销毁时清理会话\n webContents.on('destroyed', () => {\n const s = sessions.get(webContentsId);\n if (s) {\n s.client.disconnect();\n sessions.delete(webContentsId);\n }\n });\n }\n \n return session;\n }\n\n // ============ IPC Handlers ============\n\n /**\n * 开始 ASR 会话\n */\n ipcMain.handle(`${channelPrefix}:asr:start`, async (\n event: IpcMainInvokeEvent,\n sessionConfig?: AsrSessionConfig\n ) => {\n const webContents = event.sender;\n const webContentsId = webContents.id;\n const session = getOrCreateSession(webContentsId, webContents);\n \n // 如果已连接,先断开\n if (session.client.connected) {\n session.client.disconnect();\n }\n \n try {\n await session.client.connect({\n onConnected: () => {\n if (!webContents.isDestroyed()) {\n webContents.send(`${channelPrefix}:asr:connected`);\n }\n },\n onResult: (result: AsrResult, isLast: boolean) => {\n if (!webContents.isDestroyed()) {\n webContents.send(`${channelPrefix}:asr:result`, { result, isLast });\n }\n },\n onError: (error: Error) => {\n if (!webContents.isDestroyed()) {\n webContents.send(`${channelPrefix}:asr:error`, { message: error.message });\n }\n },\n onClose: () => {\n if (!webContents.isDestroyed()) {\n webContents.send(`${channelPrefix}:asr:closed`);\n }\n },\n }, sessionConfig);\n \n return { success: true };\n } catch (error) {\n console.error('[ASR Bridge] 启动失败:', error);\n return { \n success: false, \n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n /**\n * 发送音频数据\n */\n ipcMain.handle(`${channelPrefix}:asr:sendAudio`, async (\n event: IpcMainInvokeEvent,\n audioData: ArrayBufferLike\n ) => {\n const webContentsId = event.sender.id;\n const session = sessions.get(webContentsId);\n \n if (!session || !session.client.connected) {\n return { success: false, error: 'ASR 会话未启动' };\n }\n \n try {\n const payload =\n audioData instanceof ArrayBuffer\n ? Buffer.from(audioData)\n : Buffer.from(new Uint8Array(audioData));\n\n session.client.sendAudio(payload);\n return { success: true };\n } catch (error) {\n console.error('[ASR Bridge] 发送音频失败:', error);\n return { \n success: false, \n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n /**\n * 结束 ASR 会话\n */\n ipcMain.handle(`${channelPrefix}:asr:finish`, async (event: IpcMainInvokeEvent) => {\n const webContentsId = event.sender.id;\n const session = sessions.get(webContentsId);\n \n if (!session || !session.client.connected) {\n return { success: false, error: 'ASR 会话未启动' };\n }\n \n try {\n session.client.finish();\n return { success: true };\n } catch (error) {\n console.error('[ASR Bridge] 结束会话失败:', error);\n return { \n success: false, \n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n /**\n * 停止 ASR 会话(强制断开)\n */\n ipcMain.handle(`${channelPrefix}:asr:stop`, async (event: IpcMainInvokeEvent) => {\n const webContentsId = event.sender.id;\n const session = sessions.get(webContentsId);\n \n if (session) {\n session.client.disconnect();\n }\n \n return { success: true };\n });\n\n /**\n * 检查 ASR 会话状态\n */\n ipcMain.handle(`${channelPrefix}:asr:status`, async (event: IpcMainInvokeEvent) => {\n const webContentsId = event.sender.id;\n const session = sessions.get(webContentsId);\n \n return {\n connected: session?.client.connected ?? false,\n };\n });\n\n /**\n * 预热 ASR 连接(connect -> disconnect)\n * 用于减少首次语音识别启动时的冷启动耗时:\n * - DNS/TLS/WS 首次握手\n * - 模块加载/初始化\n *\n * 注意:不会发送 connected/result 事件给渲染进程,避免影响 UI。\n */\n ipcMain.handle(`${channelPrefix}:asr:warmup`, async (\n event: IpcMainInvokeEvent,\n sessionConfig?: AsrSessionConfig\n ) => {\n const webContents = event.sender;\n const webContentsId = webContents.id;\n const session = getOrCreateSession(webContentsId, webContents);\n\n if (session.client.connected) return { success: true };\n\n try {\n await session.client.connect(\n {\n onConnected: () => {},\n onResult: () => {},\n onError: () => {},\n onClose: () => {},\n },\n sessionConfig\n );\n session.client.disconnect();\n return { success: true };\n } catch (error) {\n // 预热失败不影响正常使用(下次 start 仍会重试)\n console.warn('[ASR Bridge] warmup 失败:', error);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n });\n\n console.log('[ASR Bridge] IPC handlers 已注册');\n \n return {\n /** 清理所有会话 */\n cleanup: () => {\n for (const session of sessions.values()) {\n session.client.disconnect();\n }\n sessions.clear();\n },\n };\n}\n\n"],"mappings":"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","names":["ipcMain","resolve","ipcMain","resolve","options"]}
|