@huyooo/ai-search 0.2.3 → 0.2.5
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/bridge/electron.js +1 -1
- package/dist/bridge/renderer.d.ts +71 -0
- package/dist/bridge/renderer.js +29 -0
- package/dist/bridge/renderer.js.map +1 -0
- package/dist/{chunk-GAT4F5NK.js → chunk-MXWSYA4O.js} +56 -1
- package/dist/chunk-MXWSYA4O.js.map +1 -0
- package/dist/{index-B6UR8lRu.d.ts → index-BKiGwrBS.d.ts} +2 -264
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/tools/index.d.ts +2 -1
- package/dist/types-D1U3F-Fs.d.ts +264 -0
- package/package.json +7 -1
- package/dist/chunk-GAT4F5NK.js.map +0 -1
package/dist/bridge/electron.js
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { c as SearchOptions, d as SearchResult, I as IndexProgress } from '../types-D1U3F-Fs.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Electron 渲染进程桥接(给 App UI 使用)
|
|
5
|
+
*
|
|
6
|
+
* 注意:此模块依赖 electron(ipcRenderer),请仅在 Electron 渲染进程中使用:
|
|
7
|
+
* `import { createSearchClient } from '@huyooo/ai-search/bridge/renderer'`
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type IpcDateRange = {
|
|
11
|
+
start?: string;
|
|
12
|
+
end?: string;
|
|
13
|
+
};
|
|
14
|
+
type IpcSearchOptions = Omit<SearchOptions, 'dateRange'> & {
|
|
15
|
+
dateRange?: IpcDateRange;
|
|
16
|
+
};
|
|
17
|
+
interface WorkspaceState {
|
|
18
|
+
directory: string | null;
|
|
19
|
+
indexed: boolean;
|
|
20
|
+
filesIndexed: number;
|
|
21
|
+
}
|
|
22
|
+
interface SearchClientOptions {
|
|
23
|
+
channelPrefix?: string;
|
|
24
|
+
}
|
|
25
|
+
interface SearchClient {
|
|
26
|
+
/** 搜索(语义/关键词/混合) */
|
|
27
|
+
search: (query: string, options?: IpcSearchOptions) => Promise<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
data?: SearchResult[];
|
|
30
|
+
error?: string;
|
|
31
|
+
}>;
|
|
32
|
+
/** 设置工作空间(会触发索引) */
|
|
33
|
+
setWorkspace: (directory: string) => Promise<{
|
|
34
|
+
success: boolean;
|
|
35
|
+
error?: string;
|
|
36
|
+
}>;
|
|
37
|
+
/** 获取工作空间状态 */
|
|
38
|
+
getWorkspaceState: () => Promise<WorkspaceState>;
|
|
39
|
+
getIndexStats: () => Promise<{
|
|
40
|
+
totalDocuments: number;
|
|
41
|
+
indexSize: number;
|
|
42
|
+
lastUpdated: string | null;
|
|
43
|
+
}>;
|
|
44
|
+
syncIndex: () => Promise<{
|
|
45
|
+
success: boolean;
|
|
46
|
+
}>;
|
|
47
|
+
getIndexStatus: () => Promise<{
|
|
48
|
+
isIndexing: boolean;
|
|
49
|
+
lastProgress: IndexProgress | null;
|
|
50
|
+
}>;
|
|
51
|
+
cancelIndex: () => Promise<{
|
|
52
|
+
success: boolean;
|
|
53
|
+
message?: string;
|
|
54
|
+
}>;
|
|
55
|
+
deleteIndex: () => Promise<{
|
|
56
|
+
success: boolean;
|
|
57
|
+
}>;
|
|
58
|
+
registerIndexProgressListener: () => Promise<{
|
|
59
|
+
success: boolean;
|
|
60
|
+
}>;
|
|
61
|
+
unregisterIndexProgressListener: () => Promise<{
|
|
62
|
+
success: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
onIndexProgress: (callback: (progress: IndexProgress | (IndexProgress & {
|
|
65
|
+
stage: 'cancelled' | 'error';
|
|
66
|
+
error?: string;
|
|
67
|
+
})) => void) => () => void;
|
|
68
|
+
}
|
|
69
|
+
declare function createSearchClient(options?: SearchClientOptions): SearchClient;
|
|
70
|
+
|
|
71
|
+
export { type IpcSearchOptions, type SearchClient, type SearchClientOptions, type WorkspaceState, createSearchClient };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/bridge/renderer.ts
|
|
2
|
+
import { ipcRenderer } from "electron";
|
|
3
|
+
function createSearchClient(options = {}) {
|
|
4
|
+
const channelPrefix = options.channelPrefix || "ai-chat";
|
|
5
|
+
const channel = (name) => `${channelPrefix}:${name}`;
|
|
6
|
+
return {
|
|
7
|
+
search: (query, searchOptions) => ipcRenderer.invoke(channel("search:query"), query, searchOptions),
|
|
8
|
+
setWorkspace: (directory) => ipcRenderer.invoke(channel("workspace:set"), directory),
|
|
9
|
+
getWorkspaceState: () => ipcRenderer.invoke(channel("workspace:get")),
|
|
10
|
+
getIndexStats: () => ipcRenderer.invoke(channel("index:getStats")),
|
|
11
|
+
syncIndex: () => ipcRenderer.invoke(channel("index:sync")),
|
|
12
|
+
getIndexStatus: () => ipcRenderer.invoke(channel("index:status")),
|
|
13
|
+
cancelIndex: () => ipcRenderer.invoke(channel("index:cancel")),
|
|
14
|
+
deleteIndex: () => ipcRenderer.invoke(channel("index:delete")),
|
|
15
|
+
registerIndexProgressListener: () => ipcRenderer.invoke(channel("index:registerListener")),
|
|
16
|
+
unregisterIndexProgressListener: () => ipcRenderer.invoke(channel("index:unregisterListener")),
|
|
17
|
+
onIndexProgress: (callback) => {
|
|
18
|
+
const handler = (_event, progress) => {
|
|
19
|
+
callback(progress);
|
|
20
|
+
};
|
|
21
|
+
ipcRenderer.on(channel("index:progress"), handler);
|
|
22
|
+
return () => ipcRenderer.removeListener(channel("index:progress"), handler);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
createSearchClient
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bridge/renderer.ts"],"sourcesContent":["/**\n * Electron 渲染进程桥接(给 App UI 使用)\n *\n * 注意:此模块依赖 electron(ipcRenderer),请仅在 Electron 渲染进程中使用:\n * `import { createSearchClient } from '@huyooo/ai-search/bridge/renderer'`\n */\n\nimport { ipcRenderer, type IpcRendererEvent } from 'electron'\nimport type { IndexProgress, SearchOptions, SearchResult } from '../types'\n\ntype IpcDateRange = { start?: string; end?: string }\nexport type IpcSearchOptions = Omit<SearchOptions, 'dateRange'> & {\n dateRange?: IpcDateRange\n}\n\nexport interface WorkspaceState {\n directory: string | null\n indexed: boolean\n filesIndexed: number\n}\n\nexport interface SearchClientOptions {\n channelPrefix?: string\n}\n\nexport interface SearchClient {\n /** 搜索(语义/关键词/混合) */\n search: (query: string, options?: IpcSearchOptions) => Promise<{ success: boolean; data?: SearchResult[]; error?: string }>\n /** 设置工作空间(会触发索引) */\n setWorkspace: (directory: string) => Promise<{ success: boolean; error?: string }>\n /** 获取工作空间状态 */\n getWorkspaceState: () => Promise<WorkspaceState>\n\n // ===== 索引控制(可选) =====\n getIndexStats: () => Promise<{ totalDocuments: number; indexSize: number; lastUpdated: string | null }>\n syncIndex: () => Promise<{ success: boolean }>\n getIndexStatus: () => Promise<{ isIndexing: boolean; lastProgress: IndexProgress | null }>\n cancelIndex: () => Promise<{ success: boolean; message?: string }>\n deleteIndex: () => Promise<{ success: boolean }>\n\n // ===== 进度广播 =====\n registerIndexProgressListener: () => Promise<{ success: boolean }>\n unregisterIndexProgressListener: () => Promise<{ success: boolean }>\n onIndexProgress: (callback: (progress: IndexProgress | (IndexProgress & { stage: 'cancelled' | 'error'; error?: string })) => void) => () => void\n}\n\nexport function createSearchClient(options: SearchClientOptions = {}): SearchClient {\n const channelPrefix = options.channelPrefix || 'ai-chat'\n const channel = (name: string) => `${channelPrefix}:${name}`\n\n return {\n search: (query, searchOptions) => ipcRenderer.invoke(channel('search:query'), query, searchOptions),\n setWorkspace: (directory) => ipcRenderer.invoke(channel('workspace:set'), directory),\n getWorkspaceState: () => ipcRenderer.invoke(channel('workspace:get')),\n\n getIndexStats: () => ipcRenderer.invoke(channel('index:getStats')),\n syncIndex: () => ipcRenderer.invoke(channel('index:sync')),\n getIndexStatus: () => ipcRenderer.invoke(channel('index:status')),\n cancelIndex: () => ipcRenderer.invoke(channel('index:cancel')),\n deleteIndex: () => ipcRenderer.invoke(channel('index:delete')),\n\n registerIndexProgressListener: () => ipcRenderer.invoke(channel('index:registerListener')),\n unregisterIndexProgressListener: () => ipcRenderer.invoke(channel('index:unregisterListener')),\n onIndexProgress: (callback) => {\n const handler = (_event: IpcRendererEvent, progress: IndexProgress) => {\n callback(progress)\n }\n ipcRenderer.on(channel('index:progress'), handler)\n return () => ipcRenderer.removeListener(channel('index:progress'), handler)\n },\n }\n}\n\n\n"],"mappings":";AAOA,SAAS,mBAA0C;AAuC5C,SAAS,mBAAmB,UAA+B,CAAC,GAAiB;AAClF,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,UAAU,CAAC,SAAiB,GAAG,aAAa,IAAI,IAAI;AAE1D,SAAO;AAAA,IACL,QAAQ,CAAC,OAAO,kBAAkB,YAAY,OAAO,QAAQ,cAAc,GAAG,OAAO,aAAa;AAAA,IAClG,cAAc,CAAC,cAAc,YAAY,OAAO,QAAQ,eAAe,GAAG,SAAS;AAAA,IACnF,mBAAmB,MAAM,YAAY,OAAO,QAAQ,eAAe,CAAC;AAAA,IAEpE,eAAe,MAAM,YAAY,OAAO,QAAQ,gBAAgB,CAAC;AAAA,IACjE,WAAW,MAAM,YAAY,OAAO,QAAQ,YAAY,CAAC;AAAA,IACzD,gBAAgB,MAAM,YAAY,OAAO,QAAQ,cAAc,CAAC;AAAA,IAChE,aAAa,MAAM,YAAY,OAAO,QAAQ,cAAc,CAAC;AAAA,IAC7D,aAAa,MAAM,YAAY,OAAO,QAAQ,cAAc,CAAC;AAAA,IAE7D,+BAA+B,MAAM,YAAY,OAAO,QAAQ,wBAAwB,CAAC;AAAA,IACzF,iCAAiC,MAAM,YAAY,OAAO,QAAQ,0BAA0B,CAAC;AAAA,IAC7F,iBAAiB,CAAC,aAAa;AAC7B,YAAM,UAAU,CAAC,QAA0B,aAA4B;AACrE,iBAAS,QAAQ;AAAA,MACnB;AACA,kBAAY,GAAG,QAAQ,gBAAgB,GAAG,OAAO;AACjD,aAAO,MAAM,YAAY,eAAe,QAAQ,gBAAgB,GAAG,OAAO;AAAA,IAC5E;AAAA,EACF;AACF;","names":[]}
|
|
@@ -147,6 +147,61 @@ var SearchElectronBridge = class {
|
|
|
147
147
|
throw error;
|
|
148
148
|
}
|
|
149
149
|
});
|
|
150
|
+
const normalizeSearchOptions = (options) => {
|
|
151
|
+
if (!options) return void 0;
|
|
152
|
+
const { dateRange: rawDateRange, ...rest } = options;
|
|
153
|
+
if (!rawDateRange) {
|
|
154
|
+
return rest;
|
|
155
|
+
}
|
|
156
|
+
const start = rawDateRange.start ? new Date(rawDateRange.start) : void 0;
|
|
157
|
+
const end = rawDateRange.end ? new Date(rawDateRange.end) : void 0;
|
|
158
|
+
const hasValidStart = !!start && !Number.isNaN(start.getTime());
|
|
159
|
+
const hasValidEnd = !!end && !Number.isNaN(end.getTime());
|
|
160
|
+
if (!hasValidStart && !hasValidEnd) {
|
|
161
|
+
return rest;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
...rest,
|
|
165
|
+
dateRange: {
|
|
166
|
+
start: hasValidStart ? start : void 0,
|
|
167
|
+
end: hasValidEnd ? end : void 0
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
this.ipcMain.handle(
|
|
172
|
+
`${this.channelPrefix}:search:query`,
|
|
173
|
+
async (_event, query, options) => {
|
|
174
|
+
try {
|
|
175
|
+
const searchPlugin = getSearchPlugin();
|
|
176
|
+
if (!searchPlugin) {
|
|
177
|
+
return { success: false, error: "\u641C\u7D22\u63D2\u4EF6\u672A\u521D\u59CB\u5316" };
|
|
178
|
+
}
|
|
179
|
+
const results = await searchPlugin.search.search(query, normalizeSearchOptions(options));
|
|
180
|
+
return { success: true, data: results };
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
this.ipcMain.handle(`${this.channelPrefix}:workspace:set`, async (_event, directory) => {
|
|
187
|
+
const searchPlugin = getSearchPlugin();
|
|
188
|
+
if (!searchPlugin) {
|
|
189
|
+
return { success: false, error: "\u641C\u7D22\u63D2\u4EF6\u672A\u521D\u59CB\u5316" };
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
await searchPlugin.setWorkspace(directory);
|
|
193
|
+
return { success: true };
|
|
194
|
+
} catch (error) {
|
|
195
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
this.ipcMain.handle(`${this.channelPrefix}:workspace:get`, async () => {
|
|
199
|
+
const searchPlugin = getSearchPlugin();
|
|
200
|
+
if (!searchPlugin) {
|
|
201
|
+
return { directory: null, indexed: false, filesIndexed: 0 };
|
|
202
|
+
}
|
|
203
|
+
return searchPlugin.getWorkspaceState();
|
|
204
|
+
});
|
|
150
205
|
}
|
|
151
206
|
/** 广播索引进度到所有前端监听器 */
|
|
152
207
|
broadcastIndexProgress(progress) {
|
|
@@ -173,4 +228,4 @@ export {
|
|
|
173
228
|
SearchElectronBridge,
|
|
174
229
|
createSearchElectronBridge
|
|
175
230
|
};
|
|
176
|
-
//# sourceMappingURL=chunk-
|
|
231
|
+
//# sourceMappingURL=chunk-MXWSYA4O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bridge/electron.ts"],"sourcesContent":["/**\n * Electron 桥接模块\n * \n * 使用全局进度监听器自动广播索引进度\n * 无需包装方法,无需轮询,解决时序问题\n */\n\nimport type { IpcMain, WebContents } from 'electron';\nimport { getSearchPlugin } from '../tools';\nimport { addGlobalProgressListener } from '../core/progress';\nimport type { IndexProgress, SearchOptions, SearchResult } from '../types';\n\n// 扩展的索引进度类型,支持取消和错误状态\ntype ExtendedIndexProgress = IndexProgress | {\n indexed: number;\n total: number;\n currentFile?: string;\n stage: 'cancelled' | 'error';\n error?: string;\n};\n\n/** Electron 桥接选项 */\nexport interface SearchElectronBridgeOptions {\n /** IPC channel 前缀 */\n channelPrefix?: string;\n /** IPC Main 实例 */\n ipcMain: IpcMain;\n}\n\n/** 索引进度广播系统 */\nexport class SearchElectronBridge {\n private channelPrefix: string;\n private ipcMain: IpcMain;\n private indexingListeners = new Set<WebContents>();\n private lastProgress: ExtendedIndexProgress | null = null;\n private isIndexing = false;\n\n constructor(options: SearchElectronBridgeOptions) {\n this.channelPrefix = options.channelPrefix || 'ai-chat';\n this.ipcMain = options.ipcMain;\n }\n\n /** 初始化桥接(注册 IPC handlers 和全局进度监听器) */\n init(): void {\n this.registerIpcHandlers();\n this.registerGlobalProgressListener();\n console.log('[AI-Search] Electron 桥接已初始化');\n }\n\n /** 注册全局进度监听器 */\n private registerGlobalProgressListener(): void {\n // 注册全局进度监听器,所有 indexDirectory 调用都会触发\n addGlobalProgressListener((progress: IndexProgress) => {\n // 更新状态\n this.isIndexing = progress.stage !== 'done';\n this.lastProgress = progress;\n \n // 广播进度到所有前端监听器\n this.broadcastIndexProgress(progress);\n });\n }\n\n /** 注册 IPC handlers */\n private registerIpcHandlers(): void {\n // 注册索引进度监听器(前端调用)\n this.ipcMain.handle(`${this.channelPrefix}:index:registerListener`, (event) => {\n const webContents = event.sender;\n this.indexingListeners.add(webContents);\n \n // 如果正在索引,立即发送最后进度\n if (this.lastProgress && this.isIndexing) {\n if (!webContents.isDestroyed()) {\n webContents.send(`${this.channelPrefix}:index:progress`, {\n indexed: this.lastProgress.indexed,\n total: this.lastProgress.total,\n currentFile: this.lastProgress.currentFile,\n stage: this.lastProgress.stage,\n });\n }\n }\n \n return { success: true };\n });\n\n // 注销索引进度监听器(前端调用)\n this.ipcMain.handle(`${this.channelPrefix}:index:unregisterListener`, (event) => {\n this.indexingListeners.delete(event.sender);\n return { success: true };\n });\n\n // 获取索引统计信息\n this.ipcMain.handle(`${this.channelPrefix}:index:getStats`, async () => {\n try {\n const searchPlugin = getSearchPlugin();\n \n if (!searchPlugin) {\n return {\n totalDocuments: 0,\n indexSize: 0,\n lastUpdated: null,\n };\n }\n \n const stats = searchPlugin.search.getStats();\n return {\n totalDocuments: stats.totalDocuments,\n indexSize: stats.indexSize,\n lastUpdated: stats.lastUpdated ? stats.lastUpdated.toISOString() : null,\n };\n } catch (error) {\n console.error('[AI-Search] 获取索引统计失败:', error);\n return {\n totalDocuments: 0,\n indexSize: 0,\n lastUpdated: null,\n };\n }\n });\n\n // 同步索引(重新索引工作空间)\n this.ipcMain.handle(`${this.channelPrefix}:index:sync`, async () => {\n try {\n const searchPlugin = getSearchPlugin();\n \n if (!searchPlugin) {\n throw new Error('搜索插件未初始化');\n }\n\n const workspaceState = searchPlugin.getWorkspaceState();\n if (!workspaceState.directory) {\n throw new Error('工作空间未设置');\n }\n\n // 开始索引(进度会通过全局监听器自动广播)\n await searchPlugin.search.indexDirectory(workspaceState.directory!);\n \n return { success: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error('[AI-Search] 同步索引失败:', errorMessage);\n \n // 广播错误状态\n this.broadcastIndexProgress({\n indexed: 0,\n total: 0,\n stage: 'error',\n error: errorMessage,\n });\n \n throw error;\n }\n });\n\n // 检查索引状态\n this.ipcMain.handle(`${this.channelPrefix}:index:status`, async () => {\n return {\n isIndexing: this.isIndexing,\n lastProgress: this.lastProgress,\n };\n });\n\n // 取消索引\n this.ipcMain.handle(`${this.channelPrefix}:index:cancel`, async () => {\n try {\n const searchPlugin = getSearchPlugin();\n \n if (!searchPlugin) {\n return { success: false, message: '搜索插件未初始化' };\n }\n\n const cancelled = searchPlugin.search.cancelIndexing();\n \n if (cancelled) {\n // 广播取消状态\n this.broadcastIndexProgress({\n indexed: this.lastProgress?.indexed || 0,\n total: this.lastProgress?.total || 0,\n stage: 'cancelled',\n });\n this.isIndexing = false;\n return { success: true };\n } else {\n return { success: false, message: '没有正在运行的索引任务' };\n }\n } catch (error) {\n console.error('[AI-Search] 取消索引失败:', error);\n return { success: false, message: String(error) };\n }\n });\n\n // 删除索引\n this.ipcMain.handle(`${this.channelPrefix}:index:delete`, async () => {\n try {\n const searchPlugin = getSearchPlugin();\n \n if (!searchPlugin) {\n throw new Error('搜索插件未初始化');\n }\n\n await searchPlugin.search.clear();\n \n // 重置状态\n this.isIndexing = false;\n this.lastProgress = null;\n \n // 广播清空状态(使用 done 阶段,indexed=0 表示已清空)\n this.broadcastIndexProgress({\n indexed: 0,\n total: 0,\n stage: 'done',\n });\n \n return { success: true };\n } catch (error) {\n console.error('[AI-Search] 删除索引失败:', error);\n throw error;\n }\n });\n\n // ==================== 搜索能力(给 App UI 使用) ====================\n\n type IpcDateRange = { start?: string; end?: string };\n type IpcSearchOptions = Omit<SearchOptions, 'dateRange'> & {\n dateRange?: IpcDateRange;\n };\n\n const normalizeSearchOptions = (options?: IpcSearchOptions): SearchOptions | undefined => {\n if (!options) return undefined;\n\n const { dateRange: rawDateRange, ...rest } = options;\n\n if (!rawDateRange) {\n // 关键:必须剥离 IpcSearchOptions 的 dateRange 字段,避免类型不匹配\n return rest;\n }\n\n const start = rawDateRange.start ? new Date(rawDateRange.start) : undefined;\n const end = rawDateRange.end ? new Date(rawDateRange.end) : undefined;\n\n const hasValidStart = !!start && !Number.isNaN(start.getTime());\n const hasValidEnd = !!end && !Number.isNaN(end.getTime());\n\n if (!hasValidStart && !hasValidEnd) {\n return rest;\n }\n\n return {\n ...rest,\n dateRange: {\n start: hasValidStart ? start : undefined,\n end: hasValidEnd ? end : undefined,\n },\n };\n };\n\n // 语义/关键词/混合搜索(一次性返回)\n this.ipcMain.handle(\n `${this.channelPrefix}:search:query`,\n async (_event, query: string, options?: IpcSearchOptions): Promise<{ success: boolean; data?: SearchResult[]; error?: string }> => {\n try {\n const searchPlugin = getSearchPlugin();\n if (!searchPlugin) {\n return { success: false, error: '搜索插件未初始化' };\n }\n const results = await searchPlugin.search.search(query, normalizeSearchOptions(options));\n return { success: true, data: results };\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n }\n );\n\n // 设置工作空间(会触发索引,进度通过全局监听器自动广播)\n this.ipcMain.handle(`${this.channelPrefix}:workspace:set`, async (_event, directory: string) => {\n const searchPlugin = getSearchPlugin();\n if (!searchPlugin) {\n return { success: false, error: '搜索插件未初始化' };\n }\n try {\n await searchPlugin.setWorkspace(directory);\n return { success: true };\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) };\n }\n });\n\n // 获取工作空间状态\n this.ipcMain.handle(`${this.channelPrefix}:workspace:get`, async () => {\n const searchPlugin = getSearchPlugin();\n if (!searchPlugin) {\n return { directory: null, indexed: false, filesIndexed: 0 };\n }\n return searchPlugin.getWorkspaceState();\n });\n }\n\n /** 广播索引进度到所有前端监听器 */\n private broadcastIndexProgress(progress: ExtendedIndexProgress): void {\n // 更新最后进度\n this.lastProgress = progress;\n \n // 向所有监听器广播\n this.indexingListeners.forEach((webContents) => {\n if (!webContents.isDestroyed()) {\n webContents.send(`${this.channelPrefix}:index:progress`, {\n indexed: progress.indexed,\n total: progress.total,\n currentFile: progress.currentFile,\n stage: progress.stage,\n });\n } else {\n // 清理已销毁的 webContents\n this.indexingListeners.delete(webContents);\n }\n });\n }\n}\n\n/**\n * 创建搜索 Electron 桥接\n * \n * @example\n * ```typescript\n * import { createSearchElectronBridge } from '@huyooo/ai-search/bridge/electron';\n * import { ipcMain } from 'electron';\n * \n * const bridge = createSearchElectronBridge({\n * ipcMain,\n * channelPrefix: 'ai-chat',\n * });\n * bridge.init();\n * ```\n */\nexport function createSearchElectronBridge(\n options: SearchElectronBridgeOptions\n): SearchElectronBridge {\n return new SearchElectronBridge(options);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,268 +1,6 @@
|
|
|
1
|
+
import { I as IndexProgress, S as SearchConfig, c as SearchOptions, d as SearchResult, b as IndexStats, B as BatchOperationResult, W as WatchOptions, H as HealthCheckResult, g as IndexError, E as ExportInfo, f as BackupInfo } from './types-D1U3F-Fs.js';
|
|
1
2
|
import { ToolPlugin, Tool } from '@huyooo/ai-chat-core';
|
|
2
3
|
|
|
3
|
-
/**
|
|
4
|
-
* 文件类型(与 file-explorer 兼容)
|
|
5
|
-
*/
|
|
6
|
-
declare const FileType: {
|
|
7
|
-
readonly FOLDER: "folder";
|
|
8
|
-
readonly FILE: "file";
|
|
9
|
-
readonly IMAGE: "image";
|
|
10
|
-
readonly VIDEO: "video";
|
|
11
|
-
readonly MUSIC: "music";
|
|
12
|
-
readonly DOCUMENT: "document";
|
|
13
|
-
readonly CODE: "code";
|
|
14
|
-
readonly TEXT: "text";
|
|
15
|
-
readonly PDF: "pdf";
|
|
16
|
-
readonly ARCHIVE: "archive";
|
|
17
|
-
readonly APPLICATION: "application";
|
|
18
|
-
readonly UNKNOWN: "unknown";
|
|
19
|
-
};
|
|
20
|
-
type FileType = (typeof FileType)[keyof typeof FileType];
|
|
21
|
-
/**
|
|
22
|
-
* 搜索结果(兼容 file-explorer 的 FileItem)
|
|
23
|
-
*/
|
|
24
|
-
interface SearchResult {
|
|
25
|
-
/** 唯一标识(文件路径) */
|
|
26
|
-
id: string;
|
|
27
|
-
/** 文件名 */
|
|
28
|
-
name: string;
|
|
29
|
-
/** 文件类型 */
|
|
30
|
-
type: FileType;
|
|
31
|
-
/** 格式化后的文件大小 */
|
|
32
|
-
size?: string;
|
|
33
|
-
/** 格式化后的修改日期 */
|
|
34
|
-
dateModified?: string;
|
|
35
|
-
/** 文件 URL */
|
|
36
|
-
url?: string;
|
|
37
|
-
/** 缩略图 URL(文档类型通常没有) */
|
|
38
|
-
thumbnailUrl?: string;
|
|
39
|
-
/** 相关度分数 0-1 */
|
|
40
|
-
score: number;
|
|
41
|
-
/** 匹配文本摘要 */
|
|
42
|
-
snippet?: string;
|
|
43
|
-
/** 匹配类型 */
|
|
44
|
-
matchType: 'semantic' | 'keyword' | 'hybrid';
|
|
45
|
-
/** 匹配的块索引(分块后可用) */
|
|
46
|
-
chunkIndex?: number;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* 索引项(内部使用)
|
|
50
|
-
*/
|
|
51
|
-
interface IndexedDocument {
|
|
52
|
-
/** 唯一 ID */
|
|
53
|
-
id: string;
|
|
54
|
-
/** 文件路径 */
|
|
55
|
-
path: string;
|
|
56
|
-
/** 文件名 */
|
|
57
|
-
name: string;
|
|
58
|
-
/** 文件类型 */
|
|
59
|
-
fileType: FileType;
|
|
60
|
-
/** 文件扩展名 */
|
|
61
|
-
extension: string;
|
|
62
|
-
/** 文档标题(从内容提取) */
|
|
63
|
-
title?: string;
|
|
64
|
-
/** 文档内容(用于全文搜索) */
|
|
65
|
-
content: string;
|
|
66
|
-
/** 文件大小(字节) */
|
|
67
|
-
fileSize: number;
|
|
68
|
-
/** 创建时间 */
|
|
69
|
-
createdAt: Date;
|
|
70
|
-
/** 修改时间 */
|
|
71
|
-
modifiedAt: Date;
|
|
72
|
-
/** 索引时间 */
|
|
73
|
-
indexedAt: Date;
|
|
74
|
-
/** 内容哈希(用于检测变更) */
|
|
75
|
-
contentHash: string;
|
|
76
|
-
/** 分块数量(用于清理旧数据) */
|
|
77
|
-
chunkCount?: number;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* 搜索配置
|
|
81
|
-
*/
|
|
82
|
-
interface SearchConfig {
|
|
83
|
-
/** 数据存储目录 */
|
|
84
|
-
dataDir: string;
|
|
85
|
-
/** 索引目录列表 */
|
|
86
|
-
indexDirectories?: string[];
|
|
87
|
-
/** 排除目录 */
|
|
88
|
-
excludeDirs?: string[];
|
|
89
|
-
/** 支持的扩展名 */
|
|
90
|
-
extensions?: string[];
|
|
91
|
-
/** 最大文件大小(字节) */
|
|
92
|
-
maxFileSize?: number;
|
|
93
|
-
/** Embedding 模型(默认 doubao-embedding-vision-250615) */
|
|
94
|
-
embeddingModel?: string;
|
|
95
|
-
/** 豆包 API Key(ARK_API_KEY),也可通过环境变量设置 */
|
|
96
|
-
arkApiKey?: string;
|
|
97
|
-
/** 向量维度(默认 1024) */
|
|
98
|
-
embeddingDimension?: number;
|
|
99
|
-
/** 并行索引并发数(默认 5) */
|
|
100
|
-
indexConcurrency?: number;
|
|
101
|
-
/** 是否启用并行索引(默认 true) */
|
|
102
|
-
enableParallelIndexing?: boolean;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* 搜索选项
|
|
106
|
-
*/
|
|
107
|
-
interface SearchOptions {
|
|
108
|
-
/** 返回数量限制 */
|
|
109
|
-
limit?: number;
|
|
110
|
-
/** 文件类型过滤 */
|
|
111
|
-
fileTypes?: FileType[];
|
|
112
|
-
/** 时间范围过滤 */
|
|
113
|
-
dateRange?: {
|
|
114
|
-
start?: Date;
|
|
115
|
-
end?: Date;
|
|
116
|
-
};
|
|
117
|
-
/** 目录过滤 */
|
|
118
|
-
directories?: string[];
|
|
119
|
-
/** 搜索模式 */
|
|
120
|
-
mode?: 'semantic' | 'keyword' | 'hybrid';
|
|
121
|
-
/** 文件大小过滤 */
|
|
122
|
-
sizeRange?: {
|
|
123
|
-
min?: number;
|
|
124
|
-
max?: number;
|
|
125
|
-
};
|
|
126
|
-
/** 文件名模式过滤(支持通配符 * 和 ?) */
|
|
127
|
-
fileNamePattern?: string;
|
|
128
|
-
/** 文档标题包含 */
|
|
129
|
-
titleContains?: string;
|
|
130
|
-
/** 组合条件模式 */
|
|
131
|
-
combineMode?: 'AND' | 'OR';
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* 索引进度回调
|
|
135
|
-
*/
|
|
136
|
-
interface IndexProgress {
|
|
137
|
-
/** 当前已索引数量 */
|
|
138
|
-
indexed: number;
|
|
139
|
-
/** 总文件数量 */
|
|
140
|
-
total: number;
|
|
141
|
-
/** 当前正在处理的文件 */
|
|
142
|
-
currentFile?: string;
|
|
143
|
-
/** 阶段 */
|
|
144
|
-
stage: 'scanning' | 'parsing' | 'embedding' | 'storing' | 'done' | 'cancelled';
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* 索引统计
|
|
148
|
-
*/
|
|
149
|
-
interface IndexStats {
|
|
150
|
-
/** 总文档数 */
|
|
151
|
-
totalDocuments: number;
|
|
152
|
-
/** 按类型统计 */
|
|
153
|
-
byType: Record<FileType, number>;
|
|
154
|
-
/** 索引目录 */
|
|
155
|
-
directories: string[];
|
|
156
|
-
/** 最后更新时间 */
|
|
157
|
-
lastUpdated?: Date;
|
|
158
|
-
/** 索引大小(字节) */
|
|
159
|
-
indexSize: number;
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* 文件监听选项
|
|
163
|
-
*/
|
|
164
|
-
interface WatchOptions {
|
|
165
|
-
/** 是否忽略初始扫描(只监听后续变化) */
|
|
166
|
-
ignoreInitial?: boolean;
|
|
167
|
-
/** 防抖延迟(毫秒),避免频繁触发 */
|
|
168
|
-
debounce?: number;
|
|
169
|
-
/** 监听事件回调 */
|
|
170
|
-
onEvent?: (event: WatchEvent) => void;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* 文件监听事件
|
|
174
|
-
*/
|
|
175
|
-
interface WatchEvent {
|
|
176
|
-
/** 事件类型 */
|
|
177
|
-
type: 'add' | 'change' | 'unlink' | 'unlinkDir';
|
|
178
|
-
/** 文件路径 */
|
|
179
|
-
path: string;
|
|
180
|
-
/** 时间戳 */
|
|
181
|
-
timestamp: Date;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* 批量操作结果
|
|
185
|
-
*/
|
|
186
|
-
interface BatchOperationResult {
|
|
187
|
-
/** 成功数量 */
|
|
188
|
-
success: number;
|
|
189
|
-
/** 失败数量 */
|
|
190
|
-
failed: number;
|
|
191
|
-
/** 失败的文件路径和错误信息 */
|
|
192
|
-
errors: Array<{
|
|
193
|
-
path: string;
|
|
194
|
-
error: string;
|
|
195
|
-
}>;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* 索引导出信息
|
|
199
|
-
*/
|
|
200
|
-
interface ExportInfo {
|
|
201
|
-
/** 导出路径 */
|
|
202
|
-
exportPath: string;
|
|
203
|
-
/** 导出时间 */
|
|
204
|
-
timestamp: Date;
|
|
205
|
-
/** 包含的组件 */
|
|
206
|
-
components: {
|
|
207
|
-
meta: boolean;
|
|
208
|
-
vectors: boolean;
|
|
209
|
-
fulltext: boolean;
|
|
210
|
-
};
|
|
211
|
-
/** 统计信息 */
|
|
212
|
-
stats: IndexStats;
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* 备份信息
|
|
216
|
-
*/
|
|
217
|
-
interface BackupInfo {
|
|
218
|
-
/** 备份路径 */
|
|
219
|
-
path: string;
|
|
220
|
-
/** 备份时间 */
|
|
221
|
-
timestamp: Date;
|
|
222
|
-
/** 备份大小(字节) */
|
|
223
|
-
size: number;
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* 索引错误信息
|
|
227
|
-
*/
|
|
228
|
-
interface IndexError {
|
|
229
|
-
/** 文件路径 */
|
|
230
|
-
filePath: string;
|
|
231
|
-
/** 错误信息 */
|
|
232
|
-
error: string;
|
|
233
|
-
/** 重试次数 */
|
|
234
|
-
retryCount: number;
|
|
235
|
-
/** 时间戳 */
|
|
236
|
-
timestamp: Date;
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* 健康检查结果
|
|
240
|
-
*/
|
|
241
|
-
interface HealthCheckResult {
|
|
242
|
-
/** 是否健康 */
|
|
243
|
-
healthy: boolean;
|
|
244
|
-
/** 总文档数 */
|
|
245
|
-
totalDocuments: number;
|
|
246
|
-
/** 无效索引数(文件不存在) */
|
|
247
|
-
invalidIndexes: number;
|
|
248
|
-
/** 过期索引数(文件已修改) */
|
|
249
|
-
staleIndexes: number;
|
|
250
|
-
/** 错误数 */
|
|
251
|
-
errorCount: number;
|
|
252
|
-
/** 索引完整性 */
|
|
253
|
-
integrity: {
|
|
254
|
-
meta: boolean;
|
|
255
|
-
vectors: boolean;
|
|
256
|
-
fulltext: boolean;
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* 默认配置
|
|
261
|
-
* 注意:详细的排除规则和文件类型规则现在由 rules.ts 中的规则管理器统一管理
|
|
262
|
-
* 这里的配置主要用于向后兼容和简单配置场景
|
|
263
|
-
*/
|
|
264
|
-
declare const DEFAULT_CONFIG: Partial<SearchConfig>;
|
|
265
|
-
|
|
266
4
|
/**
|
|
267
5
|
* 文本分块模块
|
|
268
6
|
* 递归语义分割,保持上下文连贯性
|
|
@@ -573,4 +311,4 @@ declare function searchPlugin(options: SearchPluginOptions): Promise<SearchPlugi
|
|
|
573
311
|
/** 获取搜索插件实例(用于运行时控制) */
|
|
574
312
|
declare function getSearchPlugin(): SearchPluginInstance | null;
|
|
575
313
|
|
|
576
|
-
export { type
|
|
314
|
+
export { type ChunkOptions as C, DocumentSearch as D, IndexingPipeline as I, type PipelineConfig as P, type SkipCheckCallback as S, type TextChunk as T, type PipelineStats as a, searchPlugin as b, createIndexingPipeline as c, getSearchPlugin as d, type SearchPluginOptions as e, type SearchPluginInstance as f, getChunkStats as g, splitText as s };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export { C as ChunkOptions, D as DocumentSearch, I as IndexingPipeline, P as PipelineConfig, a as PipelineStats, f as SearchPluginInstance, e as SearchPluginOptions, S as SkipCheckCallback, T as TextChunk, c as createIndexingPipeline, g as getChunkStats, d as getSearchPlugin, b as searchPlugin, s as splitText } from './index-BKiGwrBS.js';
|
|
2
|
+
import { I as IndexProgress, F as FileType, a as IndexedDocument, b as IndexStats } from './types-D1U3F-Fs.js';
|
|
3
|
+
export { f as BackupInfo, B as BatchOperationResult, D as DEFAULT_CONFIG, E as ExportInfo, H as HealthCheckResult, g as IndexError, S as SearchConfig, c as SearchOptions, d as SearchResult, e as WatchEvent, W as WatchOptions } from './types-D1U3F-Fs.js';
|
|
3
4
|
export { SearchElectronBridge, SearchElectronBridgeOptions, createSearchElectronBridge } from './bridge/electron.js';
|
|
4
5
|
export { Tool, ToolContext, ToolPlugin } from '@huyooo/ai-chat-core';
|
|
5
6
|
import 'electron';
|
package/dist/index.js
CHANGED
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { f as SearchPluginInstance, e as SearchPluginOptions, d as getSearchPlugin, b as searchPlugin } from '../index-BKiGwrBS.js';
|
|
2
2
|
export { SideEffect, Tool, ToolContext, ToolPlugin, ToolResult } from '@huyooo/ai-chat-core';
|
|
3
|
+
import '../types-D1U3F-Fs.js';
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 文件类型(与 file-explorer 兼容)
|
|
3
|
+
*/
|
|
4
|
+
declare const FileType: {
|
|
5
|
+
readonly FOLDER: "folder";
|
|
6
|
+
readonly FILE: "file";
|
|
7
|
+
readonly IMAGE: "image";
|
|
8
|
+
readonly VIDEO: "video";
|
|
9
|
+
readonly MUSIC: "music";
|
|
10
|
+
readonly DOCUMENT: "document";
|
|
11
|
+
readonly CODE: "code";
|
|
12
|
+
readonly TEXT: "text";
|
|
13
|
+
readonly PDF: "pdf";
|
|
14
|
+
readonly ARCHIVE: "archive";
|
|
15
|
+
readonly APPLICATION: "application";
|
|
16
|
+
readonly UNKNOWN: "unknown";
|
|
17
|
+
};
|
|
18
|
+
type FileType = (typeof FileType)[keyof typeof FileType];
|
|
19
|
+
/**
|
|
20
|
+
* 搜索结果(兼容 file-explorer 的 FileItem)
|
|
21
|
+
*/
|
|
22
|
+
interface SearchResult {
|
|
23
|
+
/** 唯一标识(文件路径) */
|
|
24
|
+
id: string;
|
|
25
|
+
/** 文件名 */
|
|
26
|
+
name: string;
|
|
27
|
+
/** 文件类型 */
|
|
28
|
+
type: FileType;
|
|
29
|
+
/** 格式化后的文件大小 */
|
|
30
|
+
size?: string;
|
|
31
|
+
/** 格式化后的修改日期 */
|
|
32
|
+
dateModified?: string;
|
|
33
|
+
/** 文件 URL */
|
|
34
|
+
url?: string;
|
|
35
|
+
/** 缩略图 URL(文档类型通常没有) */
|
|
36
|
+
thumbnailUrl?: string;
|
|
37
|
+
/** 相关度分数 0-1 */
|
|
38
|
+
score: number;
|
|
39
|
+
/** 匹配文本摘要 */
|
|
40
|
+
snippet?: string;
|
|
41
|
+
/** 匹配类型 */
|
|
42
|
+
matchType: 'semantic' | 'keyword' | 'hybrid';
|
|
43
|
+
/** 匹配的块索引(分块后可用) */
|
|
44
|
+
chunkIndex?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 索引项(内部使用)
|
|
48
|
+
*/
|
|
49
|
+
interface IndexedDocument {
|
|
50
|
+
/** 唯一 ID */
|
|
51
|
+
id: string;
|
|
52
|
+
/** 文件路径 */
|
|
53
|
+
path: string;
|
|
54
|
+
/** 文件名 */
|
|
55
|
+
name: string;
|
|
56
|
+
/** 文件类型 */
|
|
57
|
+
fileType: FileType;
|
|
58
|
+
/** 文件扩展名 */
|
|
59
|
+
extension: string;
|
|
60
|
+
/** 文档标题(从内容提取) */
|
|
61
|
+
title?: string;
|
|
62
|
+
/** 文档内容(用于全文搜索) */
|
|
63
|
+
content: string;
|
|
64
|
+
/** 文件大小(字节) */
|
|
65
|
+
fileSize: number;
|
|
66
|
+
/** 创建时间 */
|
|
67
|
+
createdAt: Date;
|
|
68
|
+
/** 修改时间 */
|
|
69
|
+
modifiedAt: Date;
|
|
70
|
+
/** 索引时间 */
|
|
71
|
+
indexedAt: Date;
|
|
72
|
+
/** 内容哈希(用于检测变更) */
|
|
73
|
+
contentHash: string;
|
|
74
|
+
/** 分块数量(用于清理旧数据) */
|
|
75
|
+
chunkCount?: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 搜索配置
|
|
79
|
+
*/
|
|
80
|
+
interface SearchConfig {
|
|
81
|
+
/** 数据存储目录 */
|
|
82
|
+
dataDir: string;
|
|
83
|
+
/** 索引目录列表 */
|
|
84
|
+
indexDirectories?: string[];
|
|
85
|
+
/** 排除目录 */
|
|
86
|
+
excludeDirs?: string[];
|
|
87
|
+
/** 支持的扩展名 */
|
|
88
|
+
extensions?: string[];
|
|
89
|
+
/** 最大文件大小(字节) */
|
|
90
|
+
maxFileSize?: number;
|
|
91
|
+
/** Embedding 模型(默认 doubao-embedding-vision-250615) */
|
|
92
|
+
embeddingModel?: string;
|
|
93
|
+
/** 豆包 API Key(ARK_API_KEY),也可通过环境变量设置 */
|
|
94
|
+
arkApiKey?: string;
|
|
95
|
+
/** 向量维度(默认 1024) */
|
|
96
|
+
embeddingDimension?: number;
|
|
97
|
+
/** 并行索引并发数(默认 5) */
|
|
98
|
+
indexConcurrency?: number;
|
|
99
|
+
/** 是否启用并行索引(默认 true) */
|
|
100
|
+
enableParallelIndexing?: boolean;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 搜索选项
|
|
104
|
+
*/
|
|
105
|
+
interface SearchOptions {
|
|
106
|
+
/** 返回数量限制 */
|
|
107
|
+
limit?: number;
|
|
108
|
+
/** 文件类型过滤 */
|
|
109
|
+
fileTypes?: FileType[];
|
|
110
|
+
/** 时间范围过滤 */
|
|
111
|
+
dateRange?: {
|
|
112
|
+
start?: Date;
|
|
113
|
+
end?: Date;
|
|
114
|
+
};
|
|
115
|
+
/** 目录过滤 */
|
|
116
|
+
directories?: string[];
|
|
117
|
+
/** 搜索模式 */
|
|
118
|
+
mode?: 'semantic' | 'keyword' | 'hybrid';
|
|
119
|
+
/** 文件大小过滤 */
|
|
120
|
+
sizeRange?: {
|
|
121
|
+
min?: number;
|
|
122
|
+
max?: number;
|
|
123
|
+
};
|
|
124
|
+
/** 文件名模式过滤(支持通配符 * 和 ?) */
|
|
125
|
+
fileNamePattern?: string;
|
|
126
|
+
/** 文档标题包含 */
|
|
127
|
+
titleContains?: string;
|
|
128
|
+
/** 组合条件模式 */
|
|
129
|
+
combineMode?: 'AND' | 'OR';
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 索引进度回调
|
|
133
|
+
*/
|
|
134
|
+
interface IndexProgress {
|
|
135
|
+
/** 当前已索引数量 */
|
|
136
|
+
indexed: number;
|
|
137
|
+
/** 总文件数量 */
|
|
138
|
+
total: number;
|
|
139
|
+
/** 当前正在处理的文件 */
|
|
140
|
+
currentFile?: string;
|
|
141
|
+
/** 阶段 */
|
|
142
|
+
stage: 'scanning' | 'parsing' | 'embedding' | 'storing' | 'done' | 'cancelled';
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 索引统计
|
|
146
|
+
*/
|
|
147
|
+
interface IndexStats {
|
|
148
|
+
/** 总文档数 */
|
|
149
|
+
totalDocuments: number;
|
|
150
|
+
/** 按类型统计 */
|
|
151
|
+
byType: Record<FileType, number>;
|
|
152
|
+
/** 索引目录 */
|
|
153
|
+
directories: string[];
|
|
154
|
+
/** 最后更新时间 */
|
|
155
|
+
lastUpdated?: Date;
|
|
156
|
+
/** 索引大小(字节) */
|
|
157
|
+
indexSize: number;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* 文件监听选项
|
|
161
|
+
*/
|
|
162
|
+
interface WatchOptions {
|
|
163
|
+
/** 是否忽略初始扫描(只监听后续变化) */
|
|
164
|
+
ignoreInitial?: boolean;
|
|
165
|
+
/** 防抖延迟(毫秒),避免频繁触发 */
|
|
166
|
+
debounce?: number;
|
|
167
|
+
/** 监听事件回调 */
|
|
168
|
+
onEvent?: (event: WatchEvent) => void;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 文件监听事件
|
|
172
|
+
*/
|
|
173
|
+
interface WatchEvent {
|
|
174
|
+
/** 事件类型 */
|
|
175
|
+
type: 'add' | 'change' | 'unlink' | 'unlinkDir';
|
|
176
|
+
/** 文件路径 */
|
|
177
|
+
path: string;
|
|
178
|
+
/** 时间戳 */
|
|
179
|
+
timestamp: Date;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 批量操作结果
|
|
183
|
+
*/
|
|
184
|
+
interface BatchOperationResult {
|
|
185
|
+
/** 成功数量 */
|
|
186
|
+
success: number;
|
|
187
|
+
/** 失败数量 */
|
|
188
|
+
failed: number;
|
|
189
|
+
/** 失败的文件路径和错误信息 */
|
|
190
|
+
errors: Array<{
|
|
191
|
+
path: string;
|
|
192
|
+
error: string;
|
|
193
|
+
}>;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* 索引导出信息
|
|
197
|
+
*/
|
|
198
|
+
interface ExportInfo {
|
|
199
|
+
/** 导出路径 */
|
|
200
|
+
exportPath: string;
|
|
201
|
+
/** 导出时间 */
|
|
202
|
+
timestamp: Date;
|
|
203
|
+
/** 包含的组件 */
|
|
204
|
+
components: {
|
|
205
|
+
meta: boolean;
|
|
206
|
+
vectors: boolean;
|
|
207
|
+
fulltext: boolean;
|
|
208
|
+
};
|
|
209
|
+
/** 统计信息 */
|
|
210
|
+
stats: IndexStats;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* 备份信息
|
|
214
|
+
*/
|
|
215
|
+
interface BackupInfo {
|
|
216
|
+
/** 备份路径 */
|
|
217
|
+
path: string;
|
|
218
|
+
/** 备份时间 */
|
|
219
|
+
timestamp: Date;
|
|
220
|
+
/** 备份大小(字节) */
|
|
221
|
+
size: number;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* 索引错误信息
|
|
225
|
+
*/
|
|
226
|
+
interface IndexError {
|
|
227
|
+
/** 文件路径 */
|
|
228
|
+
filePath: string;
|
|
229
|
+
/** 错误信息 */
|
|
230
|
+
error: string;
|
|
231
|
+
/** 重试次数 */
|
|
232
|
+
retryCount: number;
|
|
233
|
+
/** 时间戳 */
|
|
234
|
+
timestamp: Date;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* 健康检查结果
|
|
238
|
+
*/
|
|
239
|
+
interface HealthCheckResult {
|
|
240
|
+
/** 是否健康 */
|
|
241
|
+
healthy: boolean;
|
|
242
|
+
/** 总文档数 */
|
|
243
|
+
totalDocuments: number;
|
|
244
|
+
/** 无效索引数(文件不存在) */
|
|
245
|
+
invalidIndexes: number;
|
|
246
|
+
/** 过期索引数(文件已修改) */
|
|
247
|
+
staleIndexes: number;
|
|
248
|
+
/** 错误数 */
|
|
249
|
+
errorCount: number;
|
|
250
|
+
/** 索引完整性 */
|
|
251
|
+
integrity: {
|
|
252
|
+
meta: boolean;
|
|
253
|
+
vectors: boolean;
|
|
254
|
+
fulltext: boolean;
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 默认配置
|
|
259
|
+
* 注意:详细的排除规则和文件类型规则现在由 rules.ts 中的规则管理器统一管理
|
|
260
|
+
* 这里的配置主要用于向后兼容和简单配置场景
|
|
261
|
+
*/
|
|
262
|
+
declare const DEFAULT_CONFIG: Partial<SearchConfig>;
|
|
263
|
+
|
|
264
|
+
export { type BatchOperationResult as B, DEFAULT_CONFIG as D, type ExportInfo as E, FileType as F, type HealthCheckResult as H, type IndexProgress as I, type SearchConfig as S, type WatchOptions as W, type IndexedDocument as a, type IndexStats as b, type SearchOptions as c, type SearchResult as d, type WatchEvent as e, type BackupInfo as f, type IndexError as g };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huyooo/ai-search",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "本地文档语义搜索引擎 - 支持 Word、PDF、Excel、TXT、MD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
"development": "./src/bridge/electron.ts",
|
|
24
24
|
"import": "./dist/bridge/electron.js",
|
|
25
25
|
"default": "./dist/bridge/electron.js"
|
|
26
|
+
},
|
|
27
|
+
"./bridge/renderer": {
|
|
28
|
+
"types": "./dist/bridge/renderer.d.ts",
|
|
29
|
+
"development": "./src/bridge/renderer.ts",
|
|
30
|
+
"import": "./dist/bridge/renderer.js",
|
|
31
|
+
"default": "./dist/bridge/renderer.js"
|
|
26
32
|
}
|
|
27
33
|
},
|
|
28
34
|
"files": [
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bridge/electron.ts"],"sourcesContent":["/**\n * Electron 桥接模块\n * \n * 使用全局进度监听器自动广播索引进度\n * 无需包装方法,无需轮询,解决时序问题\n */\n\nimport type { IpcMain, WebContents } from 'electron';\nimport { getSearchPlugin } from '../tools';\nimport { addGlobalProgressListener } from '../core/progress';\nimport type { IndexProgress } from '../types';\n\n// 扩展的索引进度类型,支持取消和错误状态\ntype ExtendedIndexProgress = IndexProgress | {\n indexed: number;\n total: number;\n currentFile?: string;\n stage: 'cancelled' | 'error';\n error?: string;\n};\n\n/** Electron 桥接选项 */\nexport interface SearchElectronBridgeOptions {\n /** IPC channel 前缀 */\n channelPrefix?: string;\n /** IPC Main 实例 */\n ipcMain: IpcMain;\n}\n\n/** 索引进度广播系统 */\nexport class SearchElectronBridge {\n private channelPrefix: string;\n private ipcMain: IpcMain;\n private indexingListeners = new Set<WebContents>();\n private lastProgress: ExtendedIndexProgress | null = null;\n private isIndexing = false;\n\n constructor(options: SearchElectronBridgeOptions) {\n this.channelPrefix = options.channelPrefix || 'ai-chat';\n this.ipcMain = options.ipcMain;\n }\n\n /** 初始化桥接(注册 IPC handlers 和全局进度监听器) */\n init(): void {\n this.registerIpcHandlers();\n this.registerGlobalProgressListener();\n console.log('[AI-Search] Electron 桥接已初始化');\n }\n\n /** 注册全局进度监听器 */\n private registerGlobalProgressListener(): void {\n // 注册全局进度监听器,所有 indexDirectory 调用都会触发\n addGlobalProgressListener((progress: IndexProgress) => {\n // 更新状态\n this.isIndexing = progress.stage !== 'done';\n this.lastProgress = progress;\n \n // 广播进度到所有前端监听器\n this.broadcastIndexProgress(progress);\n });\n }\n\n /** 注册 IPC handlers */\n private registerIpcHandlers(): void {\n // 注册索引进度监听器(前端调用)\n this.ipcMain.handle(`${this.channelPrefix}:index:registerListener`, (event) => {\n const webContents = event.sender;\n this.indexingListeners.add(webContents);\n \n // 如果正在索引,立即发送最后进度\n if (this.lastProgress && this.isIndexing) {\n if (!webContents.isDestroyed()) {\n webContents.send(`${this.channelPrefix}:index:progress`, {\n indexed: this.lastProgress.indexed,\n total: this.lastProgress.total,\n currentFile: this.lastProgress.currentFile,\n stage: this.lastProgress.stage,\n });\n }\n }\n \n return { success: true };\n });\n\n // 注销索引进度监听器(前端调用)\n this.ipcMain.handle(`${this.channelPrefix}:index:unregisterListener`, (event) => {\n this.indexingListeners.delete(event.sender);\n return { success: true };\n });\n\n // 获取索引统计信息\n this.ipcMain.handle(`${this.channelPrefix}:index:getStats`, async () => {\n try {\n const searchPlugin = getSearchPlugin();\n \n if (!searchPlugin) {\n return {\n totalDocuments: 0,\n indexSize: 0,\n lastUpdated: null,\n };\n }\n \n const stats = searchPlugin.search.getStats();\n return {\n totalDocuments: stats.totalDocuments,\n indexSize: stats.indexSize,\n lastUpdated: stats.lastUpdated ? stats.lastUpdated.toISOString() : null,\n };\n } catch (error) {\n console.error('[AI-Search] 获取索引统计失败:', error);\n return {\n totalDocuments: 0,\n indexSize: 0,\n lastUpdated: null,\n };\n }\n });\n\n // 同步索引(重新索引工作空间)\n this.ipcMain.handle(`${this.channelPrefix}:index:sync`, async () => {\n try {\n const searchPlugin = getSearchPlugin();\n \n if (!searchPlugin) {\n throw new Error('搜索插件未初始化');\n }\n\n const workspaceState = searchPlugin.getWorkspaceState();\n if (!workspaceState.directory) {\n throw new Error('工作空间未设置');\n }\n\n // 开始索引(进度会通过全局监听器自动广播)\n await searchPlugin.search.indexDirectory(workspaceState.directory!);\n \n return { success: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error('[AI-Search] 同步索引失败:', errorMessage);\n \n // 广播错误状态\n this.broadcastIndexProgress({\n indexed: 0,\n total: 0,\n stage: 'error',\n error: errorMessage,\n });\n \n throw error;\n }\n });\n\n // 检查索引状态\n this.ipcMain.handle(`${this.channelPrefix}:index:status`, async () => {\n return {\n isIndexing: this.isIndexing,\n lastProgress: this.lastProgress,\n };\n });\n\n // 取消索引\n this.ipcMain.handle(`${this.channelPrefix}:index:cancel`, async () => {\n try {\n const searchPlugin = getSearchPlugin();\n \n if (!searchPlugin) {\n return { success: false, message: '搜索插件未初始化' };\n }\n\n const cancelled = searchPlugin.search.cancelIndexing();\n \n if (cancelled) {\n // 广播取消状态\n this.broadcastIndexProgress({\n indexed: this.lastProgress?.indexed || 0,\n total: this.lastProgress?.total || 0,\n stage: 'cancelled',\n });\n this.isIndexing = false;\n return { success: true };\n } else {\n return { success: false, message: '没有正在运行的索引任务' };\n }\n } catch (error) {\n console.error('[AI-Search] 取消索引失败:', error);\n return { success: false, message: String(error) };\n }\n });\n\n // 删除索引\n this.ipcMain.handle(`${this.channelPrefix}:index:delete`, async () => {\n try {\n const searchPlugin = getSearchPlugin();\n \n if (!searchPlugin) {\n throw new Error('搜索插件未初始化');\n }\n\n await searchPlugin.search.clear();\n \n // 重置状态\n this.isIndexing = false;\n this.lastProgress = null;\n \n // 广播清空状态(使用 done 阶段,indexed=0 表示已清空)\n this.broadcastIndexProgress({\n indexed: 0,\n total: 0,\n stage: 'done',\n });\n \n return { success: true };\n } catch (error) {\n console.error('[AI-Search] 删除索引失败:', error);\n throw error;\n }\n });\n }\n\n /** 广播索引进度到所有前端监听器 */\n private broadcastIndexProgress(progress: ExtendedIndexProgress): void {\n // 更新最后进度\n this.lastProgress = progress;\n \n // 向所有监听器广播\n this.indexingListeners.forEach((webContents) => {\n if (!webContents.isDestroyed()) {\n webContents.send(`${this.channelPrefix}:index:progress`, {\n indexed: progress.indexed,\n total: progress.total,\n currentFile: progress.currentFile,\n stage: progress.stage,\n });\n } else {\n // 清理已销毁的 webContents\n this.indexingListeners.delete(webContents);\n }\n });\n }\n}\n\n/**\n * 创建搜索 Electron 桥接\n * \n * @example\n * ```typescript\n * import { createSearchElectronBridge } from '@huyooo/ai-search/bridge/electron';\n * import { ipcMain } from 'electron';\n * \n * const bridge = createSearchElectronBridge({\n * ipcMain,\n * channelPrefix: 'ai-chat',\n * });\n * bridge.init();\n * ```\n */\nexport function createSearchElectronBridge(\n options: SearchElectronBridgeOptions\n): SearchElectronBridge {\n return new SearchElectronBridge(options);\n}\n"],"mappings":"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","names":[]}
|