diff --git a/electron/main/index.ts b/electron/main/index.ts
index a720720db..02cedbb87 100644
--- a/electron/main/index.ts
+++ b/electron/main/index.ts
@@ -89,6 +89,21 @@ app.commandLine.appendSwitch('max_old_space_size', '4096');
app.commandLine.appendSwitch('enable-features', 'MemoryPressureReduction');
app.commandLine.appendSwitch('renderer-process-limit', '8');
+// ==================== protocol privileges ====================
+// Register custom protocol privileges before app ready
+protocol.registerSchemesAsPrivileged([
+ {
+ scheme: 'localfile',
+ privileges: {
+ standard: true,
+ secure: true,
+ supportFetchAPI: true,
+ corsEnabled: false,
+ bypassCSP: false,
+ },
+ },
+]);
+
// ==================== app config ====================
process.env.APP_ROOT = MAIN_DIST;
process.env.VITE_PUBLIC = VITE_PUBLIC;
@@ -1577,12 +1592,24 @@ app.whenReady().then(async () => {
});
// ==================== protocol handle ====================
- protocol.handle('localfile', async (request) => {
+ // Register protocol handler for both default session and main window session
+ const protocolHandler = async (request: Request) => {
const url = decodeURIComponent(request.url.replace('localfile://', ''));
const filePath = path.normalize(url);
+ log.info(`[PROTOCOL] Handling localfile request: ${request.url}`);
+ log.info(`[PROTOCOL] Decoded path: ${filePath}`);
+
try {
+ // Check if file exists
+ const fileExists = await fsp.access(filePath).then(() => true).catch(() => false);
+ if (!fileExists) {
+ log.error(`[PROTOCOL] File not found: ${filePath}`);
+ return new Response('File Not Found', { status: 404 });
+ }
+
const data = await fsp.readFile(filePath);
+ log.info(`[PROTOCOL] Successfully read file, size: ${data.length} bytes`);
// set correct Content-Type according to file extension
const ext = path.extname(filePath).toLowerCase();
@@ -1596,17 +1623,46 @@ app.whenReady().then(async () => {
case '.htm':
contentType = 'text/html';
break;
+ case '.png':
+ contentType = 'image/png';
+ break;
+ case '.jpg':
+ case '.jpeg':
+ contentType = 'image/jpeg';
+ break;
+ case '.gif':
+ contentType = 'image/gif';
+ break;
+ case '.svg':
+ contentType = 'image/svg+xml';
+ break;
+ case '.webp':
+ contentType = 'image/webp';
+ break;
}
+ log.info(`[PROTOCOL] Returning file with Content-Type: ${contentType}`);
+
return new Response(new Uint8Array(data), {
headers: {
'Content-Type': contentType,
+ 'Content-Length': data.length.toString(),
},
});
} catch (err) {
- return new Response('Not Found', { status: 404 });
+ log.error(`[PROTOCOL] Error reading file: ${err}`);
+ return new Response('Internal Server Error', { status: 500 });
}
- });
+ };
+
+ // Register on default session
+ protocol.handle('localfile', protocolHandler);
+
+ // Also register on main window session
+ const mainSession = session.fromPartition('persist:main_window');
+ mainSession.protocol.handle('localfile', protocolHandler);
+
+ log.info('[PROTOCOL] Registered localfile protocol on both default and main_window sessions');
// ==================== initialize app ====================
initializeApp();
diff --git a/index.html b/index.html
index 9c2d1457f..5c3481fab 100644
--- a/index.html
+++ b/index.html
@@ -6,7 +6,7 @@
Eigent
diff --git a/src/components/AddWorker/ToolSelect.tsx b/src/components/AddWorker/ToolSelect.tsx
index 0b29ebd66..92922f92e 100644
--- a/src/components/AddWorker/ToolSelect.tsx
+++ b/src/components/AddWorker/ToolSelect.tsx
@@ -56,7 +56,7 @@ const ToolSelect = forwardRef<
const [integrations, setIntegrations] = useState([]);
const fetchIntegrationsData = (keyword?: string) => {
proxyFetchGet("/api/config/info").then((res) => {
- if (res && typeof res === "object") {
+ if (res && typeof res === "object" && !res.error) {
const baseURL = getProxyBaseURL();
const list = Object.entries(res)
@@ -187,7 +187,13 @@ const ToolSelect = forwardRef<
};
});
setIntegrations(list);
+ } else {
+ console.error("Failed to fetch integrations:", res);
+ setIntegrations([]);
}
+ }).catch((error) => {
+ console.error("Error fetching integrations:", error);
+ setIntegrations([]);
});
};
@@ -217,7 +223,16 @@ const ToolSelect = forwardRef<
page: 1,
size: 100,
}).then((res) => {
- setAllMcpList(res.items);
+ // Add defensive check for API errors
+ if (res && res.items && Array.isArray(res.items)) {
+ setAllMcpList(res.items);
+ } else {
+ console.error("Failed to fetch MCPs:", res);
+ setAllMcpList([]);
+ }
+ }).catch((error) => {
+ console.error("Error fetching MCPs:", error);
+ setAllMcpList([]);
});
};
@@ -228,7 +243,7 @@ const ToolSelect = forwardRef<
if (Array.isArray(res)) {
ids = res.map((item: any) => item.mcp_id);
dataList = res;
- } else if (Array.isArray(res.items)) {
+ } else if (res && Array.isArray(res.items)) {
ids = res.items.map((item: any) => item.mcp_id);
dataList = res.items;
}
@@ -236,14 +251,22 @@ const ToolSelect = forwardRef<
const customMcpList = dataList.filter((item: any) => item.mcp_id === 0);
setCustomMcpList(customMcpList);
+ }).catch((error) => {
+ console.error("Error fetching installed MCPs:", error);
+ setInstalledIds([]);
+ setCustomMcpList([]);
});
};
// only surface installed MCPs from the market list
useEffect(() => {
- if (!installedIds.length) {
+ // Add defensive check and fix logic: should filter when installedIds has items
+ if (Array.isArray(allMcpList) && installedIds.length > 0) {
const filtered = allMcpList.filter((item) => installedIds.includes(item.id));
setMcpList(filtered);
+ } else if (Array.isArray(allMcpList)) {
+ // If no installed IDs, show empty list instead of all
+ setMcpList([]);
}
}, [allMcpList, installedIds]);
diff --git a/src/components/Folder/index.tsx b/src/components/Folder/index.tsx
index 7834bfc4e..b12816221 100644
--- a/src/components/Folder/index.tsx
+++ b/src/components/Folder/index.tsx
@@ -183,7 +183,23 @@ export default function Folder({ data }: { data?: Agent }) {
setLoading(true);
console.log("file", JSON.parse(JSON.stringify(file)));
- // all files call open-file interface, the backend handles download and parsing
+ // For PDF files, use data URL instead of custom protocol
+ if (file.type === "pdf") {
+ window.ipcRenderer
+ .invoke("read-file-dataurl", file.path)
+ .then((dataUrl: string) => {
+ setSelectedFile({ ...file, content: dataUrl });
+ chatStore.setSelectedFile(chatStore.activeTaskId as string, file);
+ setLoading(false);
+ })
+ .catch((error) => {
+ console.error("read-file-dataurl error:", error);
+ setLoading(false);
+ });
+ return;
+ }
+
+ // all other files call open-file interface, the backend handles download and parsing
window.ipcRenderer
.invoke("open-file", file.type, file.path, isShowSourceCode)
.then((res) => {
@@ -539,10 +555,7 @@ export default function Folder({ data }: { data?: Agent }) {
) : selectedFile.type === "pdf" ? (