@huyooo/ai-search 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/bridge/electron.js +1 -10
- package/dist/bridge/renderer.js +1 -29
- package/dist/chunk-45UQFAEP.js +1 -0
- package/dist/chunk-LMBF5LBD.js +1 -0
- package/dist/index.js +1 -99
- package/dist/tools/index.js +1 -9
- package/package.json +1 -1
- package/dist/bridge/electron.js.map +0 -1
- package/dist/bridge/renderer.js.map +0 -1
- package/dist/chunk-MXWSYA4O.js +0 -231
- package/dist/chunk-MXWSYA4O.js.map +0 -1
- package/dist/chunk-YJIIX54F.js +0 -4239
- package/dist/chunk-YJIIX54F.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/tools/index.js.map +0 -1
package/dist/bridge/electron.js
CHANGED
|
@@ -1,10 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SearchElectronBridge,
|
|
3
|
-
createSearchElectronBridge
|
|
4
|
-
} from "../chunk-MXWSYA4O.js";
|
|
5
|
-
import "../chunk-YJIIX54F.js";
|
|
6
|
-
export {
|
|
7
|
-
SearchElectronBridge,
|
|
8
|
-
createSearchElectronBridge
|
|
9
|
-
};
|
|
10
|
-
//# sourceMappingURL=electron.js.map
|
|
1
|
+
import{SearchElectronBridge as o,createSearchElectronBridge as r}from"../chunk-LMBF5LBD.js";import"../chunk-45UQFAEP.js";export{o as SearchElectronBridge,r as createSearchElectronBridge};
|
package/dist/bridge/renderer.js
CHANGED
|
@@ -1,29 +1 @@
|
|
|
1
|
-
|
|
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
|
|
1
|
+
import{ipcRenderer as e}from"electron";function n(n={}){const r=n.channelPrefix||"ai-chat",t=e=>`${r}:${e}`;return{search:(n,r)=>e.invoke(t("search:query"),n,r),setWorkspace:n=>e.invoke(t("workspace:set"),n),getWorkspaceState:()=>e.invoke(t("workspace:get")),getIndexStats:()=>e.invoke(t("index:getStats")),syncIndex:()=>e.invoke(t("index:sync")),getIndexStatus:()=>e.invoke(t("index:status")),cancelIndex:()=>e.invoke(t("index:cancel")),deleteIndex:()=>e.invoke(t("index:delete")),registerIndexProgressListener:()=>e.invoke(t("index:registerListener")),unregisterIndexProgressListener:()=>e.invoke(t("index:unregisterListener")),onIndexProgress:n=>{const r=(e,r)=>{n(r)};return e.on(t("index:progress"),r),()=>e.removeListener(t("index:progress"),r)}}}export{n as createSearchClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"fs/promises";import*as t from"path";var i={FOLDER:"folder",FILE:"file",IMAGE:"image",VIDEO:"video",MUSIC:"music",DOCUMENT:"document",CODE:"code",TEXT:"text",PDF:"pdf",ARCHIVE:"archive",APPLICATION:"application",UNKNOWN:"unknown"},n={excludeDirs:["node_modules",".git",".svn","__pycache__",".cache",".Trash","Library","AppData","$RECYCLE.BIN"],extensions:[".docx",".doc",".pdf",".xlsx",".xls",".pptx",".ppt",".txt",".md",".rtf"],maxFileSize:52428800,embeddingModel:"doubao-embedding-vision-250615",embeddingDimension:1024,indexConcurrency:5,enableParallelIndexing:!0};import*as s from"@lancedb/lancedb";var r=class{db=null;table=null;dbPath;tableName;dimension;constructor(e,t="documents",i=384){this.dbPath=e,this.tableName=t,this.dimension=i}async init(){this.db=await s.connect(this.dbPath);try{this.table=await this.db.openTable(this.tableName)}catch{const e=[{id:"__init__",vector:new Array(this.dimension).fill(0)}];this.table=await this.db.createTable(this.tableName,e),await this.table.delete('id = "__init__"')}}async add(e){if(!this.table)throw new Error("VectorStore not initialized");0!==e.length&&await this.table.add(e)}async update(e){if(!this.table)throw new Error("VectorStore not initialized");await this.delete(e.id),await this.add([e])}async getById(e){if(!this.table)throw new Error("VectorStore not initialized");try{const t=await this.table.search().where(`id = "${e}"`).limit(1).toArray();return 0===t.length?null:t[0].vector}catch{return null}}async delete(e){if(!this.table)throw new Error("VectorStore not initialized");await this.table.delete(`id = "${e}"`)}async search(e,t=10){if(!this.table)throw new Error("VectorStore not initialized");return(await this.table.vectorSearch(e).limit(t).toArray()).map(e=>({id:e.id,distance:e._distance}))}async count(){if(!this.table)throw new Error("VectorStore not initialized");return await this.table.countRows()}async exists(e){if(!this.table)throw new Error("VectorStore not initialized");return(await this.table.search([]).where(`id = "${e}"`).limit(1).toArray()).length>0}close(){this.table=null,this.db=null}};import o from"flexsearch";import a from"nodejieba";import*as c from"path";var d=class{index;indexPath;docCount=0;constructor(e){this.indexPath=c.join(e,"fulltext-index.json"),this.index=this.createIndex()}createIndex(){return new o.Document({document:{id:"id",index:["title","content"],store:!0},encode:e=>a.cut(e).filter(e=>e.trim().length>0&&!/^[\s\p{P}]+$/u.test(e)),cache:100})}async init(){}add(e){this.index.add(e),this.docCount++}update(e){this.remove(e.id),this.add(e)}remove(e){this.index.remove(e),this.docCount=Math.max(0,this.docCount-1)}search(e,t=10){if(!e.trim())return[];const i=this.index.search(e,{limit:2*t,index:["title","content"],enrich:!0}),n=new Map;for(const e of i){const t="title"===e.field?2:1;if(Array.isArray(e.result))for(let i=0;i<e.result.length;i++){const s=e.result[i],r="object"==typeof s&&null!==s?s.id??s:s;if("string"==typeof r){const e=t/(i+1);n.set(r,(n.get(r)||0)+e)}}}const s=Math.max(...n.values(),1);return Array.from(n.entries()).sort((e,t)=>t[1]-e[1]).slice(0,t).map(([e,t])=>({id:e,score:t/s}))}getDocCount(){return this.docCount}getContent(e){try{const t=this.index.get(e);return t&&"object"==typeof t&&t.content||null}catch{return null}}async save(){}async load(){}clear(){this.index=this.createIndex(),this.docCount=0}};import l from"better-sqlite3";import*as u from"path";import*as h from"fs";var m=class{db;dbPath;constructor(e){this.dbPath=u.join(e,"meta.db"),h.mkdirSync(e,{recursive:!0}),this.db=new l(this.dbPath),this.init()}init(){this.db.exec("\n CREATE TABLE IF NOT EXISTS documents (\n id TEXT PRIMARY KEY,\n path TEXT UNIQUE NOT NULL,\n name TEXT NOT NULL,\n file_type TEXT NOT NULL,\n extension TEXT NOT NULL,\n title TEXT,\n content TEXT,\n file_size INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n modified_at TEXT NOT NULL,\n indexed_at TEXT NOT NULL,\n content_hash TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_documents_path ON documents(path);\n CREATE INDEX IF NOT EXISTS idx_documents_file_type ON documents(file_type);\n CREATE INDEX IF NOT EXISTS idx_documents_modified_at ON documents(modified_at);\n CREATE INDEX IF NOT EXISTS idx_documents_content_hash ON documents(content_hash);\n ")}upsert(e){this.db.prepare("\n INSERT OR REPLACE INTO documents \n (id, path, name, file_type, extension, title, content, file_size, created_at, modified_at, indexed_at, content_hash)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ").run(e.id,e.path,e.name,e.fileType,e.extension,e.title||null,e.content,e.fileSize,e.createdAt.toISOString(),e.modifiedAt.toISOString(),e.indexedAt.toISOString(),e.contentHash)}getById(e){const t=this.db.prepare("SELECT * FROM documents WHERE id = ?").get(e);return t?this.rowToDocument(t):null}getByPath(e){const t=this.db.prepare("SELECT * FROM documents WHERE path = ?").get(e);return t?this.rowToDocument(t):null}getByPathAndTime(e,t){const i=this.db.prepare("SELECT * FROM documents WHERE path = ? AND modified_at = ?").get(e,t.toISOString());return i?this.rowToDocument(i):null}getByHash(e){const t=this.db.prepare("SELECT * FROM documents WHERE content_hash = ? LIMIT 1").get(e);return t?this.rowToDocument(t):null}getPathsByHash(e){return this.db.prepare("SELECT path FROM documents WHERE content_hash = ?").all(e).map(e=>e.path)}getByIds(e){if(0===e.length)return[];const t=e.map(()=>"?").join(",");return this.db.prepare(`SELECT * FROM documents WHERE id IN (${t})`).all(...e).map(e=>this.rowToDocument(e))}delete(e){this.db.prepare("DELETE FROM documents WHERE id = ?").run(e)}deleteByPath(e){this.db.prepare("DELETE FROM documents WHERE path = ?").run(e)}getAll(){return this.db.prepare("SELECT * FROM documents").all().map(e=>this.rowToDocument(e))}getAllPathsAndHashes(){const e=this.db.prepare("SELECT id, path, content_hash, modified_at FROM documents").all(),t=new Map;for(const i of e)t.set(i.path,{id:i.id,hash:i.content_hash,modifiedAt:new Date(i.modified_at)});return t}getStats(){const e=this.db.prepare("SELECT COUNT(*) as count FROM documents").get().count,t=this.db.prepare("SELECT file_type, COUNT(*) as count FROM documents GROUP BY file_type").all(),i={};for(const e of t)i[e.file_type]=e.count;const n=this.db.prepare("SELECT MAX(indexed_at) as last FROM documents").get(),s=n.last?new Date(n.last):void 0;let r=0;try{r=h.statSync(this.dbPath).size}catch{}return{totalDocuments:e,byType:i,directories:[],lastUpdated:s,indexSize:r}}clear(){this.db.exec("DELETE FROM documents")}close(){this.db.close()}rowToDocument(e){return{id:e.id,path:e.path,name:e.name,fileType:e.file_type,extension:e.extension,title:e.title,content:e.content,fileSize:e.file_size,createdAt:new Date(e.created_at),modifiedAt:new Date(e.modified_at),indexedAt:new Date(e.indexed_at),contentHash:e.content_hash}}};import p from"mammoth";import f from"pdf-parse";import y from"xlsx";import g from"jszip";import{parseString as x}from"xml2js";import*as w from"fs/promises";import*as S from"path";async function b(e){const t=await w.readFile(e,"utf-8"),i=t.split("\n").filter(e=>e.trim());let n=i[0]?.slice(0,100);return n?.startsWith("#")&&(n=n.replace(/^#+\s*/,"")),{content:t.trim(),title:n}}async function E(e){switch(S.extname(e).toLowerCase()){case".docx":case".doc":return async function(e){const t=S.extname(e).toLowerCase();if(".doc"===t)return{content:"",title:S.basename(e,t),metadata:{unsupported:!0,reason:"旧版 .doc 格式暂不支持,请转换为 .docx"}};try{const t=await w.readFile(e),i=(await p.extractRawText({buffer:t})).value.trim(),n=i.split("\n").filter(e=>e.trim()),s=n[0]?.slice(0,100);return{content:i,title:s}}catch(i){return{content:"",title:S.basename(e,t),metadata:{error:!0,reason:i instanceof Error?i.message:String(i)}}}}(e);case".pdf":return async function(e){const t=e.toLowerCase();if(t.includes(".xcassets")||t.includes(".imageset")||t.includes(".appiconset"))return{content:"",title:S.basename(e,".pdf"),metadata:{skipped:!0,reason:"资源文件,跳过解析"}};try{const t=console.warn;console.warn=(...e)=>{const i=e.join(" ");i.includes("Ignoring invalid character")||i.includes("Warning:")&&i.includes("hex string")||t.apply(console,e)};try{const i=await w.readFile(e),n=await f(i),s=n.text.trim();if(console.warn=t,!s)return{content:"",title:S.basename(e,".pdf"),metadata:{empty:!0}};const r=n.info?.Title||s.split("\n")[0]?.slice(0,100);return{content:s,title:r,metadata:{pages:n.numpages,info:n.info}}}finally{console.warn=t}}catch(t){return{content:"",title:S.basename(e,".pdf"),metadata:{error:!0,reason:t instanceof Error?t.message:String(t)}}}}(e);case".xlsx":case".xls":return async function(e){try{const t=y.readFile(e),i=[];for(const e of t.SheetNames){const n=t.Sheets[e],s=y.utils.sheet_to_txt(n);s.trim()&&i.push(`[${e}]\n${s}`)}return{content:i.join("\n\n"),title:t.SheetNames[0],metadata:{sheetCount:t.SheetNames.length,sheetNames:t.SheetNames}}}catch(t){return{content:"",title:S.basename(e,S.extname(e)),metadata:{error:!0,reason:t instanceof Error?t.message:String(t)}}}}(e);case".pptx":case".ppt":return async function(e){const t=S.basename(e,S.extname(e));try{const i=S.isAbsolute(e)?e:S.resolve(e),n=await w.readFile(i),s=await g.loadAsync(n),r=[],o=[];s.forEach(e=>{e.startsWith("ppt/slides/slide")&&e.endsWith(".xml")&&o.push(e)}),o.sort((e,t)=>{const i=e.match(/slide(\d+)\.xml/),n=t.match(/slide(\d+)\.xml/);return(i?parseInt(i[1]):0)-(n?parseInt(n[1]):0)});for(const e of o){const t=e.match(/slide(\d+)\.xml/),i=t?parseInt(t[1]):0,n=s.file(e);if(!n)continue;const o=await n.async("string");if(!o)continue;const a=await new Promise((e,t)=>{x(o,{explicitArray:!1,mergeAttrs:!0,trim:!0,normalize:!0},(i,n)=>{i?t(i):e(n)})}),c=(e,t=!1)=>{const i=[];if("string"==typeof e){if(t){const t=e.trim();t&&t.length>0&&i.push(t)}}else if(Array.isArray(e))for(const n of e)i.push(...c(n,t));else if(e&&"object"==typeof e){if(e["a:t"]){const t=c(e["a:t"],!0);i.push(...t)}for(const n of Object.values(e))i.push(...c(n,t))}return i},d=c(a).filter(e=>{const t=e.trim();if(!t||0===t.length)return!1;if(/^-?\d+$/.test(t))return!1;if(/^\{[0-9A-F-]+\}$/i.test(t))return!1;if(t.startsWith("http://")||t.startsWith("https://"))return!1;if(t.includes("xmlns")||t.includes("schemas.openxmlformats"))return!1;if(["ctrTitle","zh-CN","en-US","body","title","subtitle","connsite","islide","组合","矩形","任意多边形","文本框","文本占位符","图文框","直接连接符","椭圆","rId","accent","minor","lt","ctr","rect","square","roundRect","ellipse","none","solid","horz","Bullet","noStrike","Arial","方正","Component","Icon","Group","Presenter","subTitle","quarter","Shape","Number","Text","TextBox","Picture","QR","adj","Thank you","IMPORTANT NOTE","Template file","20XX.XX.XX"].some(e=>t.includes(e)))return!1;if(/^\*\/.*[wh]$/.test(t))return!1;if(/^connsite[XY]\d+$/.test(t))return!1;if(/^[0-9A-F]{6}$/i.test(t))return!1;if(1===t.length)return!1;if(/^[a-zA-Z-]+$/.test(t)&&t.length<3)return!1;const i=/[\u4e00-\u9fa5]/.test(t),n=(t.match(/[\u4e00-\u9fa5]/g)||[]).length;return!(i&&n<2||!i&&t.length<3)});if(d.length>0){const e=[...new Set(d)],t=[];for(let i=0;i<e.length;i++){const n=e[i].trim();if(!n)continue;const s=/[\u4e00-\u9fa5]/.test(n);if(n.length<=3&&s&&t.length>0){const e=t.length-1,i=t[e];if(/[\u4e00-\u9fa5]/.test(i)){t[e]=i+n;continue}}if((/^\d+%?$/.test(n)||/^\d+\.\d+$/.test(n))&&t.length>0){const e=t.length-1,i=t[e];if(/[\u4e00-\u9fa5]/.test(i)){t[e]=i+n;continue}}t.push(n)}const n=t.join("\n\n").trim();n&&r.push({number:i,content:n})}}if(0===r.length)return{content:`[PPT文档] ${t}`,title:t,metadata:{error:!1,slides:[]}};return{content:r.map((e,t)=>`## 幻灯片 ${e.number}${0===t?"(封面)":""}\n\n`+e.content).join("\n\n---\n\n"),title:r[0]?.content.split("\n")[0]?.slice(0,100)||t,metadata:{slides:r.map(e=>({number:e.number,content:e.content})),totalSlides:r.length}}}catch(e){return{content:`[PPT文档] ${t}`,title:t,metadata:{error:!0,reason:e instanceof Error?e.message:String(e)}}}}(e);case".txt":case".md":case".rtf":case".text":return b(e);default:try{return await b(e)}catch{return{content:"",title:S.basename(e)}}}}function T(e){const t=S.extname(e).toLowerCase();return[".docx",".doc",".pdf",".xlsx",".xls",".pptx",".ppt",".txt",".md",".rtf"].includes(t)}function _(e){const t=S.extname(e).toLowerCase();return[".docx",".doc"].includes(t)?"word":".pdf"===t?"pdf":[".xlsx",".xls"].includes(t)?"excel":[".pptx",".ppt"].includes(t)?"powerpoint":[".txt",".md",".rtf"].includes(t)?"text":"unknown"}var D=null,I="doubao-embedding-vision-250615",N=!1,v="https://ark.cn-beijing.volces.com/api/v3/embeddings/multimodal",O=1024,P={document:"将以下文档内容转换为语义向量,用于后续的相似度检索",query:"将以下搜索查询转换为语义向量,用于检索相关文档"};async function F(e,t="doubao-embedding-vision-250615",i=1024){const n=e||process.env.ARK_API_KEY;if(!n)throw new Error("缺少 ARK_API_KEY,请通过参数或环境变量提供");D=n,I=t,O=i,N=!0}function A(e){return new Promise(t=>setTimeout(t,e))}var k=3,C=1e3,z=1e4;async function j(e,t){N&&D||await F();const i=t?.maxLength??8192,n=t?.instructions,s=e.slice(0,i),r={model:I,encoding_format:"float",dimensions:O,input:[{type:"text",text:s}]};n&&(r.instructions=n);let o=null;for(let e=0;e<=k;e++)try{const t=await fetch(v,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${D}`},body:JSON.stringify(r)});if(t.ok){const e=await t.json();if(!e.data?.embedding)throw new Error(`豆包 Embedding API 返回格式错误: ${JSON.stringify(e)}`);return e.data.embedding}if(429===t.status&&e<k){const t=Math.min(C*Math.pow(2,e),z);await A(t);continue}const i=await t.text();throw new Error(`豆包 Embedding API 错误 (${t.status}): ${i}`)}catch(t){if(o=t instanceof Error?t:new Error(String(t)),e<k){const t=Math.min(C*Math.pow(2,e),z);await A(t);continue}}throw o||new Error("Embedding 请求失败")}async function L(e,t=8192){return j(e,{maxLength:t,instructions:P.document})}async function M(e,t=8192){return j(e,{maxLength:t,instructions:P.query})}async function R(e,t=8192){N&&D||await F();const i=[];for(const n of e){const e=await L(n,t);i.push(e)}return i}async function $(e,t=5,i=8192){N&&D||await F();const n=new Array(e.length),s=[];for(let r=0;r<e.length;r++){const o=(async t=>{n[t]=await L(e[t],i)})(r);if(s.push(o),s.length>=t){await Promise.race(s);const e=s.filter(e=>{const t=e;return"fulfilled"===t._state||"rejected"===t._state});for(const t of e){const e=s.indexOf(t);e>-1&&s.splice(e,1)}}}return await Promise.all(s),n}async function J(e){N&&D||await F();const t=await fetch(v,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${D}`},body:JSON.stringify({model:I,encoding_format:"float",dimensions:O,input:[{type:"image_url",image_url:{url:e}}]})});if(!t.ok){const e=await t.text();throw new Error(`豆包 Embedding API 错误 (${t.status}): ${e}`)}const i=await t.json();if(!i.data?.embedding)throw new Error(`豆包 Embedding API 返回格式错误: ${JSON.stringify(i)}`);return i.data.embedding}async function W(e){N&&D||await F();const t=await fetch(v,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${D}`},body:JSON.stringify({model:I,encoding_format:"float",dimensions:O,input:[{type:"video_url",video_url:{url:e}}]})});if(!t.ok){const e=await t.text();throw new Error(`豆包 Embedding API 错误 (${t.status}): ${e}`)}const i=await t.json();if(!i.data?.embedding)throw new Error(`豆包 Embedding API 返回格式错误: ${JSON.stringify(i)}`);return i.data.embedding}async function B(e){N&&D||await F();const t=await fetch(v,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${D}`},body:JSON.stringify({model:I,encoding_format:"float",dimensions:O,input:e})});if(!t.ok){const e=await t.text();throw new Error(`豆包 Embedding API 错误 (${t.status}): ${e}`)}const i=await t.json();if(!i.data?.embedding)throw new Error(`豆包 Embedding API 返回格式错误: ${JSON.stringify(i)}`);return i.data.embedding}function X(){return O}function U(e){O=e}function q(){D=null,N=!1}function H(){return N&&null!==D}import{fdir as V}from"fdir";import*as K from"path";import*as Y from"os";import*as G from"path";import*as Q from"os";var Z={allowed:[".docx",".doc",".pdf",".xlsx",".xls",".pptx",".ppt",".txt",".md",".rtf"],excluded:[".doc"]},ee={excludedNames:["node_modules"],buildDirs:["__pycache__","build","dist","out","target"],resourceDirs:["Assets.xcassets"],windowsSystemDirs:["Windows","Program Files","Program Files (x86)","ProgramData","System Volume Information","AppData","Library","$RECYCLE.BIN"],excludedPaths:[]},te={excludeHidden:!0,excludedPatterns:[],excludedPathContains:[".imageset",".xcassets",".appiconset"]},ie={windows:{onlyUserDirs:!0,excludedRootDirs:["Windows","Program Files","Program Files (x86)","ProgramData","System Volume Information"]}},ne={extensions:Z,directories:ee,files:te,paths:ie},se=class{rules;excludedDirNamesSet;allowedExtensionsSet;excludedExtensionsSet;constructor(e=ne){this.rules=e,this.excludedDirNamesSet=new Set([...e.directories.excludedNames,...e.directories.buildDirs,...e.directories.resourceDirs].map(e=>e.toLowerCase())),this.allowedExtensionsSet=new Set(e.extensions.allowed.map(e=>e.toLowerCase())),this.excludedExtensionsSet=new Set(e.extensions.excluded.map(e=>e.toLowerCase()))}isExtensionAllowed(e){const t=e.toLowerCase();return!this.excludedExtensionsSet.has(t)&&this.allowedExtensionsSet.has(t)}shouldExcludeDirectory(e){return!(!this.rules.files.excludeHidden||!e.startsWith("."))||this.excludedDirNamesSet.has(e.toLowerCase())}shouldExcludeFile(e,t){if(this.rules.files.excludeHidden&&t.startsWith("."))return!0;const i=e.replace(/[/\\]/g,G.sep),n=i.toLowerCase();for(const e of this.rules.files.excludedPathContains)if(n.includes(e.toLowerCase()))return!0;const s=i.split(G.sep).filter(e=>e.length>0);for(const e of this.rules.directories.excludedPaths){const t=e.replace(/[/\\]/g,G.sep);if(s.includes(t)||s.some(e=>e===G.basename(t)))return!0}return"win32"===Q.platform()&&this.shouldExcludeWindowsPath(i)}shouldExcludeWindowsPath(e){const t=this.rules.paths.windows,i=e.match(/^([A-Z]:)\\([^\\]+)/);if(i){const e=i[1],n=i[2];if(this.rules.directories.windowsSystemDirs.includes(n))return!0;if(t.onlyUserDirs&&"C:"===e&&"Users"!==n)return!0}return!1}getExcludedDirectoryNames(){return new Set([...this.rules.directories.excludedNames,...this.rules.directories.buildDirs,...this.rules.directories.resourceDirs])}updateRules(e){this.rules={...this.rules,...e,extensions:{...this.rules.extensions,...e.extensions},directories:{...this.rules.directories,...e.directories},files:{...this.rules.files,...e.files},paths:{...this.rules.paths,...e.paths}},this.excludedDirNamesSet=new Set([...this.rules.directories.excludedNames,...this.rules.directories.buildDirs,...this.rules.directories.resourceDirs].map(e=>e.toLowerCase())),this.allowedExtensionsSet=new Set(this.rules.extensions.allowed.map(e=>e.toLowerCase())),this.excludedExtensionsSet=new Set(this.rules.extensions.excluded.map(e=>e.toLowerCase()))}getRules(){return this.rules}};function re(e){const t=e?{extensions:{...Z,...e.extensions},directories:{...ee,...e.directories},files:{...te,...e.files},paths:{...ie,...e.paths}}:ne;return new se(t)}function oe(e){return e.startsWith("~")?K.join(Y.homedir(),e.slice(1)):e}async function ae(e,t){const i=t?.onProgress,n=t?.customRules||{};if(t?.excludeDirs&&t.excludeDirs.length>0){const e=n.directories?.excludedPaths||[];n.directories={...n.directories,excludedNames:n.directories?.excludedNames||[],excludedPaths:[...e,...t.excludeDirs],windowsSystemDirs:n.directories?.windowsSystemDirs||[],buildDirs:n.directories?.buildDirs||[],resourceDirs:n.directories?.resourceDirs||[]}}if(t?.extensions&&t.extensions.length>0){const e=n.extensions?.allowed||[];n.extensions={...n.extensions,allowed:[...e,...t.extensions],excluded:n.extensions?.excluded||[]}}const s=re(n),r=[];let o=0;for(const t of e){const e=oe(t);i?.({scanned:o,currentDir:e});try{let t=0,n=Date.now();const a=2e3,c=(new V).withFullPaths().exclude(e=>s.shouldExcludeDirectory(e)).filter((r,c)=>{if(c)return!1;t++,o++;const d=Date.now();(t%5e3==0||d-n>=a)&&(i?.({scanned:o,currentDir:e}),n=d);const l=K.basename(r);if(s.shouldExcludeFile(r,l))return!1;const u=K.extname(r);return s.isExtensionAllowed(u)}),d=await c.crawl(e).withPromise();r.push(...d),i?.({scanned:o,currentDir:e})}catch(e){}}return r}function ce(){const e=Y.homedir(),t=Y.platform(),i=[K.join(e,"Documents"),K.join(e,"Desktop"),K.join(e,"Downloads"),K.join(e,"Pictures"),K.join(e,"Music")];return"darwin"===t?i.push(K.join(e,"Movies")):i.push(K.join(e,"Videos")),i}import*as de from"crypto";import*as le from"fs/promises";import*as ue from"path";async function he(e){const t=await le.stat(e),i=`${e}:${t.size}:${t.mtime.getTime()}`;return de.createHash("md5").update(i).digest("hex").slice(0,16)}function me(){return de.randomUUID()}function pe(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:e<1073741824?`${(e/1048576).toFixed(1)} MB`:`${(e/1073741824).toFixed(1)} GB`}function fe(e){return e.toISOString().split("T")[0]}function ye(e){const t=ue.extname(e).toLowerCase();return[".docx",".doc",".rtf"].includes(t)?i.DOCUMENT:".pdf"===t?i.PDF:[".xlsx",".xls"].includes(t)||[".pptx",".ppt"].includes(t)?i.DOCUMENT:[".txt",".md"].includes(t)?i.TEXT:i.FILE}function ge(e,t,i=200){const n=e.replace(/\s+/g," ").trim(),s=t.toLowerCase().split(/\s+/).filter(e=>e.length>1),r=n.toLowerCase();let o=-1;for(const e of s){const t=r.indexOf(e);-1!==t&&(-1===o||t<o)&&(o=t)}if(-1===o){return n.slice(0,i)+(n.length>i?"...":"")}const a=Math.floor(.3*i),c=Math.floor(.7*i),d=Math.max(0,o-a),l=Math.min(n.length,o+c);let u=n.slice(d,l);return d>0&&(u="..."+u),l<n.length&&(u+="..."),u}function xe(e){return new Promise(t=>setTimeout(t,e))}var we=Symbol.for("ai-search-progress-listeners");function Se(){const e=globalThis;return e[we]||(e[we]=new Set),e[we]}function be(e){Se().add(e)}function Ee(e){Se().delete(e)}import*as Te from"path";import _e from"chokidar";var De=class{constructor(e){this.deps=e}watchers=new Map;debounceTimers=new Map;isSupportedFile(e){const t=Te.extname(e).toLowerCase();return(this.deps.config.extensions||n.extensions||[]).includes(t)}shouldExclude(e){return(this.deps.config.excludeDirs||n.excludeDirs||[]).some(t=>e.includes(t))}watch(e,t){const{ignoreInitial:i=!0,debounce:n=1e3,onEvent:s}=t||{};this.watchers.has(e)&&this.unwatch(e);const r=(e,t)=>{const i=`${e}:${t}`,r=this.debounceTimers.get(i);r&&clearTimeout(r);const o=setTimeout(async()=>{this.debounceTimers.delete(i);const n={type:e,path:t,timestamp:new Date};s?.(n);try{"add"===e||"change"===e?this.isSupportedFile(t)&&!this.shouldExclude(t)&&(await this.deps.indexFile(t),await this.deps.fullTextIndex.save()):"unlink"===e&&await this.deps.removeFile(t)}catch(e){}},n);this.debounceTimers.set(i,o)},o=_e.watch(e,{ignored:e=>!!this.shouldExclude(e)||!!Te.extname(e)&&!this.isSupportedFile(e),ignoreInitial:i,persistent:!0,awaitWriteFinish:{stabilityThreshold:500,pollInterval:100}});o.on("add",e=>r("add",e)),o.on("change",e=>r("change",e)),o.on("unlink",e=>r("unlink",e)),o.on("unlinkDir",e=>{const t={type:"unlinkDir",path:e,timestamp:new Date};s?.(t)}),this.watchers.set(e,o)}unwatch(e){const t=this.watchers.get(e);t&&(t.close(),this.watchers.delete(e));for(const[t,i]of this.debounceTimers.entries())t.includes(e)&&(clearTimeout(i),this.debounceTimers.delete(t))}unwatchAll(){for(const e of this.watchers.keys())this.unwatch(e)}getWatchedDirectories(){return Array.from(this.watchers.keys())}clearTimers(){for(const e of this.debounceTimers.values())clearTimeout(e);this.debounceTimers.clear()}};import*as Ie from"fs/promises";async function Ne(e){const t=e.metaStore.getAll();let i=0,n=0;for(const s of t)try{await Ie.access(s.path);const t=await Ie.stat(s.path),i=await he(s.path);s.contentHash===i&&s.modifiedAt.getTime()===t.mtime.getTime()||(await e.indexFile(s.path),n++)}catch{await e.removeFile(s.path),i++}return await e.fullTextIndex.save(),{removed:i,updated:n}}function ve(e){return Array.from(e.indexErrors.values())}import*as Oe from"fs/promises";import*as Pe from"path";async function Fe(e,t){await Oe.mkdir(t,{recursive:!0});const i=await Oe.readdir(e,{withFileTypes:!0});for(const n of i){const i=Pe.join(e,n.name),s=Pe.join(t,n.name);n.isDirectory()?await Fe(i,s):await Oe.copyFile(i,s)}}async function Ae(e){const t=await Oe.readdir(e,{withFileTypes:!0});let i=0;for(const n of t){const t=Pe.join(e,n.name);if(n.isDirectory())i+=await Ae(t);else{i+=(await Oe.stat(t)).size}}return i}import*as ke from"path";import*as Ce from"fs/promises";import{createHash as ze}from"crypto";var je={chunkSize:800,overlap:100,minChunkSize:100},Le=["\n\n\n","\n\n","\n","。","!","?",";",". ","! ","? ","; ",",",", "," "];function Me(e,t){const i={...je,...t};if(!e.trim())return[];if(e.length<=i.chunkSize)return[{content:e,index:0,startOffset:0,endOffset:e.length}];return function(e,t,i){const n=[];let s=0;for(let r=0;r<e.length;r++){let o=e[r].trim();if(!o){const i=t.indexOf(e[r],s);i>=0&&(s=i+e[r].length);continue}if(o.length<i.minChunkSize&&n.length>0){const t=n[n.length-1];if(t.content.length+o.length<1.5*i.chunkSize){t.content+="\n"+o,t.endOffset=s+e[r].length;continue}}const a=t.indexOf(o.slice(0,50),Math.max(0,s-10)),c=a>=0?a:s;if(i.overlap>0&&n.length>0){const e=n[n.length-1].content.slice(-i.overlap),t=$e(e);t>0&&(o=e.slice(t)+"\n"+o)}n.push({content:o,index:n.length,startOffset:c,endOffset:c+o.length}),s=c+e[r].length}return n}(Re(e,i.chunkSize,0),e,i)}function Re(e,t,i){if(e.length<=t)return[e];for(;i<Le.length;){const n=Le[i],s=e.split(n);if(s.length>1){const e=[];let r="";for(const o of s){const s=r?r+n+o:o;s.length<=t?r=s:r?(e.push(r),o.length>t?(e.push(...Re(o,t,i+1)),r=""):r=o):e.push(...Re(o,t,i+1))}return r&&e.push(r),e}i++}return function(e,t){const i=[];let n=0;for(;n<e.length;)i.push(e.slice(n,n+t)),n+=t;return i}(e,t)}function $e(e){const t=["。","!","?",". ","! ","? ","\n"];for(const i of t){const t=e.lastIndexOf(i);if(t>=0)return t+i.length}return 0}function Je(e){if(0===e.length)return{count:0,avgSize:0,minSize:0,maxSize:0,totalSize:0};const t=e.map(e=>e.content.length),i=t.reduce((e,t)=>e+t,0);return{count:e.length,avgSize:Math.round(i/e.length),minSize:Math.min(...t),maxSize:Math.max(...t),totalSize:i}}var We={concurrency:50,chunk:{chunkSize:8e3,overlap:500,minChunkSize:500}},Be=class{config;onProgress;storeCallback;skipCheck;cancelled=!1;stats={totalFiles:0,completed:0,stored:0,skipped:0,failed:0,totalTime:0};constructor(e){this.config={...We,...e}}cancel(){this.cancelled=!0}isCancelled(){return this.cancelled}async processFile(e){try{if(this.skipCheck){const t=await Ce.stat(e);if(this.skipCheck(e,t.mtime))return void this.stats.skipped++}const t=await E(e);if(!t.content.trim())return void this.stats.skipped++;if(this.cancelled)return;const i=await async function(e){const t=await Ce.readFile(e);return ze("md5").update(t).digest("hex")}(e);if(this.cancelled)return;const n=Me(t.content,this.config.chunk),s=n.length,r=n.map(e=>L(e.content)),o=await Promise.all(r);if(this.cancelled)return;const a=n.map(async(n,r)=>{if(this.cancelled)return!1;const a={filePath:e,content:n.content,title:t.title||ke.basename(e),contentHash:i,vector:o[r],chunkIndex:r,totalChunks:s};return(await this.storeCallback(a)).stored}),c=await Promise.all(a);c.filter(Boolean).length>0?this.stats.stored++:this.stats.skipped++}catch(e){this.stats.failed++}}async run(e,t,i,n){const s=Date.now();this.cancelled=!1,this.stats={totalFiles:e.length,completed:0,stored:0,skipped:0,failed:0,totalTime:0},this.onProgress=i,this.storeCallback=t,this.skipCheck=n;const r=[];let o=0;for(;(o<e.length||r.length>0)&&!this.cancelled;){for(;o<e.length&&r.length<this.config.concurrency&&!this.cancelled;){const t=e[o++],i=this.processFile(t).then(()=>{this.cancelled||(this.stats.completed++,this.reportProgress(t))}).finally(()=>{const e=r.indexOf(i);e>-1&&r.splice(e,1)});r.push(i)}r.length>0&&await Promise.race(r)}return r.length>0&&await Promise.all(r),this.stats.totalTime=Date.now()-s,this.cancelled,this.stats}reportProgress(e){const{completed:t,totalFiles:i}=this.stats;this.onProgress?.({indexed:t,total:i,currentFile:e,stage:t===i?"done":"storing"})}};function Xe(e){return new Be(e)}var Ue=class{config;vectorStore;fullTextIndex;metaStore;initialized=!1;directoryWatcher;indexErrors=new Map;maxRetries=3;indexingLocks=new Set;currentPipeline=null;constructor(e){this.config={...n,...e};const i=this.config.dataDir;this.vectorStore=new r(t.join(i,"vectors"),"documents",X()),this.fullTextIndex=new d(i),this.metaStore=new m(i),this.directoryWatcher=new De({config:this.config,fullTextIndex:this.fullTextIndex,indexFile:e=>this.indexFile(e),removeFile:e=>this.removeFile(e)})}async init(){this.initialized||(await F(this.config.arkApiKey,this.config.embeddingModel,this.config.embeddingDimension),await this.vectorStore.init(),await this.fullTextIndex.init(),this.initialized=!0)}async indexFile(i,s=0){await this.ensureInitialized();const r=t.normalize(i);if(!this.indexingLocks.has(r)){this.indexingLocks.add(r);try{const s=await e.stat(i);if(s.size>(this.config.maxFileSize||n.maxFileSize))return;const r=this.metaStore.getByPath(i);if(r){const e=r.modifiedAt.getTime()===s.mtime.getTime(),t=r.fileSize===s.size;if(e&&t)return;if(t){const e=await he(i);if(r.contentHash===e)return r.modifiedAt=s.mtime,r.fileSize=s.size,void this.metaStore.upsert(r)}}const o=await he(i),a=this.metaStore.getByHash(o);if(a&&a.path!==i){const e=me(),n={id:e,path:i,name:t.basename(i),fileType:ye(i),extension:t.extname(i).toLowerCase(),title:a.title,content:a.content,fileSize:s.size,createdAt:s.birthtime,modifiedAt:s.mtime,indexedAt:new Date,contentHash:o};this.metaStore.upsert(n);const r=await this.vectorStore.getById(a.id);return r&&await this.vectorStore.update({id:e,vector:r}),void this.fullTextIndex.update({id:e,title:n.title||n.name,content:n.content})}const c=await E(i);if(!c.content.trim()){if(c.metadata?.error){const e=c.metadata.reason;e?.includes("bad XRef")||e?.includes("FormatError")||e?.includes("XRef")}return}const d=await L(c.content),l=r?.id||me(),u={id:l,path:i,name:t.basename(i),fileType:ye(i),extension:t.extname(i).toLowerCase(),title:c.title,content:c.content,fileSize:s.size,createdAt:s.birthtime,modifiedAt:s.mtime,indexedAt:new Date,contentHash:o};this.metaStore.upsert(u),await this.vectorStore.update({id:l,vector:d}),this.fullTextIndex.update({id:l,title:u.title||u.name,content:u.content})}catch(e){const t=e instanceof Error?e.message:String(e),n=e instanceof Error?e.stack:String(e),r=t.includes("bad XRef")||t.includes("FormatError")||t.includes("Invalid PDF")||t.includes("XRef")||n?.includes("pdf-parse")||n?.includes("pdf.js"),o={filePath:i,error:t,retryCount:s,timestamp:new Date};return this.indexErrors.set(i,o),!r&&s<this.maxRetries?(await xe(1e3*(s+1)),this.indexFile(i,s+1)):void 0}finally{this.indexingLocks.delete(r)}}}async indexDirectory(i,n,s){await this.ensureInitialized();const r=Date.now(),o=e=>{n?.(e),function(e){Se().forEach(t=>{try{t(e)}catch(e){}})}(e)};o({indexed:0,total:0,stage:"scanning"});Date.now();const a=this.metaStore.getAll(),c=new Set(a.filter(e=>e.path.startsWith(i)).map(e=>e.path));Date.now();let d=0;const l=await ae([i],{excludeDirs:this.config.excludeDirs,extensions:this.config.extensions,maxFileSize:this.config.maxFileSize,directories:[i],onProgress:e=>{d=e.scanned,o({indexed:e.scanned,total:0,currentFile:e.currentDir,stage:"scanning"})}});Date.now();o({indexed:0,total:0,stage:"parsing",currentFile:"正在分析文件变更..."});const u=new Set(l),h=new Set(l.map(e=>t.normalize(e))),m=(new Set(Array.from(c).map(e=>t.normalize(e))),[]);for(const e of l){this.metaStore.getByPath(e);m.push(e)}const p=[];for(const e of c){const i=t.normalize(e);h.has(i)||u.has(e)||p.push(e)}let f=0;if(p.length>0){f=(await this.removeFiles(p)).success}const y=new Map;for(const e of a)e.path.startsWith(i)&&e.modifiedAt&&y.set(e.path,e.modifiedAt.getTime());this.currentPipeline=Xe(s);const g=new Map,x=await this.currentPipeline.run(m,async i=>{try{const n=i.chunkIndex??0,s=i.totalChunks??1;if(0===n){const n=this.metaStore.getByPath(i.filePath);if(n&&n.contentHash===i.contentHash)return{stored:!1,skipped:!0};if(n&&n.chunkCount)for(let e=0;e<n.chunkCount;e++){const t=`${n.id}_${e}`;await this.vectorStore.delete(t),this.fullTextIndex.remove(t)}const r=await e.stat(i.filePath),o=n?.id||me();g.set(i.filePath,o);const a={id:o,path:i.filePath,name:t.basename(i.filePath),fileType:ye(i.filePath),extension:t.extname(i.filePath).toLowerCase(),title:i.title,content:i.content,fileSize:r.size,createdAt:r.birthtime,modifiedAt:r.mtime,indexedAt:new Date,contentHash:i.contentHash,chunkCount:s};this.metaStore.upsert(a)}const r=g.get(i.filePath)||this.metaStore.getByPath(i.filePath)?.id;if(!r)return{stored:!1,skipped:!1};const o=`${r}_${n}`;return await this.vectorStore.update({id:o,vector:i.vector}),this.fullTextIndex.update({id:o,title:i.title,content:i.content}),{stored:!0,skipped:!1}}catch{return{stored:!1,skipped:!1}}},o,(e,t)=>{const i=y.get(e);return!!i&&t.getTime()<=i}),w=this.currentPipeline?.isCancelled()??!1;this.currentPipeline=null,await this.fullTextIndex.save();Date.now();o(w?{indexed:x.completed,total:m.length,stage:"cancelled"}:{indexed:m.length,total:m.length,stage:"done"})}cancelIndexing(){return!!this.currentPipeline&&(this.currentPipeline.cancel(),!0)}isIndexing(){return null!==this.currentPipeline&&!this.currentPipeline.isCancelled()}async indexDefaultDirectories(e){const t=this.config.indexDirectories||ce();for(const i of t)await this.indexDirectory(i,e)}async search(e,t){await this.ensureInitialized();const i=t?.limit||10,n=t?.mode||"hybrid";let s,r=[],o=[];if("semantic"===n||"hybrid"===n){const t=await M(e);r=await this.vectorStore.search(t,2*i)}if("keyword"!==n&&"hybrid"!==n||(o=this.fullTextIndex.search(e,2*i)),"hybrid"===n){const e=r.length>0,t=o.length>0;s=e&&t?function(e,t=60){const i=new Map;for(const{results:n,weight:s=1}of e)for(let e=0;e<n.length;e++){const r=n[e].id,o=s/(t+e+1);i.set(r,(i.get(r)||0)+o)}const n=Array.from(i.entries()).sort((e,t)=>t[1]-e[1]);if(0===n.length)return[];const s=n[0][1];return n.map(([e,t])=>({id:e,score:s>0?t/s:0}))}([{results:r.map(e=>({id:e.id,score:Math.max(0,1-e.distance)})),weight:.6},{results:o,weight:.4}]):e?this.normalizeVectorScores(r):t?o:[]}else s="semantic"===n?this.normalizeVectorScores(r):o;const a=e=>{const t=e.lastIndexOf("_"),i=parseInt(e.slice(t+1),10);return{docId:e.slice(0,t),chunkIndex:i}},c=s.slice(0,2*i).map(e=>e.id),d=[...new Set(c.map(e=>a(e).docId))],l=this.metaStore.getByIds(d),u=new Map(l.map(e=>[e.id,e])),h=new Map(s.map(e=>[e.id,e.score])),m=[],p=new Set;for(const s of c){if(m.length>=i)break;const{docId:r,chunkIndex:o}=a(s);if(p.has(r))continue;const c=u.get(r);if(!c)continue;const d=h.get(s)||0;if(!this.matchesFilters(c,t))continue;p.add(r);const l=this.fullTextIndex.getContent(s);m.push({id:c.path,name:c.name,type:c.fileType,size:pe(c.fileSize),dateModified:fe(c.modifiedAt),url:`file://${c.path}`,thumbnailUrl:void 0,score:d,snippet:ge(l||c.content,e),matchType:"hybrid"===n?"hybrid":"semantic"===n?"semantic":"keyword",chunkIndex:o})}return m}matchesFilters(e,t){if(!t)return!0;const i=t.combineMode||"AND",n=[];if(t.fileTypes&&n.push(t.fileTypes.includes(e.fileType)),t.dateRange){let i=!0;t.dateRange.start&&e.modifiedAt<t.dateRange.start&&(i=!1),t.dateRange.end&&e.modifiedAt>t.dateRange.end&&(i=!1),n.push(i)}if(t.directories&&t.directories.length>0){const i=t.directories.some(t=>e.path.startsWith(t));n.push(i)}if(t.sizeRange){let i=!0;void 0!==t.sizeRange.min&&e.fileSize<t.sizeRange.min&&(i=!1),void 0!==t.sizeRange.max&&e.fileSize>t.sizeRange.max&&(i=!1),n.push(i)}if(t.fileNamePattern){const i=this.wildcardToRegex(t.fileNamePattern);n.push(i.test(e.name))}if(t.titleContains){const i=e.title||e.name;n.push(i.toLowerCase().includes(t.titleContains.toLowerCase()))}return 0===n.length||("AND"===i?n.every(e=>e):n.some(e=>e))}wildcardToRegex(e){const t=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".");return new RegExp(`^${t}$`,"i")}normalizeVectorScores(e){if(0===e.length)return[];const t=e.map(e=>({id:e.id,rawScore:1/(1+e.distance)})),i=Math.max(...t.map(e=>e.rawScore));return t.map(e=>({id:e.id,score:i>0?e.rawScore/i:0}))}async removeFile(e){const t=this.metaStore.getByPath(e);t&&(await this.vectorStore.delete(t.id),this.fullTextIndex.remove(t.id),this.metaStore.deleteByPath(e))}getStats(){const e=this.metaStore.getStats();return e.directories=this.config.indexDirectories||ce(),e}async clear(){this.metaStore.clear(),this.fullTextIndex.clear()}async save(){await this.fullTextIndex.save()}async indexFiles(e,t){return!1!==this.config.enableParallelIndexing&&e.length>1?this.indexFilesParallel(e,t):this.indexFilesSerial(e,t)}async indexFilesSerial(e,t){await this.ensureInitialized();const i={success:0,failed:0,errors:[]},n=e.length;t?.({indexed:0,total:n,stage:"parsing"});for(let s=0;s<e.length;s++){const r=e[s];try{await this.indexFile(r),i.success++}catch(e){i.failed++,i.errors.push({path:r,error:e instanceof Error?e.message:String(e)})}t?.({indexed:s+1,total:n,currentFile:r,stage:s===e.length-1?"done":"parsing"}),s%10==0&&await xe(10)}return await this.fullTextIndex.save(),i}async indexFilesParallel(e,t,i){await this.ensureInitialized();const n={success:0,failed:0,errors:[]},s=e.length,r=i||this.config.indexConcurrency||5;t?.({indexed:0,total:s,stage:"parsing"});let o=0,a=0;const c=new Set,d=async()=>{for(;a<s;){const i=a++,d=e[i],l=(async()=>{try{await this.indexFile(d),n.success++}catch(e){n.failed++,n.errors.push({path:d,error:e instanceof Error?e.message:String(e)})}finally{o++,t?.({indexed:o,total:s,currentFile:d,stage:o===s?"done":"parsing"})}})();c.add(l),l.finally(()=>{c.delete(l)}),c.size>=r&&await Promise.race(c)}},l=Array.from({length:Math.min(r,s)},()=>d());return await Promise.all(l),await Promise.all(c),await this.fullTextIndex.save(),n}async removeFiles(e){const t={success:0,failed:0,errors:[]};let i=0;for(const n of e)try{if(!this.metaStore.getByPath(n)){i++;continue}await this.removeFile(n),t.success++}catch(e){t.failed++,t.errors.push({path:n,error:e instanceof Error?e.message:String(e)})}return i>0&&t.errors.length<10&&t.errors.push({path:`[${i} 个文件不存在于索引中]`,error:"文件可能已被删除或路径不匹配"}),t}async updateFiles(e,t){return await this.removeFiles(e),this.indexFiles(e,t)}watchDirectory(e,t){this.directoryWatcher.watch(e,t)}unwatchDirectory(e){this.directoryWatcher.unwatch(e)}unwatchAll(){this.directoryWatcher.unwatchAll()}getWatchedDirectories(){return this.directoryWatcher.getWatchedDirectories()}getMaintenanceDeps(){return{metaStore:this.metaStore,vectorStore:this.vectorStore,fullTextIndex:this.fullTextIndex,indexErrors:this.indexErrors,indexFile:e=>this.indexFile(e),removeFile:e=>this.removeFile(e),indexFiles:e=>this.indexFiles(e)}}async cleanup(){return await this.ensureInitialized(),Ne(this.getMaintenanceDeps())}async optimize(){return await this.ensureInitialized(),async function(e){await Ne(e),await e.fullTextIndex.save()}(this.getMaintenanceDeps())}async healthCheck(){return await this.ensureInitialized(),async function(e){const t=e.metaStore.getAll();let i=0,n=0;const s={meta:!0,vectors:!0,fulltext:!0};try{const t=e.metaStore.getStats();s.meta=t.totalDocuments>=0}catch{s.meta=!1}try{await e.vectorStore.search(new Array(X()).fill(0),1),s.vectors=!0}catch{s.vectors=!1}try{e.fullTextIndex.search("test",1),s.fulltext=!0}catch{s.fulltext=!1}const r=Math.min(100,t.length),o=t.slice(0,r);for(const e of o)try{await Ie.access(e.path);const t=await Ie.stat(e.path),i=await he(e.path);e.contentHash===i&&e.modifiedAt.getTime()===t.mtime.getTime()||n++}catch{i++}if(t.length>r){const e=i/r;i=Math.floor(e*t.length);const s=n/r;n=Math.floor(s*t.length)}return{healthy:s.meta&&s.vectors&&s.fulltext&&0===i&&0===n&&0===e.indexErrors.size,totalDocuments:t.length,invalidIndexes:i,staleIndexes:n,errorCount:e.indexErrors.size,integrity:s}}(this.getMaintenanceDeps())}getIndexErrors(){return ve(this.getMaintenanceDeps())}async retryFailedIndexes(){return async function(e){const t=ve(e).map(e=>e.filePath);return e.indexErrors.clear(),e.indexFiles(t)}(this.getMaintenanceDeps())}async ensureInitialized(){this.initialized||await this.init()}getBackupDeps(){return{config:this.config,metaStore:this.metaStore,vectorStore:this.vectorStore,fullTextIndex:this.fullTextIndex,getStats:()=>this.getStats(),reinitializeStores:(e,t,i)=>{this.metaStore=e,this.vectorStore=t,this.fullTextIndex=i}}}async exportIndex(e){return await this.ensureInitialized(),async function(e,t){const i=Pe.dirname(t);await Oe.mkdir(i,{recursive:!0});const n=Pe.join(i,Pe.basename(t,Pe.extname(t)));await Oe.mkdir(n,{recursive:!0});const s={meta:!1,vectors:!1,fulltext:!1};try{const t=Pe.join(e.config.dataDir,"meta.db"),i=Pe.join(n,"meta.db");await Oe.copyFile(t,i),s.meta=!0}catch(e){}try{const t=Pe.join(e.config.dataDir,"vectors"),i=Pe.join(n,"vectors");await Fe(t,i),s.vectors=!0}catch(e){}try{const t=Pe.join(e.config.dataDir,"fulltext.json"),i=Pe.join(n,"fulltext.json");try{await Oe.access(t),await Oe.copyFile(t,i),s.fulltext=!0}catch{}}catch(e){}const r=e.getStats(),o={exportPath:n,timestamp:new Date,components:s,stats:r},a=Pe.join(n,"export-info.json");return await Oe.writeFile(a,JSON.stringify(o,null,2),"utf-8"),o}(this.getBackupDeps(),e)}async importIndex(e){return await this.ensureInitialized(),async function(e,t,i){let n;if(!(await Oe.stat(t)).isDirectory())throw new Error("目前只支持从目录导入,请先解压备份文件");n=t;const s=Pe.join(n,"export-info.json");try{await Oe.readFile(s,"utf-8")}catch{}let r=e.metaStore,o=e.vectorStore,a=e.fullTextIndex;const c=Pe.join(n,"meta.db"),d=Pe.join(e.config.dataDir,"meta.db");try{await Oe.access(c),await Oe.copyFile(c,d),r=new i.MetaStore(e.config.dataDir)}catch(e){}const l=Pe.join(n,"vectors"),u=Pe.join(e.config.dataDir,"vectors");try{await Oe.access(l),await Fe(l,u),o=new i.VectorStore(u,"documents",X()),await o.init()}catch(e){}const h=Pe.join(n,"fulltext.json"),m=Pe.join(e.config.dataDir,"fulltext.json");try{await Oe.access(h),await Oe.copyFile(h,m),a=new i.FullTextIndex(e.config.dataDir),await a.init()}catch(e){}e.reinitializeStores(r,o,a)}(this.getBackupDeps(),e,{MetaStore:m,VectorStore:r,FullTextIndex:d})}async listBackups(e){return async function(e){try{const t=await Oe.readdir(e,{withFileTypes:!0}),i=[];for(const n of t){if(!n.isDirectory())continue;const t=Pe.join(e,n.name),s=Pe.join(t,"export-info.json");try{const e=await Oe.readFile(s,"utf-8"),n=JSON.parse(e),r=await Ae(t);i.push({path:t,timestamp:new Date(n.timestamp),size:r})}catch{}}return i.sort((e,t)=>t.timestamp.getTime()-e.timestamp.getTime()),i}catch{return[]}}(e)}async destroy(){this.unwatchAll(),this.directoryWatcher.clearTimers(),this.indexingLocks.clear();try{this.metaStore.close()}catch(e){}try{this.vectorStore.close()}catch(e){}try{q()}catch(e){}this.indexErrors.clear(),this.initialized=!1}},qe=Symbol.for("ai-search-plugin-instance");function He(e){return async function(e){const{dataDir:t,workspace:i,arkApiKey:n,embeddingDimension:s}=e;try{const e=new Ue({dataDir:t,arkApiKey:n,embeddingDimension:s});await e.init();const r={directory:null,indexed:!1,filesIndexed:0},o=async t=>{r.directory=t,r.indexed=!1,r.filesIndexed=0;try{await e.indexDirectory(t,e=>{"scanning"!==e.stage&&e.total>0&&(r.filesIndexed=e.indexed,e.indexed)}),r.indexed=!0}catch(e){throw e}};i&&o(i).catch(e=>{});const a={tools:[Ke(e,r),Ye(e),Ge(e),tt(e),it(e),nt(e),Qe(e,r),Ze(e),et(e,r),yt(r),st(e),rt(e),ot(e),at(e),ct(e),dt(e),lt(e),ht(e),mt(e),pt(e),ft(e)],setWorkspace:o,getWorkspaceState:()=>({...r}),search:e};return function(e){globalThis[qe]=e}(a),a}catch(e){throw e}}(e)}function Ve(){return globalThis[qe]??null}function Ke(e,t){return{name:"search_local_documents",description:'搜索用户电脑上的本地文档,支持语义搜索(理解意图)和关键词搜索。\n\n⚠️ 重要:查找文档时,优先使用此工具而不是 execute_command 执行 find 命令!\n- 此工具支持语义理解,可以根据内容查找文档,不仅仅是文件名匹配\n- 已索引的文档可以直接搜索,无需遍历文件系统\n- 支持按文件类型过滤(如只搜索 PDF:file_types="pdf")\n\n支持的文件类型:\n- Word 文档 (.docx, .doc)\n- PDF 文件 (.pdf)\n- Excel 表格 (.xlsx, .xls)\n- PPT 演示文稿 (.pptx, .ppt)\n- 文本文件 (.txt, .md)\n\n使用场景:\n- 用户想查找某个主题的文档,如"找一下去年的采购合同"、"搜索关于糖尿病的PDF文档"\n- 用户需要特定内容的文件,如"关于用户隐私政策的文档"\n- 用户记不清文件名但记得内容,如"有个文档提到了季度销售目标"\n- 用户想找特定类型的文件,如"找所有PDF文件"(使用 file_types="pdf")\n\n搜索范围:\n- 如果设置了工作空间,默认只搜索工作空间内的文档\n- 可以通过 scope 参数选择搜索范围:workspace(工作空间)、all(全部已索引)\n- 使用 set_search_workspace 工具可以设置工作空间',parameters:{type:"object",properties:{query:{type:"string",description:'搜索内容,可以是关键词或自然语言描述,如"采购合同"或"关于项目预算的文档"'},limit:{type:"number",description:"返回结果数量,默认 10,最大 50"},mode:{type:"string",enum:["semantic","keyword","hybrid"],description:"搜索模式:semantic(语义,理解意图)、keyword(精确关键词)、hybrid(混合,推荐)"},file_types:{type:"string",description:'限定文件类型,逗号分隔,可选:document,pdf,text。如 "pdf,document"'},scope:{type:"string",enum:["workspace","all"],description:"搜索范围:workspace(仅工作空间,默认)、all(全部已索引文档)"}},required:["query"]},execute:async(i,n)=>{const s=i.query,r=Math.min(i.limit||10,50),o=i.mode||"hybrid",a=i.scope||"workspace",c={limit:r,mode:o};if(i.file_types){const e=i.file_types.split(",").map(e=>e.trim());c.fileTypes=e}let d=await e.search(s,c);if("workspace"===a&&t.directory&&(d=d.filter(e=>e.id.startsWith(t.directory))),0===d.length){const e=t.directory&&"workspace"===a?'可以尝试换个关键词,或使用 scope="all" 搜索全部文档,或用 set_search_workspace 设置新的工作空间':"可以尝试换个关键词,或者先用 set_search_workspace 设置工作空间";return JSON.stringify({message:"没有找到匹配的文档",suggestion:e,workspace:t.directory,scope:a})}const l=d.map((e,t)=>({rank:t+1,path:e.id,name:e.name,type:e.type,size:e.size,modified:e.dateModified,relevance:`${Math.round(100*e.score)}%`,matchType:e.matchType,snippet:e.snippet}));return JSON.stringify({query:s,mode:o,scope:a,workspace:t.directory,total:d.length,results:l})}}}function Ye(e){return{name:"index_document_file",description:"将指定的文档文件添加到搜索索引中。\n索引后,该文件可以通过 search_local_documents 搜索到。\n支持的文件类型:.docx, .doc, .pdf, .xlsx, .xls, .pptx, .ppt, .txt, .md",parameters:{type:"object",properties:{file_path:{type:"string",description:"要索引的文件绝对路径,如 /Users/xxx/Documents/report.pdf"}},required:["file_path"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{const n=t.file_path;try{return await e.indexFile(n),JSON.stringify({success:!0,message:`文件已成功索引: ${n}`,path:n})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e),path:n})}}}}function Ge(e){return{name:"index_document_directory",description:"扫描并索引指定目录下的所有文档文件(包括子目录)。\n索引完成后,目录内的所有支持的文档都可以被搜索。\n首次索引可能需要一些时间,取决于文件数量。\n\n使用场景:\n- 用户想搜索某个目录但还没索引过\n- 用户的文档目录有更新,需要重新索引",parameters:{type:"object",properties:{directory:{type:"string",description:"要索引的目录绝对路径,如 /Users/xxx/Documents 或 ~/Documents"}},required:["directory"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{let n=t.directory;if(n.startsWith("~")){const e=await import("os");n=n.replace("~",e.homedir())}const s=await import("path");s.isAbsolute(n)||(n=s.resolve(i.cwd,n));try{let t=0,i=0;return await e.indexDirectory(n,e=>{t=e.indexed,i=e.total}),JSON.stringify({success:!0,message:"目录索引完成",directory:n,filesIndexed:t,filesFound:i})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e),directory:n})}}}}function Qe(e,t){return{name:"get_document_index_stats",description:"获取文档搜索索引的统计信息。\n包括已索引文档数量、文件类型分布、索引目录、当前工作空间等。\n用于了解当前索引状态,帮助决定是否需要索引新目录。",parameters:{type:"object",properties:{},required:[]},execute:async(i,n)=>{const s=e.getStats();return JSON.stringify({totalDocuments:s.totalDocuments,byType:s.byType,indexedDirectories:s.directories,lastUpdated:s.lastUpdated?.toISOString()||"N/A",indexSize:s.indexSize,workspace:{directory:t.directory,indexed:t.indexed,filesIndexed:t.filesIndexed}})}}}function Ze(e){return{name:"clear_document_index",description:"清除所有文档索引数据。\n这是一个危险操作,会删除所有已索引的文档信息。\n清除后需要重新索引才能搜索。\n通常只在索引出问题或需要重建时使用。",parameters:{type:"object",properties:{confirm:{type:"string",description:'确认清除,必须输入 "YES" 才会执行'}},required:["confirm"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{if("YES"!==t.confirm)return JSON.stringify({success:!1,message:'操作取消:需要输入 "YES" 确认清除'});try{return await e.clear(),JSON.stringify({success:!0,message:"索引已清除,需要重新索引文档"})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function et(e,t){return{name:"set_search_workspace",description:'设置文档搜索的工作空间目录。\n\n设置后:\n- 工作空间内的文档会被自动索引\n- search_local_documents 默认只搜索工作空间内的文档\n- 可以随时更换工作空间\n\n使用场景:\n- 用户说"在这个项目里找文档"时,设置当前目录为工作空间\n- 用户切换项目时,更新工作空间\n- 用户想搜索特定文件夹的文档',parameters:{type:"object",properties:{directory:{type:"string",description:"工作空间目录路径,可以是绝对路径或相对路径(相对于当前 cwd)"}},required:["directory"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(i,n)=>{let s=i.directory;if(s.startsWith("~")){const e=await import("os");s=s.replace("~",e.homedir())}const r=await import("path");r.isAbsolute(s)||(s=r.resolve(n.cwd,s));const o=await import("fs/promises");try{if(!(await o.stat(s)).isDirectory())return JSON.stringify({success:!1,error:"指定路径不是一个目录",path:s})}catch{return JSON.stringify({success:!1,error:"目录不存在",path:s})}t.directory=s,t.indexed=!1,t.filesIndexed=0;try{return await e.indexDirectory(s,e=>{t.filesIndexed=e.indexed}),t.indexed=!0,JSON.stringify({success:!0,message:"工作空间已设置并索引完成",workspace:s,filesIndexed:t.filesIndexed})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e),workspace:s})}}}}function tt(e){return{name:"index_document_files",description:"批量索引多个文档文件。\n相比单个文件索引,批量索引效率更高,适合一次性索引多个文件。\n会返回成功和失败的数量,以及失败的文件列表。",parameters:{type:"object",properties:{file_paths:{type:"array",items:{type:"string"},description:"要索引的文件路径数组(绝对路径)"}},required:["file_paths"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{const n=t.file_paths;if(!Array.isArray(n)||0===n.length)return JSON.stringify({success:!1,error:"file_paths 必须是非空数组"});try{const t=await e.indexFiles(n);return JSON.stringify({success:!0,message:"批量索引完成",successCount:t.success,failedCount:t.failed,errors:t.errors})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function it(e){return{name:"remove_document_files",description:"批量删除多个文件的索引。\n用于清理不再需要的文档索引,比如文件已删除或移动到其他位置。",parameters:{type:"object",properties:{file_paths:{type:"array",items:{type:"string"},description:"要删除索引的文件路径数组(绝对路径)"}},required:["file_paths"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{const n=t.file_paths;if(!Array.isArray(n)||0===n.length)return JSON.stringify({success:!1,error:"file_paths 必须是非空数组"});try{const t=await e.removeFiles(n);return JSON.stringify({success:!0,message:"批量删除完成",successCount:t.success,failedCount:t.failed,errors:t.errors})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function nt(e){return{name:"update_document_files",description:"批量更新多个文件的索引(重新索引)。\n用于文件内容已修改,需要更新索引的场景。\n会先删除旧索引,再重新索引文件。",parameters:{type:"object",properties:{file_paths:{type:"array",items:{type:"string"},description:"要更新索引的文件路径数组(绝对路径)"}},required:["file_paths"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{const n=t.file_paths;if(!Array.isArray(n)||0===n.length)return JSON.stringify({success:!1,error:"file_paths 必须是非空数组"});try{const t=await e.updateFiles(n);return JSON.stringify({success:!0,message:"批量更新完成",successCount:t.success,failedCount:t.failed,errors:t.errors})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function st(e){return{name:"watch_document_directory",description:"开始监听目录变化,自动更新索引。\n监听后,目录内的文件新增、修改、删除都会自动更新索引,无需手动操作。\n适合需要实时保持索引同步的场景。\n\n注意:监听会持续运行,直到调用 unwatch_document_directory 停止。",parameters:{type:"object",properties:{directory:{type:"string",description:"要监听的目录绝对路径"},ignore_initial:{type:"boolean",description:"是否忽略初始扫描(只监听后续变化),默认 true"},debounce:{type:"number",description:"防抖延迟(毫秒),避免频繁触发,默认 1000"}},required:["directory"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{let n=t.directory;if(n.startsWith("~")){const e=await import("os");n=n.replace("~",e.homedir())}const s=await import("path");s.isAbsolute(n)||(n=s.resolve(i.cwd,n));const r=await import("fs/promises");try{if(!(await r.stat(n)).isDirectory())return JSON.stringify({success:!1,error:"指定路径不是一个目录",path:n})}catch{return JSON.stringify({success:!1,error:"目录不存在",path:n})}try{return e.watchDirectory(n,{ignoreInitial:!1!==t.ignore_initial,debounce:"number"==typeof t.debounce?t.debounce:1e3}),JSON.stringify({success:!0,message:"开始监听目录",directory:n})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e),directory:n})}}}}function rt(e){return{name:"unwatch_document_directory",description:"停止监听指定目录的变化。\n停止后,该目录的文件变化将不再自动更新索引。",parameters:{type:"object",properties:{directory:{type:"string",description:"要停止监听的目录绝对路径"}},required:["directory"]},execute:async(t,i)=>{let n=t.directory;if(n.startsWith("~")){const e=await import("os");n=n.replace("~",e.homedir())}const s=await import("path");s.isAbsolute(n)||(n=s.resolve(i.cwd,n));try{return e.unwatchDirectory(n),JSON.stringify({success:!0,message:"已停止监听目录",directory:n})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e),directory:n})}}}}function ot(e){return{name:"get_watched_directories",description:"获取当前正在监听的目录列表。\n用于查看哪些目录正在被自动监听和更新。",parameters:{type:"object",properties:{},required:[]},execute:async(t,i)=>{const n=e.getWatchedDirectories();return JSON.stringify({success:!0,directories:n,count:n.length})}}}function at(e){return{name:"cleanup_document_index",description:"清理无效的索引(文件已删除但索引还在)。\n会检查所有已索引的文件是否还存在:\n- 如果文件不存在,删除索引\n- 如果文件已修改,重新索引\n\n用于维护索引的一致性,建议定期执行。",parameters:{type:"object",properties:{},required:[]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{try{const t=await e.cleanup();return JSON.stringify({success:!0,message:"清理完成",removedCount:t.removed,updatedCount:t.updated})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function ct(e){return{name:"export_document_index",description:"导出文档索引数据到指定路径。\n导出内容包括:\n- 元数据(SQLite 数据库)\n- 向量数据(LanceDB)\n- 全文索引(FlexSearch)\n\n导出的数据可以用于备份、迁移或恢复索引。",parameters:{type:"object",properties:{output_path:{type:"string",description:"导出路径(目录路径,会在该目录下创建导出文件夹)"}},required:["output_path"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{let n=t.output_path;if(n.startsWith("~")){const e=await import("os");n=n.replace("~",e.homedir())}const s=await import("path");s.isAbsolute(n)||(n=s.resolve(i.cwd,n));try{const t=await e.exportIndex(n);return JSON.stringify({success:!0,message:"索引导出完成",exportPath:t.exportPath,timestamp:t.timestamp.toISOString(),components:t.components,stats:{totalDocuments:t.stats.totalDocuments,byType:t.stats.byType}})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function dt(e){return{name:"import_document_index",description:"从指定路径导入文档索引数据。\n导入前会检查导出信息,确保数据完整性。\n导入后会替换当前索引数据,请谨慎使用。",parameters:{type:"object",properties:{input_path:{type:"string",description:"导入路径(导出时创建的目录路径)"}},required:["input_path"]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{let n=t.input_path;if(n.startsWith("~")){const e=await import("os");n=n.replace("~",e.homedir())}const s=await import("path");s.isAbsolute(n)||(n=s.resolve(i.cwd,n));try{return await e.importIndex(n),JSON.stringify({success:!0,message:"索引导入完成",inputPath:n})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function lt(e){return{name:"list_document_index_backups",description:"列出指定目录下的所有索引备份。\n用于查看可用的备份,以便选择恢复。",parameters:{type:"object",properties:{backup_dir:{type:"string",description:"备份目录路径"}},required:["backup_dir"]},execute:async(t,i)=>{let n=t.backup_dir;if(n.startsWith("~")){const e=await import("os");n=n.replace("~",e.homedir())}const s=await import("path");s.isAbsolute(n)||(n=s.resolve(i.cwd,n));try{const t=await e.listBackups(n);return JSON.stringify({success:!0,backups:t.map(e=>({path:e.path,timestamp:e.timestamp.toISOString(),size:e.size,sizeFormatted:ut(e.size)})),count:t.length})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function ut(e){if(0===e)return"0 B";const t=Math.floor(Math.log(e)/Math.log(1024));return`${(e/Math.pow(1024,t)).toFixed(2)} ${["B","KB","MB","GB"][t]}`}function ht(e){return{name:"optimize_document_index",description:"优化文档索引,包括:\n- 清理无效索引(文件已删除)\n- 更新过期索引(文件已修改)\n- 压缩索引数据\n- 碎片整理\n\n建议定期执行以保持索引性能和一致性。",parameters:{type:"object",properties:{},required:[]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{try{return await e.optimize(),JSON.stringify({success:!0,message:"索引优化完成"})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function mt(e){return{name:"check_document_index_health",description:"检查文档索引的健康状态。\n包括:\n- 索引完整性检查(元数据、向量、全文索引)\n- 无效索引统计(文件已删除)\n- 过期索引统计(文件已修改)\n- 错误统计\n\n用于诊断索引问题,决定是否需要优化或修复。",parameters:{type:"object",properties:{},required:[]},execute:async(t,i)=>{try{const t=await e.healthCheck();return JSON.stringify({success:!0,healthy:t.healthy,totalDocuments:t.totalDocuments,invalidIndexes:t.invalidIndexes,staleIndexes:t.staleIndexes,errorCount:t.errorCount,integrity:t.integrity,message:t.healthy?"索引健康":"索引存在问题,建议执行 optimize_document_index 或 cleanup_document_index"})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function pt(e){return{name:"get_document_index_errors",description:"获取索引过程中发生的错误列表。\n包括文件路径、错误信息、重试次数和时间戳。\n用于排查索引问题,了解哪些文件索引失败。",parameters:{type:"object",properties:{},required:[]},execute:async(t,i)=>{try{const t=e.getIndexErrors();return JSON.stringify({success:!0,errors:t.map(e=>({filePath:e.filePath,error:e.error,retryCount:e.retryCount,timestamp:e.timestamp.toISOString()})),count:t.length})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function ft(e){return{name:"retry_failed_document_indexes",description:"重试之前索引失败的文件。\n会清除错误记录,然后重新尝试索引所有失败的文件。\n用于修复索引错误,恢复完整的索引覆盖。",parameters:{type:"object",properties:{},required:[]},sideEffects:[{type:"filesystem",success:!0}],execute:async(t,i)=>{try{const t=await e.retryFailedIndexes();return JSON.stringify({success:!0,message:"重试完成",successCount:t.success,failedCount:t.failed,errors:t.errors})}catch(e){return JSON.stringify({success:!1,error:e instanceof Error?e.message:String(e)})}}}}function yt(e){return{name:"get_search_workspace",description:"获取当前文档搜索的工作空间状态。\n返回当前工作空间目录、是否已索引、索引文件数量等信息。",parameters:{type:"object",properties:{},required:[]},execute:async(t,i)=>e.directory?JSON.stringify({hasWorkspace:!0,directory:e.directory,indexed:e.indexed,filesIndexed:e.filesIndexed}):JSON.stringify({hasWorkspace:!1,message:"尚未设置工作空间,使用 set_search_workspace 设置"})}}export{i as FileType,n as DEFAULT_CONFIG,r as VectorStore,d as FullTextIndex,m as MetaStore,E as parseDocument,T as isSupportedDocument,_ as getDocumentType,P as DEFAULT_INSTRUCTIONS,F as initEmbedder,j as embed,L as embedDocument,M as embedQuery,R as embedBatch,$ as embedBatchConcurrent,J as embedImage,W as embedVideo,B as embedMultimodal,X as getEmbeddingDimension,U as setEmbeddingDimension,q as disposeEmbedder,H as isEmbedderInitialized,Z as DEFAULT_EXTENSION_RULES,ee as DEFAULT_DIRECTORY_RULES,te as DEFAULT_FILE_RULES,ie as DEFAULT_PATH_RULES,ne as DEFAULT_SCAN_RULES,se as ScanRulesManager,re as createRulesManager,ae as scanDirectories,ce as getDefaultDirectories,pe as formatSize,fe as formatDate,ye as getFileType,ge as extractSnippet,be as addGlobalProgressListener,Ee as removeGlobalProgressListener,Me as splitText,Je as getChunkStats,Be as IndexingPipeline,Xe as createIndexingPipeline,Ue as DocumentSearch,He as searchPlugin,Ve as getSearchPlugin};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{addGlobalProgressListener as e,getSearchPlugin as s}from"./chunk-45UQFAEP.js";var t=class{channelPrefix;ipcMain;indexingListeners=new Set;lastProgress=null;isIndexing=!1;constructor(e){this.channelPrefix=e.channelPrefix||"ai-chat",this.ipcMain=e.ipcMain}init(){this.registerIpcHandlers(),this.registerGlobalProgressListener()}registerGlobalProgressListener(){e(e=>{this.isIndexing="done"!==e.stage,this.lastProgress=e,this.broadcastIndexProgress(e)})}registerIpcHandlers(){this.ipcMain.handle(`${this.channelPrefix}:index:registerListener`,e=>{const s=e.sender;return this.indexingListeners.add(s),this.lastProgress&&this.isIndexing&&(s.isDestroyed()||s.send(`${this.channelPrefix}:index:progress`,{indexed:this.lastProgress.indexed,total:this.lastProgress.total,currentFile:this.lastProgress.currentFile,stage:this.lastProgress.stage})),{success:!0}}),this.ipcMain.handle(`${this.channelPrefix}:index:unregisterListener`,e=>(this.indexingListeners.delete(e.sender),{success:!0})),this.ipcMain.handle(`${this.channelPrefix}:index:getStats`,async()=>{try{const e=s();if(!e)return{totalDocuments:0,indexSize:0,lastUpdated:null};const t=e.search.getStats();return{totalDocuments:t.totalDocuments,indexSize:t.indexSize,lastUpdated:t.lastUpdated?t.lastUpdated.toISOString():null}}catch(e){return{totalDocuments:0,indexSize:0,lastUpdated:null}}}),this.ipcMain.handle(`${this.channelPrefix}:index:sync`,async()=>{try{const e=s();if(!e)throw new Error("搜索插件未初始化");const t=e.getWorkspaceState();if(!t.directory)throw new Error("工作空间未设置");return await e.search.indexDirectory(t.directory),{success:!0}}catch(e){const s=e instanceof Error?e.message:String(e);throw this.broadcastIndexProgress({indexed:0,total:0,stage:"error",error:s}),e}}),this.ipcMain.handle(`${this.channelPrefix}:index:status`,async()=>({isIndexing:this.isIndexing,lastProgress:this.lastProgress})),this.ipcMain.handle(`${this.channelPrefix}:index:cancel`,async()=>{try{const e=s();if(!e)return{success:!1,message:"搜索插件未初始化"};return e.search.cancelIndexing()?(this.broadcastIndexProgress({indexed:this.lastProgress?.indexed||0,total:this.lastProgress?.total||0,stage:"cancelled"}),this.isIndexing=!1,{success:!0}):{success:!1,message:"没有正在运行的索引任务"}}catch(e){return{success:!1,message:String(e)}}}),this.ipcMain.handle(`${this.channelPrefix}:index:delete`,async()=>{try{const e=s();if(!e)throw new Error("搜索插件未初始化");return await e.search.clear(),this.isIndexing=!1,this.lastProgress=null,this.broadcastIndexProgress({indexed:0,total:0,stage:"done"}),{success:!0}}catch(e){throw e}});this.ipcMain.handle(`${this.channelPrefix}:search:query`,async(e,t,r)=>{try{const e=s();if(!e)return{success:!1,error:"搜索插件未初始化"};const n=await e.search.search(t,(e=>{if(!e)return;const{dateRange:s,...t}=e;if(!s)return t;const r=s.start?new Date(s.start):void 0,n=s.end?new Date(s.end):void 0,i=!!r&&!Number.isNaN(r.getTime()),a=!!n&&!Number.isNaN(n.getTime());return i||a?{...t,dateRange:{start:i?r:void 0,end:a?n:void 0}}:t})(r));return{success:!0,data:n}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),this.ipcMain.handle(`${this.channelPrefix}:workspace:set`,async(e,t)=>{const r=s();if(!r)return{success:!1,error:"搜索插件未初始化"};try{return await r.setWorkspace(t),{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}),this.ipcMain.handle(`${this.channelPrefix}:workspace:get`,async()=>{const e=s();return e?e.getWorkspaceState():{directory:null,indexed:!1,filesIndexed:0}})}broadcastIndexProgress(e){this.lastProgress=e,this.indexingListeners.forEach(s=>{s.isDestroyed()?this.indexingListeners.delete(s):s.send(`${this.channelPrefix}:index:progress`,{indexed:e.indexed,total:e.total,currentFile:e.currentFile,stage:e.stage})})}};function r(e){return new t(e)}export{t as SearchElectronBridge,r as createSearchElectronBridge};
|
package/dist/index.js
CHANGED
|
@@ -1,99 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SearchElectronBridge,
|
|
3
|
-
createSearchElectronBridge
|
|
4
|
-
} from "./chunk-MXWSYA4O.js";
|
|
5
|
-
import {
|
|
6
|
-
DEFAULT_CONFIG,
|
|
7
|
-
DEFAULT_DIRECTORY_RULES,
|
|
8
|
-
DEFAULT_EXTENSION_RULES,
|
|
9
|
-
DEFAULT_FILE_RULES,
|
|
10
|
-
DEFAULT_INSTRUCTIONS,
|
|
11
|
-
DEFAULT_PATH_RULES,
|
|
12
|
-
DEFAULT_SCAN_RULES,
|
|
13
|
-
DocumentSearch,
|
|
14
|
-
FileType,
|
|
15
|
-
FullTextIndex,
|
|
16
|
-
IndexingPipeline,
|
|
17
|
-
MetaStore,
|
|
18
|
-
ScanRulesManager,
|
|
19
|
-
VectorStore,
|
|
20
|
-
addGlobalProgressListener,
|
|
21
|
-
createIndexingPipeline,
|
|
22
|
-
createRulesManager,
|
|
23
|
-
disposeEmbedder,
|
|
24
|
-
embed,
|
|
25
|
-
embedBatch,
|
|
26
|
-
embedBatchConcurrent,
|
|
27
|
-
embedDocument,
|
|
28
|
-
embedImage,
|
|
29
|
-
embedMultimodal,
|
|
30
|
-
embedQuery,
|
|
31
|
-
embedVideo,
|
|
32
|
-
extractSnippet,
|
|
33
|
-
formatDate,
|
|
34
|
-
formatSize,
|
|
35
|
-
getChunkStats,
|
|
36
|
-
getDefaultDirectories,
|
|
37
|
-
getDocumentType,
|
|
38
|
-
getEmbeddingDimension,
|
|
39
|
-
getFileType,
|
|
40
|
-
getSearchPlugin,
|
|
41
|
-
initEmbedder,
|
|
42
|
-
isEmbedderInitialized,
|
|
43
|
-
isSupportedDocument,
|
|
44
|
-
parseDocument,
|
|
45
|
-
removeGlobalProgressListener,
|
|
46
|
-
scanDirectories,
|
|
47
|
-
searchPlugin,
|
|
48
|
-
setEmbeddingDimension,
|
|
49
|
-
splitText
|
|
50
|
-
} from "./chunk-YJIIX54F.js";
|
|
51
|
-
export {
|
|
52
|
-
DEFAULT_CONFIG,
|
|
53
|
-
DEFAULT_DIRECTORY_RULES,
|
|
54
|
-
DEFAULT_EXTENSION_RULES,
|
|
55
|
-
DEFAULT_FILE_RULES,
|
|
56
|
-
DEFAULT_INSTRUCTIONS,
|
|
57
|
-
DEFAULT_PATH_RULES,
|
|
58
|
-
DEFAULT_SCAN_RULES,
|
|
59
|
-
DocumentSearch,
|
|
60
|
-
FileType,
|
|
61
|
-
FullTextIndex,
|
|
62
|
-
IndexingPipeline,
|
|
63
|
-
MetaStore,
|
|
64
|
-
ScanRulesManager,
|
|
65
|
-
SearchElectronBridge,
|
|
66
|
-
VectorStore,
|
|
67
|
-
addGlobalProgressListener,
|
|
68
|
-
createIndexingPipeline,
|
|
69
|
-
createRulesManager,
|
|
70
|
-
createSearchElectronBridge,
|
|
71
|
-
disposeEmbedder,
|
|
72
|
-
embed,
|
|
73
|
-
embedBatch,
|
|
74
|
-
embedBatchConcurrent,
|
|
75
|
-
embedDocument,
|
|
76
|
-
embedImage,
|
|
77
|
-
embedMultimodal,
|
|
78
|
-
embedQuery,
|
|
79
|
-
embedVideo,
|
|
80
|
-
extractSnippet,
|
|
81
|
-
formatDate,
|
|
82
|
-
formatSize,
|
|
83
|
-
getChunkStats,
|
|
84
|
-
getDefaultDirectories,
|
|
85
|
-
getDocumentType,
|
|
86
|
-
getEmbeddingDimension,
|
|
87
|
-
getFileType,
|
|
88
|
-
getSearchPlugin,
|
|
89
|
-
initEmbedder,
|
|
90
|
-
isEmbedderInitialized,
|
|
91
|
-
isSupportedDocument,
|
|
92
|
-
parseDocument,
|
|
93
|
-
removeGlobalProgressListener,
|
|
94
|
-
scanDirectories,
|
|
95
|
-
searchPlugin,
|
|
96
|
-
setEmbeddingDimension,
|
|
97
|
-
splitText
|
|
98
|
-
};
|
|
99
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import{SearchElectronBridge as o,createSearchElectronBridge as r}from"./chunk-LMBF5LBD.js";import{DEFAULT_CONFIG as m,DEFAULT_DIRECTORY_RULES as p,DEFAULT_EXTENSION_RULES as t,DEFAULT_FILE_RULES as c,DEFAULT_INSTRUCTIONS as f,DEFAULT_PATH_RULES as h,DEFAULT_SCAN_RULES as i,DocumentSearch as j,FileType as k,FullTextIndex as n,IndexingPipeline as s,MetaStore as u,ScanRulesManager as B,VectorStore as F,addGlobalProgressListener as L,createIndexingPipeline as e,createRulesManager as x,disposeEmbedder as A,embed as D,embedBatch as E,embedBatchConcurrent as M,embedDocument as P,embedImage as Q,embedMultimodal as U,embedQuery as a,embedVideo as b,extractSnippet as d,formatDate as g,formatSize as l,getChunkStats as q,getDefaultDirectories as v,getDocumentType as w,getEmbeddingDimension as y,getFileType as z,getSearchPlugin as C,initEmbedder as G,isEmbedderInitialized as H,isSupportedDocument as I,parseDocument as J,removeGlobalProgressListener as K,scanDirectories as N,searchPlugin as O,setEmbeddingDimension as R,splitText as S}from"./chunk-45UQFAEP.js";export{m as DEFAULT_CONFIG,p as DEFAULT_DIRECTORY_RULES,t as DEFAULT_EXTENSION_RULES,c as DEFAULT_FILE_RULES,f as DEFAULT_INSTRUCTIONS,h as DEFAULT_PATH_RULES,i as DEFAULT_SCAN_RULES,j as DocumentSearch,k as FileType,n as FullTextIndex,s as IndexingPipeline,u as MetaStore,B as ScanRulesManager,o as SearchElectronBridge,F as VectorStore,L as addGlobalProgressListener,e as createIndexingPipeline,x as createRulesManager,r as createSearchElectronBridge,A as disposeEmbedder,D as embed,E as embedBatch,M as embedBatchConcurrent,P as embedDocument,Q as embedImage,U as embedMultimodal,a as embedQuery,b as embedVideo,d as extractSnippet,g as formatDate,l as formatSize,q as getChunkStats,v as getDefaultDirectories,w as getDocumentType,y as getEmbeddingDimension,z as getFileType,C as getSearchPlugin,G as initEmbedder,H as isEmbedderInitialized,I as isSupportedDocument,J as parseDocument,K as removeGlobalProgressListener,N as scanDirectories,O as searchPlugin,R as setEmbeddingDimension,S as splitText};
|
package/dist/tools/index.js
CHANGED
|
@@ -1,9 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getSearchPlugin,
|
|
3
|
-
searchPlugin
|
|
4
|
-
} from "../chunk-YJIIX54F.js";
|
|
5
|
-
export {
|
|
6
|
-
getSearchPlugin,
|
|
7
|
-
searchPlugin
|
|
8
|
-
};
|
|
9
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import{getSearchPlugin as o,searchPlugin as r}from"../chunk-45UQFAEP.js";export{o as getSearchPlugin,r as searchPlugin};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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":[]}
|
package/dist/chunk-MXWSYA4O.js
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
addGlobalProgressListener,
|
|
3
|
-
getSearchPlugin
|
|
4
|
-
} from "./chunk-YJIIX54F.js";
|
|
5
|
-
|
|
6
|
-
// src/bridge/electron.ts
|
|
7
|
-
var SearchElectronBridge = class {
|
|
8
|
-
channelPrefix;
|
|
9
|
-
ipcMain;
|
|
10
|
-
indexingListeners = /* @__PURE__ */ new Set();
|
|
11
|
-
lastProgress = null;
|
|
12
|
-
isIndexing = false;
|
|
13
|
-
constructor(options) {
|
|
14
|
-
this.channelPrefix = options.channelPrefix || "ai-chat";
|
|
15
|
-
this.ipcMain = options.ipcMain;
|
|
16
|
-
}
|
|
17
|
-
/** 初始化桥接(注册 IPC handlers 和全局进度监听器) */
|
|
18
|
-
init() {
|
|
19
|
-
this.registerIpcHandlers();
|
|
20
|
-
this.registerGlobalProgressListener();
|
|
21
|
-
console.log("[AI-Search] Electron \u6865\u63A5\u5DF2\u521D\u59CB\u5316");
|
|
22
|
-
}
|
|
23
|
-
/** 注册全局进度监听器 */
|
|
24
|
-
registerGlobalProgressListener() {
|
|
25
|
-
addGlobalProgressListener((progress) => {
|
|
26
|
-
this.isIndexing = progress.stage !== "done";
|
|
27
|
-
this.lastProgress = progress;
|
|
28
|
-
this.broadcastIndexProgress(progress);
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
/** 注册 IPC handlers */
|
|
32
|
-
registerIpcHandlers() {
|
|
33
|
-
this.ipcMain.handle(`${this.channelPrefix}:index:registerListener`, (event) => {
|
|
34
|
-
const webContents = event.sender;
|
|
35
|
-
this.indexingListeners.add(webContents);
|
|
36
|
-
if (this.lastProgress && this.isIndexing) {
|
|
37
|
-
if (!webContents.isDestroyed()) {
|
|
38
|
-
webContents.send(`${this.channelPrefix}:index:progress`, {
|
|
39
|
-
indexed: this.lastProgress.indexed,
|
|
40
|
-
total: this.lastProgress.total,
|
|
41
|
-
currentFile: this.lastProgress.currentFile,
|
|
42
|
-
stage: this.lastProgress.stage
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return { success: true };
|
|
47
|
-
});
|
|
48
|
-
this.ipcMain.handle(`${this.channelPrefix}:index:unregisterListener`, (event) => {
|
|
49
|
-
this.indexingListeners.delete(event.sender);
|
|
50
|
-
return { success: true };
|
|
51
|
-
});
|
|
52
|
-
this.ipcMain.handle(`${this.channelPrefix}:index:getStats`, async () => {
|
|
53
|
-
try {
|
|
54
|
-
const searchPlugin = getSearchPlugin();
|
|
55
|
-
if (!searchPlugin) {
|
|
56
|
-
return {
|
|
57
|
-
totalDocuments: 0,
|
|
58
|
-
indexSize: 0,
|
|
59
|
-
lastUpdated: null
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
const stats = searchPlugin.search.getStats();
|
|
63
|
-
return {
|
|
64
|
-
totalDocuments: stats.totalDocuments,
|
|
65
|
-
indexSize: stats.indexSize,
|
|
66
|
-
lastUpdated: stats.lastUpdated ? stats.lastUpdated.toISOString() : null
|
|
67
|
-
};
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error("[AI-Search] \u83B7\u53D6\u7D22\u5F15\u7EDF\u8BA1\u5931\u8D25:", error);
|
|
70
|
-
return {
|
|
71
|
-
totalDocuments: 0,
|
|
72
|
-
indexSize: 0,
|
|
73
|
-
lastUpdated: null
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
this.ipcMain.handle(`${this.channelPrefix}:index:sync`, async () => {
|
|
78
|
-
try {
|
|
79
|
-
const searchPlugin = getSearchPlugin();
|
|
80
|
-
if (!searchPlugin) {
|
|
81
|
-
throw new Error("\u641C\u7D22\u63D2\u4EF6\u672A\u521D\u59CB\u5316");
|
|
82
|
-
}
|
|
83
|
-
const workspaceState = searchPlugin.getWorkspaceState();
|
|
84
|
-
if (!workspaceState.directory) {
|
|
85
|
-
throw new Error("\u5DE5\u4F5C\u7A7A\u95F4\u672A\u8BBE\u7F6E");
|
|
86
|
-
}
|
|
87
|
-
await searchPlugin.search.indexDirectory(workspaceState.directory);
|
|
88
|
-
return { success: true };
|
|
89
|
-
} catch (error) {
|
|
90
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
91
|
-
console.error("[AI-Search] \u540C\u6B65\u7D22\u5F15\u5931\u8D25:", errorMessage);
|
|
92
|
-
this.broadcastIndexProgress({
|
|
93
|
-
indexed: 0,
|
|
94
|
-
total: 0,
|
|
95
|
-
stage: "error",
|
|
96
|
-
error: errorMessage
|
|
97
|
-
});
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
this.ipcMain.handle(`${this.channelPrefix}:index:status`, async () => {
|
|
102
|
-
return {
|
|
103
|
-
isIndexing: this.isIndexing,
|
|
104
|
-
lastProgress: this.lastProgress
|
|
105
|
-
};
|
|
106
|
-
});
|
|
107
|
-
this.ipcMain.handle(`${this.channelPrefix}:index:cancel`, async () => {
|
|
108
|
-
try {
|
|
109
|
-
const searchPlugin = getSearchPlugin();
|
|
110
|
-
if (!searchPlugin) {
|
|
111
|
-
return { success: false, message: "\u641C\u7D22\u63D2\u4EF6\u672A\u521D\u59CB\u5316" };
|
|
112
|
-
}
|
|
113
|
-
const cancelled = searchPlugin.search.cancelIndexing();
|
|
114
|
-
if (cancelled) {
|
|
115
|
-
this.broadcastIndexProgress({
|
|
116
|
-
indexed: this.lastProgress?.indexed || 0,
|
|
117
|
-
total: this.lastProgress?.total || 0,
|
|
118
|
-
stage: "cancelled"
|
|
119
|
-
});
|
|
120
|
-
this.isIndexing = false;
|
|
121
|
-
return { success: true };
|
|
122
|
-
} else {
|
|
123
|
-
return { success: false, message: "\u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u7D22\u5F15\u4EFB\u52A1" };
|
|
124
|
-
}
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error("[AI-Search] \u53D6\u6D88\u7D22\u5F15\u5931\u8D25:", error);
|
|
127
|
-
return { success: false, message: String(error) };
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
this.ipcMain.handle(`${this.channelPrefix}:index:delete`, async () => {
|
|
131
|
-
try {
|
|
132
|
-
const searchPlugin = getSearchPlugin();
|
|
133
|
-
if (!searchPlugin) {
|
|
134
|
-
throw new Error("\u641C\u7D22\u63D2\u4EF6\u672A\u521D\u59CB\u5316");
|
|
135
|
-
}
|
|
136
|
-
await searchPlugin.search.clear();
|
|
137
|
-
this.isIndexing = false;
|
|
138
|
-
this.lastProgress = null;
|
|
139
|
-
this.broadcastIndexProgress({
|
|
140
|
-
indexed: 0,
|
|
141
|
-
total: 0,
|
|
142
|
-
stage: "done"
|
|
143
|
-
});
|
|
144
|
-
return { success: true };
|
|
145
|
-
} catch (error) {
|
|
146
|
-
console.error("[AI-Search] \u5220\u9664\u7D22\u5F15\u5931\u8D25:", error);
|
|
147
|
-
throw error;
|
|
148
|
-
}
|
|
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
|
-
});
|
|
205
|
-
}
|
|
206
|
-
/** 广播索引进度到所有前端监听器 */
|
|
207
|
-
broadcastIndexProgress(progress) {
|
|
208
|
-
this.lastProgress = progress;
|
|
209
|
-
this.indexingListeners.forEach((webContents) => {
|
|
210
|
-
if (!webContents.isDestroyed()) {
|
|
211
|
-
webContents.send(`${this.channelPrefix}:index:progress`, {
|
|
212
|
-
indexed: progress.indexed,
|
|
213
|
-
total: progress.total,
|
|
214
|
-
currentFile: progress.currentFile,
|
|
215
|
-
stage: progress.stage
|
|
216
|
-
});
|
|
217
|
-
} else {
|
|
218
|
-
this.indexingListeners.delete(webContents);
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
function createSearchElectronBridge(options) {
|
|
224
|
-
return new SearchElectronBridge(options);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export {
|
|
228
|
-
SearchElectronBridge,
|
|
229
|
-
createSearchElectronBridge
|
|
230
|
-
};
|
|
231
|
-
//# sourceMappingURL=chunk-MXWSYA4O.js.map
|
|
@@ -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, 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":[]}
|