@huyooo/ai-chat-bridge-electron 0.2.13 → 0.2.15
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/index.d.ts +2 -0
- package/dist/main/index.js +1 -823
- package/dist/preload/index.d.ts +25 -7
- package/dist/preload/index.js +1 -136
- package/dist/renderer/index.d.cts +1 -1
- package/dist/renderer/index.d.ts +54 -15
- package/dist/renderer/index.js +1 -253
- package/package.json +4 -4
- package/src/main/index.ts +33 -0
- package/src/preload/index.ts +25 -7
- package/src/renderer/index.ts +39 -7
package/dist/preload/index.d.ts
CHANGED
|
@@ -21,12 +21,14 @@ interface SendMessageParams {
|
|
|
21
21
|
interface ModelOption {
|
|
22
22
|
modelId: string;
|
|
23
23
|
displayName: string;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
supportsThinking: boolean;
|
|
25
|
+
supportsVision: boolean;
|
|
26
|
+
tooltip?: {
|
|
27
|
+
features?: string[];
|
|
28
|
+
/** 开销信息(数组,分行显示) */
|
|
29
|
+
cost?: string[];
|
|
30
|
+
description?: string;
|
|
31
|
+
};
|
|
30
32
|
}
|
|
31
33
|
/** 会话记录 */
|
|
32
34
|
interface SessionRecord {
|
|
@@ -49,6 +51,8 @@ interface MessageRecord {
|
|
|
49
51
|
sessionId: string;
|
|
50
52
|
role: 'user' | 'assistant';
|
|
51
53
|
content: string;
|
|
54
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
55
|
+
images: string[];
|
|
52
56
|
/** 生成此消息时使用的模型 */
|
|
53
57
|
model?: string | null;
|
|
54
58
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -60,7 +64,10 @@ interface MessageRecord {
|
|
|
60
64
|
/** 执行步骤列表 JSON */
|
|
61
65
|
steps?: string | null;
|
|
62
66
|
operationIds?: string | null;
|
|
63
|
-
|
|
67
|
+
/** 消息序号(会话内递增,用于分叉删除,必须) */
|
|
68
|
+
sequence: number;
|
|
69
|
+
/** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
|
|
70
|
+
timestamp: number;
|
|
64
71
|
}
|
|
65
72
|
/** 操作记录 */
|
|
66
73
|
interface OperationRecord {
|
|
@@ -124,6 +131,11 @@ interface AsrResultData {
|
|
|
124
131
|
interface AiChatBridge {
|
|
125
132
|
/** 获取可用模型 */
|
|
126
133
|
getModels(): Promise<ModelOption[]>;
|
|
134
|
+
/** 获取所有工具列表(用于设置面板) */
|
|
135
|
+
getAllTools(): Promise<Array<{
|
|
136
|
+
name: string;
|
|
137
|
+
description: string;
|
|
138
|
+
}>>;
|
|
127
139
|
/** 发送消息 */
|
|
128
140
|
send(params: SendMessageParams): Promise<void>;
|
|
129
141
|
/** 取消当前请求 */
|
|
@@ -169,6 +181,8 @@ interface AiChatBridge {
|
|
|
169
181
|
sessionId: string;
|
|
170
182
|
role: 'user' | 'assistant';
|
|
171
183
|
content: string;
|
|
184
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
185
|
+
images?: string[];
|
|
172
186
|
/** 生成此消息时使用的模型 */
|
|
173
187
|
model?: string;
|
|
174
188
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -193,6 +207,10 @@ interface AiChatBridge {
|
|
|
193
207
|
deleteMessagesAfter(sessionId: string, timestamp: number): Promise<{
|
|
194
208
|
success: boolean;
|
|
195
209
|
}>;
|
|
210
|
+
/** 删除指定消息之后的所有消息(用于分叉) */
|
|
211
|
+
deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<{
|
|
212
|
+
success: boolean;
|
|
213
|
+
}>;
|
|
196
214
|
/** 获取操作日志 */
|
|
197
215
|
getOperations(sessionId: string): Promise<OperationRecord[]>;
|
|
198
216
|
/** 获取回收站 */
|
package/dist/preload/index.js
CHANGED
|
@@ -1,136 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { contextBridge, ipcRenderer } from "electron";
|
|
3
|
-
function exposeElectronBridge(options = {}) {
|
|
4
|
-
const { channelPrefix = "ai-chat", exposeName = "aiChatBridge" } = options;
|
|
5
|
-
const bridge = {
|
|
6
|
-
// ============ Chat API ============
|
|
7
|
-
getModels: () => ipcRenderer.invoke(`${channelPrefix}:models`),
|
|
8
|
-
send: (params) => ipcRenderer.invoke(`${channelPrefix}:send`, params),
|
|
9
|
-
cancel: () => ipcRenderer.invoke(`${channelPrefix}:cancel`),
|
|
10
|
-
// 无状态架构:历史通过 ChatOptions.history 传入
|
|
11
|
-
setCwd: (dir) => ipcRenderer.invoke(`${channelPrefix}:setCwd`, dir),
|
|
12
|
-
getConfig: () => ipcRenderer.invoke(`${channelPrefix}:config`),
|
|
13
|
-
onProgress: (callback) => {
|
|
14
|
-
const handler = (_event, progress) => {
|
|
15
|
-
callback(progress);
|
|
16
|
-
};
|
|
17
|
-
ipcRenderer.on(`${channelPrefix}:progress`, handler);
|
|
18
|
-
return () => {
|
|
19
|
-
ipcRenderer.removeListener(`${channelPrefix}:progress`, handler);
|
|
20
|
-
};
|
|
21
|
-
},
|
|
22
|
-
onToolApprovalRequest: (callback) => {
|
|
23
|
-
const handler = (_event, request) => {
|
|
24
|
-
callback(request);
|
|
25
|
-
};
|
|
26
|
-
ipcRenderer.on(`${channelPrefix}:toolApprovalRequest`, handler);
|
|
27
|
-
return () => {
|
|
28
|
-
ipcRenderer.removeListener(`${channelPrefix}:toolApprovalRequest`, handler);
|
|
29
|
-
};
|
|
30
|
-
},
|
|
31
|
-
respondToolApproval: (id, approved) => ipcRenderer.invoke(`${channelPrefix}:toolApprovalResponse`, { id, approved }),
|
|
32
|
-
// ============ Sessions API ============
|
|
33
|
-
getSessions: () => ipcRenderer.invoke(`${channelPrefix}:sessions:list`),
|
|
34
|
-
getSession: (id) => ipcRenderer.invoke(`${channelPrefix}:sessions:get`, id),
|
|
35
|
-
createSession: (params) => ipcRenderer.invoke(`${channelPrefix}:sessions:create`, params || {}),
|
|
36
|
-
updateSession: (id, data) => ipcRenderer.invoke(`${channelPrefix}:sessions:update`, id, data),
|
|
37
|
-
deleteSession: (id) => ipcRenderer.invoke(`${channelPrefix}:sessions:delete`, id),
|
|
38
|
-
// ============ Messages API ============
|
|
39
|
-
getMessages: (sessionId) => ipcRenderer.invoke(`${channelPrefix}:messages:list`, sessionId),
|
|
40
|
-
saveMessage: (params) => ipcRenderer.invoke(`${channelPrefix}:messages:save`, params),
|
|
41
|
-
updateMessage: (params) => ipcRenderer.invoke(`${channelPrefix}:messages:update`, params),
|
|
42
|
-
deleteMessagesAfter: (sessionId, timestamp) => ipcRenderer.invoke(`${channelPrefix}:messages:deleteAfter`, sessionId, timestamp),
|
|
43
|
-
// ============ Operations API ============
|
|
44
|
-
getOperations: (sessionId) => ipcRenderer.invoke(`${channelPrefix}:operations:list`, sessionId),
|
|
45
|
-
// ============ Trash API ============
|
|
46
|
-
getTrashItems: () => ipcRenderer.invoke(`${channelPrefix}:trash:list`),
|
|
47
|
-
restoreFromTrash: (id) => ipcRenderer.invoke(`${channelPrefix}:trash:restore`, id),
|
|
48
|
-
// ============ System API ============
|
|
49
|
-
openExternal: (url) => ipcRenderer.invoke(`${channelPrefix}:openExternal`, url),
|
|
50
|
-
// ============ File System API ============
|
|
51
|
-
listDir: (dirPath) => ipcRenderer.invoke(`${channelPrefix}:fs:listDir`, dirPath),
|
|
52
|
-
exists: (filePath) => ipcRenderer.invoke(`${channelPrefix}:fs:exists`, filePath),
|
|
53
|
-
stat: (filePath) => ipcRenderer.invoke(`${channelPrefix}:fs:stat`, filePath),
|
|
54
|
-
readFile: (filePath) => ipcRenderer.invoke(`${channelPrefix}:fs:readFile`, filePath),
|
|
55
|
-
readFileBase64: (filePath) => ipcRenderer.invoke(`${channelPrefix}:fs:readFileBase64`, filePath),
|
|
56
|
-
homeDir: () => ipcRenderer.invoke(`${channelPrefix}:fs:homeDir`),
|
|
57
|
-
resolvePath: (inputPath) => ipcRenderer.invoke(`${channelPrefix}:fs:resolvePath`, inputPath),
|
|
58
|
-
parentDir: (dirPath) => ipcRenderer.invoke(`${channelPrefix}:fs:parentDir`, dirPath),
|
|
59
|
-
watchDir: (dirPath) => ipcRenderer.invoke(`${channelPrefix}:fs:watchDir`, dirPath),
|
|
60
|
-
unwatchDir: (dirPath) => ipcRenderer.invoke(`${channelPrefix}:fs:unwatchDir`, dirPath),
|
|
61
|
-
onDirChange: (callback) => {
|
|
62
|
-
const handler = (_event, data) => {
|
|
63
|
-
callback(data);
|
|
64
|
-
};
|
|
65
|
-
ipcRenderer.on(`${channelPrefix}:fs:dirChange`, handler);
|
|
66
|
-
return () => {
|
|
67
|
-
ipcRenderer.removeListener(`${channelPrefix}:fs:dirChange`, handler);
|
|
68
|
-
};
|
|
69
|
-
},
|
|
70
|
-
// ============ Settings API ============
|
|
71
|
-
getSetting: (key) => ipcRenderer.invoke(`${channelPrefix}:settings:get`, key),
|
|
72
|
-
setSetting: (key, value) => ipcRenderer.invoke(`${channelPrefix}:settings:set`, key, value),
|
|
73
|
-
getAllSettings: () => ipcRenderer.invoke(`${channelPrefix}:settings:getAll`),
|
|
74
|
-
deleteSetting: (key) => ipcRenderer.invoke(`${channelPrefix}:settings:delete`, key),
|
|
75
|
-
// ============ Index API ============
|
|
76
|
-
getIndexStats: () => ipcRenderer.invoke(`${channelPrefix}:index:getStats`),
|
|
77
|
-
getIndexStatus: () => ipcRenderer.invoke(`${channelPrefix}:index:status`),
|
|
78
|
-
syncIndex: () => ipcRenderer.invoke(`${channelPrefix}:index:sync`),
|
|
79
|
-
cancelIndex: () => ipcRenderer.invoke(`${channelPrefix}:index:cancel`),
|
|
80
|
-
deleteIndex: () => ipcRenderer.invoke(`${channelPrefix}:index:delete`),
|
|
81
|
-
registerIndexListener: () => ipcRenderer.invoke(`${channelPrefix}:index:registerListener`),
|
|
82
|
-
unregisterIndexListener: () => ipcRenderer.invoke(`${channelPrefix}:index:unregisterListener`),
|
|
83
|
-
onIndexProgress: (callback) => {
|
|
84
|
-
const handler = (_event, progress) => {
|
|
85
|
-
callback(progress);
|
|
86
|
-
};
|
|
87
|
-
ipcRenderer.on(`${channelPrefix}:index:progress`, handler);
|
|
88
|
-
return () => {
|
|
89
|
-
ipcRenderer.removeListener(`${channelPrefix}:index:progress`, handler);
|
|
90
|
-
};
|
|
91
|
-
},
|
|
92
|
-
// ============ ASR API ============
|
|
93
|
-
asrStart: (config) => ipcRenderer.invoke(`${channelPrefix}:asr:start`, config),
|
|
94
|
-
asrSendAudio: (audioData) => ipcRenderer.invoke(`${channelPrefix}:asr:sendAudio`, audioData),
|
|
95
|
-
asrFinish: () => ipcRenderer.invoke(`${channelPrefix}:asr:finish`),
|
|
96
|
-
asrStop: () => ipcRenderer.invoke(`${channelPrefix}:asr:stop`),
|
|
97
|
-
asrStatus: () => ipcRenderer.invoke(`${channelPrefix}:asr:status`),
|
|
98
|
-
asrWarmup: (config) => ipcRenderer.invoke(`${channelPrefix}:asr:warmup`, config),
|
|
99
|
-
onAsrConnected: (callback) => {
|
|
100
|
-
const handler = () => callback();
|
|
101
|
-
ipcRenderer.on(`${channelPrefix}:asr:connected`, handler);
|
|
102
|
-
return () => {
|
|
103
|
-
ipcRenderer.removeListener(`${channelPrefix}:asr:connected`, handler);
|
|
104
|
-
};
|
|
105
|
-
},
|
|
106
|
-
onAsrResult: (callback) => {
|
|
107
|
-
const handler = (_event, data) => {
|
|
108
|
-
callback(data);
|
|
109
|
-
};
|
|
110
|
-
ipcRenderer.on(`${channelPrefix}:asr:result`, handler);
|
|
111
|
-
return () => {
|
|
112
|
-
ipcRenderer.removeListener(`${channelPrefix}:asr:result`, handler);
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
onAsrError: (callback) => {
|
|
116
|
-
const handler = (_event, error) => {
|
|
117
|
-
callback(error);
|
|
118
|
-
};
|
|
119
|
-
ipcRenderer.on(`${channelPrefix}:asr:error`, handler);
|
|
120
|
-
return () => {
|
|
121
|
-
ipcRenderer.removeListener(`${channelPrefix}:asr:error`, handler);
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
onAsrClosed: (callback) => {
|
|
125
|
-
const handler = () => callback();
|
|
126
|
-
ipcRenderer.on(`${channelPrefix}:asr:closed`, handler);
|
|
127
|
-
return () => {
|
|
128
|
-
ipcRenderer.removeListener(`${channelPrefix}:asr:closed`, handler);
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
contextBridge.exposeInMainWorld(exposeName, bridge);
|
|
133
|
-
}
|
|
134
|
-
export {
|
|
135
|
-
exposeElectronBridge
|
|
136
|
-
};
|
|
1
|
+
import{contextBridge as e,ipcRenderer as s}from"electron";function n(n={}){const{channelPrefix:o="ai-chat",exposeName:t="aiChatBridge"}=n,r={getModels:()=>s.invoke(`${o}:models`),getAllTools:()=>s.invoke(`${o}:getAllTools`),send:e=>s.invoke(`${o}:send`,e),cancel:()=>s.invoke(`${o}:cancel`),setCwd:e=>s.invoke(`${o}:setCwd`,e),getConfig:()=>s.invoke(`${o}:config`),onProgress:e=>{const n=(s,n)=>{e(n)};return s.on(`${o}:progress`,n),()=>{s.removeListener(`${o}:progress`,n)}},onToolApprovalRequest:e=>{const n=(s,n)=>{e(n)};return s.on(`${o}:toolApprovalRequest`,n),()=>{s.removeListener(`${o}:toolApprovalRequest`,n)}},respondToolApproval:(e,n)=>s.invoke(`${o}:toolApprovalResponse`,{id:e,approved:n}),getSessions:()=>s.invoke(`${o}:sessions:list`),getSession:e=>s.invoke(`${o}:sessions:get`,e),createSession:e=>s.invoke(`${o}:sessions:create`,e||{}),updateSession:(e,n)=>s.invoke(`${o}:sessions:update`,e,n),deleteSession:e=>s.invoke(`${o}:sessions:delete`,e),getMessages:e=>s.invoke(`${o}:messages:list`,e),saveMessage:e=>s.invoke(`${o}:messages:save`,e),updateMessage:e=>s.invoke(`${o}:messages:update`,e),deleteMessagesAfter:(e,n)=>s.invoke(`${o}:messages:deleteAfter`,e,n),deleteMessagesAfterMessageId:(e,n)=>s.invoke(`${o}:messages:deleteAfterMessageId`,e,n),getOperations:e=>s.invoke(`${o}:operations:list`,e),getTrashItems:()=>s.invoke(`${o}:trash:list`),restoreFromTrash:e=>s.invoke(`${o}:trash:restore`,e),openExternal:e=>s.invoke(`${o}:openExternal`,e),listDir:e=>s.invoke(`${o}:fs:listDir`,e),exists:e=>s.invoke(`${o}:fs:exists`,e),stat:e=>s.invoke(`${o}:fs:stat`,e),readFile:e=>s.invoke(`${o}:fs:readFile`,e),readFileBase64:e=>s.invoke(`${o}:fs:readFileBase64`,e),homeDir:()=>s.invoke(`${o}:fs:homeDir`),resolvePath:e=>s.invoke(`${o}:fs:resolvePath`,e),parentDir:e=>s.invoke(`${o}:fs:parentDir`,e),watchDir:e=>s.invoke(`${o}:fs:watchDir`,e),unwatchDir:e=>s.invoke(`${o}:fs:unwatchDir`,e),onDirChange:e=>{const n=(s,n)=>{e(n)};return s.on(`${o}:fs:dirChange`,n),()=>{s.removeListener(`${o}:fs:dirChange`,n)}},getSetting:e=>s.invoke(`${o}:settings:get`,e),setSetting:(e,n)=>s.invoke(`${o}:settings:set`,e,n),getAllSettings:()=>s.invoke(`${o}:settings:getAll`),deleteSetting:e=>s.invoke(`${o}:settings:delete`,e),getIndexStats:()=>s.invoke(`${o}:index:getStats`),getIndexStatus:()=>s.invoke(`${o}:index:status`),syncIndex:()=>s.invoke(`${o}:index:sync`),cancelIndex:()=>s.invoke(`${o}:index:cancel`),deleteIndex:()=>s.invoke(`${o}:index:delete`),registerIndexListener:()=>s.invoke(`${o}:index:registerListener`),unregisterIndexListener:()=>s.invoke(`${o}:index:unregisterListener`),onIndexProgress:e=>{const n=(s,n)=>{e(n)};return s.on(`${o}:index:progress`,n),()=>{s.removeListener(`${o}:index:progress`,n)}},asrStart:e=>s.invoke(`${o}:asr:start`,e),asrSendAudio:e=>s.invoke(`${o}:asr:sendAudio`,e),asrFinish:()=>s.invoke(`${o}:asr:finish`),asrStop:()=>s.invoke(`${o}:asr:stop`),asrStatus:()=>s.invoke(`${o}:asr:status`),asrWarmup:e=>s.invoke(`${o}:asr:warmup`,e),onAsrConnected:e=>{const n=()=>e();return s.on(`${o}:asr:connected`,n),()=>{s.removeListener(`${o}:asr:connected`,n)}},onAsrResult:e=>{const n=(s,n)=>{e(n)};return s.on(`${o}:asr:result`,n),()=>{s.removeListener(`${o}:asr:result`,n)}},onAsrError:e=>{const n=(s,n)=>{e(n)};return s.on(`${o}:asr:error`,n),()=>{s.removeListener(`${o}:asr:error`,n)}},onAsrClosed:e=>{const n=()=>e();return s.on(`${o}:asr:closed`,n),()=>{s.removeListener(`${o}:asr:closed`,n)}}};e.exposeInMainWorld(t,r)}export{n as exposeElectronBridge};
|
|
@@ -269,7 +269,7 @@ interface AiChatBridge {
|
|
|
269
269
|
* 在渲染进程中使用,创建 ChatAdapter
|
|
270
270
|
*/
|
|
271
271
|
|
|
272
|
-
type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
|
|
272
|
+
type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_output' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
|
|
273
273
|
interface ChatEvent {
|
|
274
274
|
type: ChatEventType;
|
|
275
275
|
data: unknown;
|
package/dist/renderer/index.d.ts
CHANGED
|
@@ -21,12 +21,14 @@ interface SendMessageParams {
|
|
|
21
21
|
interface ModelOption$1 {
|
|
22
22
|
modelId: string;
|
|
23
23
|
displayName: string;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
supportsThinking: boolean;
|
|
25
|
+
supportsVision: boolean;
|
|
26
|
+
tooltip?: {
|
|
27
|
+
features?: string[];
|
|
28
|
+
/** 开销信息(数组,分行显示) */
|
|
29
|
+
cost?: string[];
|
|
30
|
+
description?: string;
|
|
31
|
+
};
|
|
30
32
|
}
|
|
31
33
|
/** 会话记录 */
|
|
32
34
|
interface SessionRecord$1 {
|
|
@@ -49,6 +51,8 @@ interface MessageRecord$1 {
|
|
|
49
51
|
sessionId: string;
|
|
50
52
|
role: 'user' | 'assistant';
|
|
51
53
|
content: string;
|
|
54
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
55
|
+
images: string[];
|
|
52
56
|
/** 生成此消息时使用的模型 */
|
|
53
57
|
model?: string | null;
|
|
54
58
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -60,7 +64,10 @@ interface MessageRecord$1 {
|
|
|
60
64
|
/** 执行步骤列表 JSON */
|
|
61
65
|
steps?: string | null;
|
|
62
66
|
operationIds?: string | null;
|
|
63
|
-
|
|
67
|
+
/** 消息序号(会话内递增,用于分叉删除,必须) */
|
|
68
|
+
sequence: number;
|
|
69
|
+
/** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
|
|
70
|
+
timestamp: number;
|
|
64
71
|
}
|
|
65
72
|
/** 操作记录 */
|
|
66
73
|
interface OperationRecord$1 {
|
|
@@ -124,6 +131,11 @@ interface AsrResultData$1 {
|
|
|
124
131
|
interface AiChatBridge {
|
|
125
132
|
/** 获取可用模型 */
|
|
126
133
|
getModels(): Promise<ModelOption$1[]>;
|
|
134
|
+
/** 获取所有工具列表(用于设置面板) */
|
|
135
|
+
getAllTools(): Promise<Array<{
|
|
136
|
+
name: string;
|
|
137
|
+
description: string;
|
|
138
|
+
}>>;
|
|
127
139
|
/** 发送消息 */
|
|
128
140
|
send(params: SendMessageParams): Promise<void>;
|
|
129
141
|
/** 取消当前请求 */
|
|
@@ -169,6 +181,8 @@ interface AiChatBridge {
|
|
|
169
181
|
sessionId: string;
|
|
170
182
|
role: 'user' | 'assistant';
|
|
171
183
|
content: string;
|
|
184
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
185
|
+
images?: string[];
|
|
172
186
|
/** 生成此消息时使用的模型 */
|
|
173
187
|
model?: string;
|
|
174
188
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -193,6 +207,10 @@ interface AiChatBridge {
|
|
|
193
207
|
deleteMessagesAfter(sessionId: string, timestamp: number): Promise<{
|
|
194
208
|
success: boolean;
|
|
195
209
|
}>;
|
|
210
|
+
/** 删除指定消息之后的所有消息(用于分叉) */
|
|
211
|
+
deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<{
|
|
212
|
+
success: boolean;
|
|
213
|
+
}>;
|
|
196
214
|
/** 获取操作日志 */
|
|
197
215
|
getOperations(sessionId: string): Promise<OperationRecord$1[]>;
|
|
198
216
|
/** 获取回收站 */
|
|
@@ -340,7 +358,7 @@ interface AiChatBridge {
|
|
|
340
358
|
* 在渲染进程中使用,创建 ChatAdapter
|
|
341
359
|
*/
|
|
342
360
|
|
|
343
|
-
type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
|
|
361
|
+
type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_output' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
|
|
344
362
|
interface ChatEvent {
|
|
345
363
|
type: ChatEventType;
|
|
346
364
|
data: unknown;
|
|
@@ -369,6 +387,8 @@ interface ChatOptions {
|
|
|
369
387
|
enableWebSearch?: boolean;
|
|
370
388
|
/** 深度思考开关(每个 provider 内部使用最优参数) */
|
|
371
389
|
thinkingMode?: ThinkingMode;
|
|
390
|
+
/** 启用的工具名称列表(undefined 表示全部启用) */
|
|
391
|
+
enabledTools?: string[];
|
|
372
392
|
/** 自动运行配置 */
|
|
373
393
|
autoRunConfig?: AutoRunConfig;
|
|
374
394
|
/** 对话历史(无状态架构,历史从前端传入) */
|
|
@@ -382,12 +402,17 @@ interface ModelOption {
|
|
|
382
402
|
modelId: string;
|
|
383
403
|
/** 显示名称 */
|
|
384
404
|
displayName: string;
|
|
385
|
-
/**
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
|
|
405
|
+
/** 是否支持深度思考 */
|
|
406
|
+
supportsThinking: boolean;
|
|
407
|
+
/** 是否支持图片理解 */
|
|
408
|
+
supportsVision: boolean;
|
|
409
|
+
/** 模型项 hover 提示(由后端定义,前端只渲染) */
|
|
410
|
+
tooltip?: {
|
|
411
|
+
features?: string[];
|
|
412
|
+
/** 开销信息(数组,分行显示) */
|
|
413
|
+
cost?: string[];
|
|
414
|
+
description?: string;
|
|
415
|
+
};
|
|
391
416
|
}
|
|
392
417
|
interface SessionRecord {
|
|
393
418
|
id: string;
|
|
@@ -408,6 +433,8 @@ interface MessageRecord {
|
|
|
408
433
|
sessionId: string;
|
|
409
434
|
role: 'user' | 'assistant';
|
|
410
435
|
content: string;
|
|
436
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
437
|
+
images: string[];
|
|
411
438
|
/** 生成此消息时使用的模型 */
|
|
412
439
|
model?: string | null;
|
|
413
440
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -419,7 +446,10 @@ interface MessageRecord {
|
|
|
419
446
|
/** 执行步骤列表 JSON */
|
|
420
447
|
steps?: string | null;
|
|
421
448
|
operationIds?: string | null;
|
|
422
|
-
|
|
449
|
+
/** 消息序号(会话内递增,用于分叉删除,必须) */
|
|
450
|
+
sequence: number;
|
|
451
|
+
/** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
|
|
452
|
+
timestamp: number;
|
|
423
453
|
}
|
|
424
454
|
interface OperationRecord {
|
|
425
455
|
id: string;
|
|
@@ -478,6 +508,11 @@ interface AsrResultData {
|
|
|
478
508
|
interface ChatAdapter {
|
|
479
509
|
/** 获取可用模型 */
|
|
480
510
|
getModels(): Promise<ModelOption[]>;
|
|
511
|
+
/** 获取所有工具列表(用于设置面板) */
|
|
512
|
+
getAllTools?(): Promise<Array<{
|
|
513
|
+
name: string;
|
|
514
|
+
description: string;
|
|
515
|
+
}>>;
|
|
481
516
|
/** 发送消息,返回异步迭代器 */
|
|
482
517
|
sendMessage(message: string, options?: ChatOptions, images?: string[], sessionId?: string): AsyncIterable<ChatEvent>;
|
|
483
518
|
/** 取消当前请求 */
|
|
@@ -517,6 +552,8 @@ interface ChatAdapter {
|
|
|
517
552
|
sessionId: string;
|
|
518
553
|
role: 'user' | 'assistant';
|
|
519
554
|
content: string;
|
|
555
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
556
|
+
images?: string[];
|
|
520
557
|
/** 生成此消息时使用的模型 */
|
|
521
558
|
model?: string;
|
|
522
559
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -537,6 +574,8 @@ interface ChatAdapter {
|
|
|
537
574
|
}): Promise<void>;
|
|
538
575
|
/** 删除指定时间之后的消息(用于分叉) */
|
|
539
576
|
deleteMessagesAfter(sessionId: string, timestamp: number): Promise<void>;
|
|
577
|
+
/** 删除指定消息之后的所有消息(用于分叉) */
|
|
578
|
+
deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<void>;
|
|
540
579
|
/** 获取操作日志 */
|
|
541
580
|
getOperations(sessionId: string): Promise<OperationRecord[]>;
|
|
542
581
|
/** 获取回收站 */
|
package/dist/renderer/index.js
CHANGED
|
@@ -1,253 +1 @@
|
|
|
1
|
-
|
|
2
|
-
function createElectronAdapter(options = {}) {
|
|
3
|
-
const { bridgeName = "aiChatBridge" } = options;
|
|
4
|
-
const getBridge = () => {
|
|
5
|
-
const bridge = window[bridgeName];
|
|
6
|
-
if (!bridge) {
|
|
7
|
-
throw new Error(`AI Chat Bridge not found. Make sure to call exposeElectronBridge() in preload.`);
|
|
8
|
-
}
|
|
9
|
-
return bridge;
|
|
10
|
-
};
|
|
11
|
-
return {
|
|
12
|
-
// ============ Chat API ============
|
|
13
|
-
async getModels() {
|
|
14
|
-
const bridge = getBridge();
|
|
15
|
-
return bridge.getModels();
|
|
16
|
-
},
|
|
17
|
-
async *sendMessage(message, options2, images, sessionId) {
|
|
18
|
-
const bridge = getBridge();
|
|
19
|
-
const queue = [];
|
|
20
|
-
let resolveNext = null;
|
|
21
|
-
let done = false;
|
|
22
|
-
const cleanup = bridge.onProgress((progress) => {
|
|
23
|
-
const eventWithSession = progress;
|
|
24
|
-
if (sessionId && eventWithSession.sessionId && eventWithSession.sessionId !== sessionId) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
queue.push(progress);
|
|
28
|
-
resolveNext?.();
|
|
29
|
-
if (progress.type === "done" || progress.type === "error") {
|
|
30
|
-
done = true;
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
bridge.send({ message, images, options: options2, sessionId });
|
|
34
|
-
try {
|
|
35
|
-
while (!done || queue.length > 0) {
|
|
36
|
-
while (queue.length === 0 && !done) {
|
|
37
|
-
await new Promise((r) => resolveNext = r);
|
|
38
|
-
}
|
|
39
|
-
if (queue.length > 0) {
|
|
40
|
-
const progress = queue.shift();
|
|
41
|
-
yield progress;
|
|
42
|
-
if (progress.type === "done" || progress.type === "error") {
|
|
43
|
-
break;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
} finally {
|
|
48
|
-
cleanup();
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
cancel() {
|
|
52
|
-
getBridge().cancel();
|
|
53
|
-
},
|
|
54
|
-
// 无状态架构:历史通过 ChatOptions.history 传入
|
|
55
|
-
setCwd(dir) {
|
|
56
|
-
getBridge().setCwd(dir);
|
|
57
|
-
},
|
|
58
|
-
// ============ Sessions API ============
|
|
59
|
-
async getSessions() {
|
|
60
|
-
return getBridge().getSessions();
|
|
61
|
-
},
|
|
62
|
-
async getSession(id) {
|
|
63
|
-
return getBridge().getSession(id);
|
|
64
|
-
},
|
|
65
|
-
async createSession(params) {
|
|
66
|
-
return getBridge().createSession(params);
|
|
67
|
-
},
|
|
68
|
-
async updateSession(id, data) {
|
|
69
|
-
return getBridge().updateSession(id, data);
|
|
70
|
-
},
|
|
71
|
-
async deleteSession(id) {
|
|
72
|
-
await getBridge().deleteSession(id);
|
|
73
|
-
},
|
|
74
|
-
// ============ Messages API ============
|
|
75
|
-
async getMessages(sessionId) {
|
|
76
|
-
return getBridge().getMessages(sessionId);
|
|
77
|
-
},
|
|
78
|
-
async saveMessage(params) {
|
|
79
|
-
return getBridge().saveMessage(params);
|
|
80
|
-
},
|
|
81
|
-
async updateMessage(params) {
|
|
82
|
-
await getBridge().updateMessage(params);
|
|
83
|
-
},
|
|
84
|
-
async deleteMessagesAfter(sessionId, timestamp) {
|
|
85
|
-
await getBridge().deleteMessagesAfter(sessionId, timestamp);
|
|
86
|
-
},
|
|
87
|
-
// ============ Operations API ============
|
|
88
|
-
async getOperations(sessionId) {
|
|
89
|
-
return getBridge().getOperations(sessionId);
|
|
90
|
-
},
|
|
91
|
-
// ============ Trash API ============
|
|
92
|
-
async getTrashItems() {
|
|
93
|
-
return getBridge().getTrashItems();
|
|
94
|
-
},
|
|
95
|
-
async restoreFromTrash(id) {
|
|
96
|
-
return getBridge().restoreFromTrash(id);
|
|
97
|
-
},
|
|
98
|
-
// ============ File System API ============
|
|
99
|
-
async listDir(dirPath) {
|
|
100
|
-
return getBridge().listDir(dirPath);
|
|
101
|
-
},
|
|
102
|
-
async exists(filePath) {
|
|
103
|
-
return getBridge().exists(filePath);
|
|
104
|
-
},
|
|
105
|
-
async stat(filePath) {
|
|
106
|
-
return getBridge().stat(filePath);
|
|
107
|
-
},
|
|
108
|
-
async readFile(filePath) {
|
|
109
|
-
return getBridge().readFile(filePath);
|
|
110
|
-
},
|
|
111
|
-
async readFileBase64(filePath) {
|
|
112
|
-
return getBridge().readFileBase64(filePath);
|
|
113
|
-
},
|
|
114
|
-
async homeDir() {
|
|
115
|
-
return getBridge().homeDir();
|
|
116
|
-
},
|
|
117
|
-
async resolvePath(inputPath) {
|
|
118
|
-
return getBridge().resolvePath(inputPath);
|
|
119
|
-
},
|
|
120
|
-
async parentDir(dirPath) {
|
|
121
|
-
return getBridge().parentDir(dirPath);
|
|
122
|
-
},
|
|
123
|
-
async watchDir(dirPath) {
|
|
124
|
-
return getBridge().watchDir(dirPath);
|
|
125
|
-
},
|
|
126
|
-
async unwatchDir(dirPath) {
|
|
127
|
-
return getBridge().unwatchDir(dirPath);
|
|
128
|
-
},
|
|
129
|
-
onDirChange(callback) {
|
|
130
|
-
return getBridge().onDirChange(callback);
|
|
131
|
-
},
|
|
132
|
-
// ============ Settings API ============
|
|
133
|
-
async getSetting(key) {
|
|
134
|
-
const bridge = getBridge();
|
|
135
|
-
if (bridge.getSetting) {
|
|
136
|
-
return bridge.getSetting(key);
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
},
|
|
140
|
-
async setSetting(key, value) {
|
|
141
|
-
const bridge = getBridge();
|
|
142
|
-
if (bridge.setSetting) {
|
|
143
|
-
await bridge.setSetting(key, value);
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
async getAllSettings() {
|
|
147
|
-
const bridge = getBridge();
|
|
148
|
-
if (bridge.getAllSettings) {
|
|
149
|
-
return bridge.getAllSettings();
|
|
150
|
-
}
|
|
151
|
-
return {};
|
|
152
|
-
},
|
|
153
|
-
async deleteSetting(key) {
|
|
154
|
-
const bridge = getBridge();
|
|
155
|
-
if (bridge.deleteSetting) {
|
|
156
|
-
await bridge.deleteSetting(key);
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
// ============ Tool Approval API ============
|
|
160
|
-
onToolApprovalRequest(callback) {
|
|
161
|
-
const bridge = getBridge();
|
|
162
|
-
if (bridge.onToolApprovalRequest) {
|
|
163
|
-
return bridge.onToolApprovalRequest(callback);
|
|
164
|
-
}
|
|
165
|
-
return () => {
|
|
166
|
-
};
|
|
167
|
-
},
|
|
168
|
-
async respondToolApproval(id, approved) {
|
|
169
|
-
const bridge = getBridge();
|
|
170
|
-
if (bridge.respondToolApproval) {
|
|
171
|
-
return bridge.respondToolApproval(id, approved);
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
// ============ ASR API ============
|
|
175
|
-
async asrStart(config) {
|
|
176
|
-
const bridge = getBridge();
|
|
177
|
-
if (bridge.asrStart) {
|
|
178
|
-
return bridge.asrStart(config);
|
|
179
|
-
}
|
|
180
|
-
return { success: false, error: "ASR not supported" };
|
|
181
|
-
},
|
|
182
|
-
async asrSendAudio(audioData) {
|
|
183
|
-
const bridge = getBridge();
|
|
184
|
-
if (bridge.asrSendAudio) {
|
|
185
|
-
return bridge.asrSendAudio(audioData);
|
|
186
|
-
}
|
|
187
|
-
return { success: false, error: "ASR not supported" };
|
|
188
|
-
},
|
|
189
|
-
async asrFinish() {
|
|
190
|
-
const bridge = getBridge();
|
|
191
|
-
if (bridge.asrFinish) {
|
|
192
|
-
return bridge.asrFinish();
|
|
193
|
-
}
|
|
194
|
-
return { success: false, error: "ASR not supported" };
|
|
195
|
-
},
|
|
196
|
-
async asrStop() {
|
|
197
|
-
const bridge = getBridge();
|
|
198
|
-
if (bridge.asrStop) {
|
|
199
|
-
return bridge.asrStop();
|
|
200
|
-
}
|
|
201
|
-
return { success: true };
|
|
202
|
-
},
|
|
203
|
-
async asrStatus() {
|
|
204
|
-
const bridge = getBridge();
|
|
205
|
-
if (bridge.asrStatus) {
|
|
206
|
-
return bridge.asrStatus();
|
|
207
|
-
}
|
|
208
|
-
return { connected: false };
|
|
209
|
-
},
|
|
210
|
-
async asrWarmup(config) {
|
|
211
|
-
const bridge = getBridge();
|
|
212
|
-
if (bridge.asrWarmup) {
|
|
213
|
-
return bridge.asrWarmup(config);
|
|
214
|
-
}
|
|
215
|
-
return { success: false, error: "ASR not supported" };
|
|
216
|
-
},
|
|
217
|
-
onAsrConnected(callback) {
|
|
218
|
-
const bridge = getBridge();
|
|
219
|
-
if (bridge.onAsrConnected) {
|
|
220
|
-
return bridge.onAsrConnected(callback);
|
|
221
|
-
}
|
|
222
|
-
return () => {
|
|
223
|
-
};
|
|
224
|
-
},
|
|
225
|
-
onAsrResult(callback) {
|
|
226
|
-
const bridge = getBridge();
|
|
227
|
-
if (bridge.onAsrResult) {
|
|
228
|
-
return bridge.onAsrResult(callback);
|
|
229
|
-
}
|
|
230
|
-
return () => {
|
|
231
|
-
};
|
|
232
|
-
},
|
|
233
|
-
onAsrError(callback) {
|
|
234
|
-
const bridge = getBridge();
|
|
235
|
-
if (bridge.onAsrError) {
|
|
236
|
-
return bridge.onAsrError(callback);
|
|
237
|
-
}
|
|
238
|
-
return () => {
|
|
239
|
-
};
|
|
240
|
-
},
|
|
241
|
-
onAsrClosed(callback) {
|
|
242
|
-
const bridge = getBridge();
|
|
243
|
-
if (bridge.onAsrClosed) {
|
|
244
|
-
return bridge.onAsrClosed(callback);
|
|
245
|
-
}
|
|
246
|
-
return () => {
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
export {
|
|
252
|
-
createElectronAdapter
|
|
253
|
-
};
|
|
1
|
+
function e(e={}){const{bridgeName:s="aiChatBridge"}=e,t=()=>{const e=window[s];if(!e)throw new Error("AI Chat Bridge not found. Make sure to call exposeElectronBridge() in preload.");return e};return{getModels:async()=>t().getModels(),async getAllTools(){const e=t();return"function"==typeof e.getAllTools?e.getAllTools():[]},async*sendMessage(e,s,n,r){const o=t(),a=[];let c=null,i=!1;const l=o.onProgress(e=>{const s=e;r&&s.sessionId&&s.sessionId!==r||(a.push(e),c?.(),"done"!==e.type&&"error"!==e.type||(i=!0))});o.send({message:e,images:n,options:s,sessionId:r});try{for(;!i||a.length>0;){for(;0===a.length&&!i;)await new Promise(e=>c=e);if(a.length>0){const e=a.shift();if(yield e,"done"===e.type||"error"===e.type)break}}}finally{l()}},cancel(){t().cancel()},setCwd(e){t().setCwd(e)},getSessions:async()=>t().getSessions(),getSession:async e=>t().getSession(e),createSession:async e=>t().createSession(e),updateSession:async(e,s)=>t().updateSession(e,s),async deleteSession(e){await t().deleteSession(e)},getMessages:async e=>t().getMessages(e),saveMessage:async e=>t().saveMessage(e),async updateMessage(e){await t().updateMessage(e)},async deleteMessagesAfter(e,s){await t().deleteMessagesAfter(e,s)},async deleteMessagesAfterMessageId(e,s){await t().deleteMessagesAfterMessageId(e,s)},getOperations:async e=>t().getOperations(e),getTrashItems:async()=>t().getTrashItems(),restoreFromTrash:async e=>t().restoreFromTrash(e),listDir:async e=>t().listDir(e),exists:async e=>t().exists(e),stat:async e=>t().stat(e),readFile:async e=>t().readFile(e),readFileBase64:async e=>t().readFileBase64(e),homeDir:async()=>t().homeDir(),resolvePath:async e=>t().resolvePath(e),parentDir:async e=>t().parentDir(e),watchDir:async e=>t().watchDir(e),unwatchDir:async e=>t().unwatchDir(e),onDirChange:e=>t().onDirChange(e),async getSetting(e){const s=t();return s.getSetting?s.getSetting(e):null},async setSetting(e,s){const n=t();n.setSetting&&await n.setSetting(e,s)},async getAllSettings(){const e=t();return e.getAllSettings?e.getAllSettings():{}},async deleteSetting(e){const s=t();s.deleteSetting&&await s.deleteSetting(e)},onToolApprovalRequest(e){const s=t();return s.onToolApprovalRequest?s.onToolApprovalRequest(e):()=>{}},async respondToolApproval(e,s){const n=t();if(n.respondToolApproval)return n.respondToolApproval(e,s)},async asrStart(e){const s=t();return s.asrStart?s.asrStart(e):{success:!1,error:"ASR not supported"}},async asrSendAudio(e){const s=t();return s.asrSendAudio?s.asrSendAudio(e):{success:!1,error:"ASR not supported"}},async asrFinish(){const e=t();return e.asrFinish?e.asrFinish():{success:!1,error:"ASR not supported"}},async asrStop(){const e=t();return e.asrStop?e.asrStop():{success:!0}},async asrStatus(){const e=t();return e.asrStatus?e.asrStatus():{connected:!1}},async asrWarmup(e){const s=t();return s.asrWarmup?s.asrWarmup(e):{success:!1,error:"ASR not supported"}},onAsrConnected(e){const s=t();return s.onAsrConnected?s.onAsrConnected(e):()=>{}},onAsrResult(e){const s=t();return s.onAsrResult?s.onAsrResult(e):()=>{}},onAsrError(e){const s=t();return s.onAsrError?s.onAsrError(e):()=>{}},onAsrClosed(e){const s=t();return s.onAsrClosed?s.onAsrClosed(e):()=>{}}}}export{e as createElectronAdapter};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huyooo/ai-chat-bridge-electron",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "AI Chat Electron Bridge - IPC integration for Electron apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main/index.js",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"clean": "rm -rf dist"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@huyooo/ai-chat-core": "^0.2.
|
|
38
|
-
"@huyooo/ai-chat-storage": "^0.2.
|
|
39
|
-
"@huyooo/ai-search": "^0.2.
|
|
37
|
+
"@huyooo/ai-chat-core": "^0.2.15",
|
|
38
|
+
"@huyooo/ai-chat-storage": "^0.2.15",
|
|
39
|
+
"@huyooo/ai-search": "^0.2.15",
|
|
40
40
|
"uuid": "^11.1.0",
|
|
41
41
|
"ws": "^8.18.3"
|
|
42
42
|
},
|