@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.
@@ -21,12 +21,14 @@ interface SendMessageParams {
21
21
  interface ModelOption {
22
22
  modelId: string;
23
23
  displayName: string;
24
- /** 分组名称(由后端决定,前端只负责渲染,必填) */
25
- group: string;
26
- /** 是否来自 OpenRouter(保留用于兼容,后续可能移除) */
27
- isOpenRouter?: boolean;
28
- /** 提供商名称(保留用于兼容,后续可能移除) */
29
- provider?: string;
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
- timestamp: Date;
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
  /** 获取回收站 */
@@ -1,136 +1 @@
1
- // src/preload/index.ts
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;
@@ -21,12 +21,14 @@ interface SendMessageParams {
21
21
  interface ModelOption$1 {
22
22
  modelId: string;
23
23
  displayName: string;
24
- /** 分组名称(由后端决定,前端只负责渲染,必填) */
25
- group: string;
26
- /** 是否来自 OpenRouter(保留用于兼容,后续可能移除) */
27
- isOpenRouter?: boolean;
28
- /** 提供商名称(保留用于兼容,后续可能移除) */
29
- provider?: string;
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
- timestamp: Date;
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
- group: string;
387
- /** 是否来自 OpenRouter(保留用于兼容,后续可能移除) */
388
- isOpenRouter?: boolean;
389
- /** 提供商名称(保留用于兼容,后续可能移除) */
390
- provider?: string;
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
- timestamp: Date;
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
  /** 获取回收站 */
@@ -1,253 +1 @@
1
- // src/renderer/index.ts
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.13",
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.13",
38
- "@huyooo/ai-chat-storage": "^0.2.13",
39
- "@huyooo/ai-search": "^0.2.13",
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
  },