-
Notifications
You must be signed in to change notification settings - Fork 206
Feat/migrate to electron #214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
b382be7
4da3fe0
0cdd5a6
78a0911
84ce290
d4e59b6
0ac7c01
8938ea8
8297e10
1f7cb2f
9399c2a
bf3598e
009e03e
8f22f3f
1d156d3
09621db
8881db0
e9939b6
a7afd93
ea0364a
a9f9b55
c33b59d
9c92a01
cf98e74
736d1a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| # Deployment Guide: Running Amica Locally | ||
|
|
||
| This guide provides step-by-step instructions for setting up and running the Rust-powered version of Amica on your local machine. | ||
|
|
||
| ## 1. Prerequisites | ||
|
|
||
| Before you begin, you need to have the following software installed on your system: | ||
|
|
||
| * **Node.js:** Amica's user interface is built with Node.js. You will need version `18.18.0` or newer. You can download it from the [official Node.js website](https://nodejs.org/). | ||
| * **Rust:** The new backend is written in Rust. The easiest way to install Rust is by using `rustup`. You can find instructions at the [official Rust website](https://www.rust-lang.org/tools/install). | ||
| * **`text-generation-webui`:** You must have a working, pre-compiled version of `text-generation-webui`. You can find releases and setup instructions on its [GitHub repository](https://github.com/oobabooga/text-generation-webui). | ||
| > **Important:** When you launch `text-generation-webui`, you must enable the API with the `--api` flag. For example: `./start_linux.sh --api`. | ||
| * **(Linux Only) Build Dependencies:** On Linux, you will need to install a few extra packages for Tauri to build correctly. You can install them with the following command: | ||
| ```bash | ||
| sudo apt-get update | ||
| sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev | ||
| ``` | ||
| > **Note:** This project uses Tauri v1, which requires `libwebkit2gtk-4.0-dev`. If you are working on a project with Tauri v2 or newer, you will need to use `libwebkit2gtk-4.1-dev` instead. | ||
|
|
||
| ## 2. Installation and Configuration | ||
|
|
||
| Follow these steps to get the Amica project set up. | ||
|
|
||
| #### Step 1: Clone the Amica Repository | ||
|
|
||
| Open your terminal, navigate to where you want to store the project, and run the following command: | ||
|
|
||
| ```bash | ||
| git clone https://github.com/semperai/amica | ||
| cd amica | ||
| ``` | ||
|
|
||
| #### Step 2: Install JavaScript Dependencies | ||
|
|
||
| Once you are in the `amica` directory, run this command to install all the necessary frontend packages: | ||
|
|
||
| ```bash | ||
| npm install | ||
| ``` | ||
|
|
||
| #### Step 3: Configure the `text-generation-webui` Path | ||
|
|
||
| Amica needs to know where to find your `text-generation-webui` executable. This is configured in a `settings.json` file. | ||
|
|
||
| ##### How Configuration Works | ||
|
|
||
| Amica uses a default, bundled configuration file to start. To customize the settings, you must create your own `settings.json` file and place it in the correct application configuration directory for your operating system. | ||
|
|
||
| When Amica starts, it looks for `settings.json` in this order: | ||
| 1. **Your Custom `settings.json`:** It checks for the file in your OS's standard application config directory. | ||
| 2. **Default `settings.json`:** If no custom file is found, it falls back to the default settings bundled inside the application. The default has an empty path, so you **must** create a custom file. | ||
|
|
||
| ##### Creating Your Custom `settings.json` | ||
|
|
||
| 1. First, you need to find your application's configuration directory. The paths are typically: | ||
| * **Windows:** `%APPDATA%\\com.heyamica.dev` (you can paste this into the Explorer address bar) | ||
| * **macOS:** `~/Library/Application Support/com.heyamica.dev` | ||
| * **Linux:** `~/.config/com.heyamica.dev` | ||
|
|
||
| *(Note: The `com.heyamica.dev` directory might not exist until you run Amica at least once.)* | ||
|
|
||
| 2. Create a new file named `settings.json` inside that directory. | ||
|
|
||
| 3. Copy and paste the following content into your new `settings.json` file: | ||
| ```json | ||
| { | ||
| "text_generation_webui_path": "" | ||
| } | ||
| ``` | ||
|
|
||
| 4. Add the **full path** to your `text-generation-webui` executable inside the quotes. | ||
|
|
||
| * **Windows Example:** | ||
| ```json | ||
| { | ||
| "text_generation_webui_path": "C:\\Users\\YourUser\\Desktop\\text-generation-webui\\start.bat" | ||
| } | ||
| ``` | ||
| *(Note the double backslashes `\\`)* | ||
|
|
||
| * **Linux/macOS Example:** | ||
| ```json | ||
| { | ||
| "text_generation_webui_path": "/home/youruser/text-generation-webui/start.sh" | ||
| } | ||
| ``` | ||
|
|
||
| If Amica ever has trouble starting, it will show a dialog box explaining the configuration error. This usually means there's a typo in your `settings.json` file or the path to the executable is incorrect. | ||
|
|
||
| ## 3. Building the Application | ||
|
|
||
| Now that everything is configured, you can build the final, standalone executable. | ||
|
|
||
| Run the following command in your terminal. This process will compile the Rust backend and package it with the frontend into a single application. It may take several minutes. | ||
|
|
||
| ```bash | ||
| npm run tauri build | ||
| ``` | ||
|
|
||
| Once the build is complete, you will find the final application inside the `src-tauri/target/release/bundle/` directory, organized by platform and package type: | ||
| * **Windows:** The installer can be found under the `msi/` subdirectory, and the portable `.exe` under the `nsis/` subdirectory (or a similar name). | ||
| * **Linux:** The `.AppImage` can be found under the `appimage/` subdirectory, and the `.deb` package under the `deb/` subdirectory. | ||
| * **macOS:** The `.app` file is under the `macos/` subdirectory, and the `.dmg` installer is under the `dmg/` subdirectory. | ||
|
|
||
| ## 4. Running Amica | ||
|
|
||
| You can now run this executable file directly! There is no need for any further commands. | ||
|
|
||
| On the first run, be sure to open the in-app settings and configure the following: | ||
| * **Chatbot Backend:** Select **ChatGPT**. | ||
| * In the ChatGPT settings, you may need to enter a dummy API key (e.g., "123") for the UI to proceed, but the key itself is not used by the proxy. | ||
|
|
||
| That's it! Your self-contained, Rust-powered Amica application is now ready to use. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,127 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "use strict"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new (P || (P = Promise))(function (resolve, reject) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var __generator = (this && this.__generator) || function (thisArg, body) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function verb(n) { return function (v) { return step([n, v]); }; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function step(op) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (f) throw new TypeError("Generator is already executing."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (g && (g = 0, op[0] && (_ = 0)), _) try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (y = 0, t) op = [op[0] & 2, t.value]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (op[0]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 0: case 1: t = op; break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 4: _.label++; return { value: op[1], done: false }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 5: _.label++; y = op[1]; op = [0]; continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (t[2]) _.ops.pop(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _.trys.pop(); continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| op = body.call(thisArg, _); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var electron_1 = require("electron"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var path_1 = require("path"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is a temporary solution to get the native module path. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // In a real application, this would be handled by the build process. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var nativeModulePath = path_1.default.join(__dirname, "../rust-lib/amica-rust-lib.node"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var _a = require(nativeModulePath), proxy_request_blocking = _a.proxy_request_blocking, proxy_request_streaming = _a.proxy_request_streaming, start_sidecar = _a.start_sidecar, stop_sidecar = _a.stop_sidecar; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+43
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Native module loading path is fragile and may fail in production. The hardcoded relative path to the native module (line 43) could break in production builds where the directory structure might differ. Additionally, there's no error handling for the case where the module fails to load. Add error handling and consider using a more robust module resolution strategy: -var nativeModulePath = path_1.default.join(__dirname, "../rust-lib/amica-rust-lib.node");
-var _a = require(nativeModulePath), proxy_request_blocking = _a.proxy_request_blocking, proxy_request_streaming = _a.proxy_request_streaming, start_sidecar = _a.start_sidecar, stop_sidecar = _a.stop_sidecar;
+var nativeModulePath = path_1.default.join(__dirname, "../rust-lib/amica-rust-lib.node");
+var nativeModule;
+try {
+ nativeModule = require(nativeModulePath);
+} catch (error) {
+ console.error("Failed to load native module:", error);
+ electron_1.app.quit();
+ throw error;
+}
+var proxy_request_blocking = nativeModule.proxy_request_blocking;
+var proxy_request_streaming = nativeModule.proxy_request_streaming;
+var start_sidecar = nativeModule.start_sidecar;
+var stop_sidecar = nativeModule.stop_sidecar;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function createWindow() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var _this = this; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mainWindow = new electron_1.BrowserWindow({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: 1200, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height: 800, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| webPreferences: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| preload: path_1.default.join(__dirname, 'preload.js'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nodeIntegration: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contextIsolation: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var startUrl = process.env.ELECTRON_START_URL || path_1.default.join(__dirname, '../out/index.html'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (process.env.ELECTRON_START_URL) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.loadURL(startUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.openDevTools(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.loadFile(startUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| electron_1.ipcMain.handle('proxy_request_blocking', function (event, payload) { return __awaiter(_this, void 0, void 0, function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return __generator(this, function (_a) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (_a.label) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 0: return [4 /*yield*/, proxy_request_blocking(payload)]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 1: return [2 /*return*/, _a.sent()]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| electron_1.ipcMain.on('proxy_request_streaming', function (event, payload) { return __awaiter(_this, void 0, void 0, function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var onChunk, onEnd, onError; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return __generator(this, function (_a) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (_a.label) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChunk = function (chunk) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.send('stream-chunk', chunk); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onEnd = function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.send('stream-end'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onError = function (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.send('stream-error', error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [4 /*yield*/, proxy_request_streaming(payload, onChunk, onEnd, onError)]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 1: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _a.sent(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [2 /*return*/]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+72
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing error handling for IPC streaming callbacks. The Add try-catch error handling: electron_1.ipcMain.on('proxy_request_streaming', function (event, payload) { return __awaiter(_this, void 0, void 0, function () {
var onChunk, onEnd, onError;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
onChunk = function (chunk) {
mainWindow.webContents.send('stream-chunk', chunk);
};
onEnd = function () {
mainWindow.webContents.send('stream-end');
};
onError = function (error) {
mainWindow.webContents.send('stream-error', error);
};
- return [4 /*yield*/, proxy_request_streaming(payload, onChunk, onEnd, onError)];
+ return [4 /*yield*/, proxy_request_streaming(payload, onChunk, onEnd, onError)
+ .catch(function(error) {
+ onError(error.message || 'Unknown streaming error');
+ })];
case 1:
_a.sent();
return [2 /*return*/];📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| electron_1.ipcMain.handle('start_sidecar', function (event, payload) { return __awaiter(_this, void 0, void 0, function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var onOutput; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return __generator(this, function (_a) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (_a.label) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onOutput = function (output) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.send('sidecar-output', output); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [4 /*yield*/, start_sidecar(payload, onOutput)]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 1: return [2 /*return*/, _a.sent()]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| electron_1.ipcMain.handle('stop_sidecar', function () { return __awaiter(_this, void 0, void 0, function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return __generator(this, function (_a) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (_a.label) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 0: return [4 /*yield*/, stop_sidecar()]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 1: return [2 /*return*/, _a.sent()]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| electron_1.app.whenReady().then(function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createWindow(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| electron_1.app.on('activate', function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (electron_1.BrowserWindow.getAllWindows().length === 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createWindow(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| electron_1.app.on('window-all-closed', function () { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (process.platform !== 'darwin') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| electron_1.app.quit(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,72 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { app, BrowserWindow, ipcMain } from 'electron'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import path from 'path'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is a temporary solution to get the native module path. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // In a real application, this would be handled by the build process. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const nativeModulePath = path.join(__dirname, `../rust-lib/amica-rust-lib.node`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { proxy_request_blocking, proxy_request_streaming, start_sidecar, stop_sidecar } = require(nativeModulePath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+4
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Native module path resolution needs improvement for production. The comment acknowledges this is temporary, but the hardcoded relative path will likely fail in production builds. Also, there's no error handling if the module fails to load. Implement a more robust module resolution strategy: -// This is a temporary solution to get the native module path.
-// In a real application, this would be handled by the build process.
-const nativeModulePath = path.join(__dirname, `../rust-lib/amica-rust-lib.node`);
-const { proxy_request_blocking, proxy_request_streaming, start_sidecar, stop_sidecar } = require(nativeModulePath);
+function loadNativeModule() {
+ const possiblePaths = [
+ path.join(__dirname, '../rust-lib/amica-rust-lib.node'),
+ path.join(process.resourcesPath, 'amica-rust-lib.node'),
+ path.join(app.getAppPath(), 'rust-lib/amica-rust-lib.node'),
+ ];
+
+ for (const modulePath of possiblePaths) {
+ try {
+ if (require('fs').existsSync(modulePath)) {
+ return require(modulePath);
+ }
+ } catch (error) {
+ console.warn(`Failed to load native module from ${modulePath}:`, error);
+ }
+ }
+
+ throw new Error('Failed to load native module from any expected location');
+}
+
+const { proxy_request_blocking, proxy_request_streaming, start_sidecar, stop_sidecar } = loadNativeModule();📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function createWindow() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mainWindow = new BrowserWindow({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: 1200, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height: 800, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| webPreferences: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| preload: path.join(__dirname, 'preload.js'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nodeIntegration: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contextIsolation: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+10
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Missing security configurations for BrowserWindow. While Add additional security configurations: const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true,
+ sandbox: true,
+ webSecurity: true,
+ allowRunningInsecureContent: false,
+ experimentalFeatures: false,
},
+ // Additional window security
+ webPreferences: {
+ preload: path.join(__dirname, 'preload.js'),
+ nodeIntegration: false,
+ contextIsolation: true,
+ sandbox: true,
+ },
+ titleBarStyle: 'hiddenInset',
+ frame: process.platform !== 'win32',
});
+
+ // Prevent navigation to external URLs
+ mainWindow.webContents.on('will-navigate', (event, url) => {
+ if (!url.startsWith('file://') && !url.startsWith('http://localhost')) {
+ event.preventDefault();
+ }
+ });
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const startUrl = process.env.ELECTRON_START_URL || path.join(__dirname, '../out/index.html'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (process.env.ELECTRON_START_URL) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.loadURL(startUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.openDevTools(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.loadFile(startUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcMain.handle('proxy_request_blocking', async (event, payload) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await proxy_request_blocking(payload); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcMain.on('proxy_request_streaming', async (event, payload) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onChunk = (chunk) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.send('stream-chunk', chunk); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onEnd = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.send('stream-end'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onError = (error) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.send('stream-error', error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await proxy_request_streaming(payload, onChunk, onEnd, onError); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+33
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IPC handler uses incorrect method for streaming. The Either use - ipcMain.on('proxy_request_streaming', async (event, payload) => {
+ ipcMain.handle('proxy_request_streaming', async (event, payload) => {
const onChunk = (chunk) => {
- mainWindow.webContents.send('stream-chunk', chunk);
+ event.sender.send('stream-chunk', chunk);
};
const onEnd = () => {
- mainWindow.webContents.send('stream-end');
+ event.sender.send('stream-end');
};
const onError = (error) => {
- mainWindow.webContents.send('stream-error', error);
+ event.sender.send('stream-error', error);
};
- await proxy_request_streaming(payload, onChunk, onEnd, onError);
+ try {
+ await proxy_request_streaming(payload, onChunk, onEnd, onError);
+ return { success: true };
+ } catch (error) {
+ onError(error.message || 'Streaming failed');
+ throw error;
+ }
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcMain.handle('start_sidecar', async (event, payload) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onOutput = (output) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainWindow.webContents.send('sidecar-output', output); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await start_sidecar(payload, onOutput); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+46
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Potential race condition with window reference in async handlers. The IPC handlers capture Add window validity checks: ipcMain.handle('start_sidecar', async (event, payload) => {
const onOutput = (output) => {
- mainWindow.webContents.send('sidecar-output', output);
+ if (!mainWindow.isDestroyed()) {
+ mainWindow.webContents.send('sidecar-output', output);
+ }
};
return await start_sidecar(payload, onOutput);
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcMain.handle('stop_sidecar', async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await stop_sidecar(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app.whenReady().then(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createWindow(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app.on('activate', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (BrowserWindow.getAllWindows().length === 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createWindow(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app.on('window-all-closed', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (process.platform !== 'darwin') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| app.quit(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| var electron_1 = require("electron"); | ||
| electron_1.contextBridge.exposeInMainWorld('electronAPI', { | ||
| proxyRequestBlocking: function (payload) { return electron_1.ipcRenderer.invoke('proxy_request_blocking', payload); }, | ||
| proxyRequestStreaming: function (payload, onChunk, onEnd, onError) { | ||
| electron_1.ipcRenderer.on('stream-chunk', function (event, chunk) { return onChunk(chunk); }); | ||
| electron_1.ipcRenderer.on('stream-end', function (event) { return onEnd(); }); | ||
| electron_1.ipcRenderer.on('stream-error', function (event, error) { return onError(error); }); | ||
| electron_1.ipcRenderer.send('proxy_request_streaming', payload); | ||
| }, | ||
|
Comment on lines
+6
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Memory leak: Event listeners accumulate without cleanup. The same memory leak issues present in the TypeScript version exist here - event listeners are registered but never removed. This issue needs to be fixed in the TypeScript source file ( 🤖 Prompt for AI Agents |
||
| startSidecar: function (payload, onOutput) { | ||
| electron_1.ipcRenderer.on('sidecar-output', function (event, output) { return onOutput(output); }); | ||
| return electron_1.ipcRenderer.invoke('start_sidecar', payload); | ||
| }, | ||
| stopSidecar: function () { return electron_1.ipcRenderer.invoke('stop_sidecar'); }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,16 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { contextBridge, ipcRenderer } from 'electron'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contextBridge.exposeInMainWorld('electronAPI', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| proxyRequestBlocking: (payload) => ipcRenderer.invoke('proxy_request_blocking', payload), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| proxyRequestStreaming: (payload, onChunk, onEnd, onError) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcRenderer.on('stream-chunk', (event, chunk) => onChunk(chunk)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcRenderer.on('stream-end', (event) => onEnd()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcRenderer.on('stream-error', (event, error) => onError(error)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcRenderer.send('proxy_request_streaming', payload); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Memory leak: IPC event listeners are never removed. The Store and remove listeners after use: proxyRequestStreaming: (payload, onChunk, onEnd, onError) => {
- ipcRenderer.on('stream-chunk', (event, chunk) => onChunk(chunk));
- ipcRenderer.on('stream-end', (event) => onEnd());
- ipcRenderer.on('stream-error', (event, error) => onError(error));
+ const chunkHandler = (event, chunk) => onChunk(chunk);
+ const endHandler = (event) => {
+ ipcRenderer.removeListener('stream-chunk', chunkHandler);
+ ipcRenderer.removeListener('stream-end', endHandler);
+ ipcRenderer.removeListener('stream-error', errorHandler);
+ onEnd();
+ };
+ const errorHandler = (event, error) => {
+ ipcRenderer.removeListener('stream-chunk', chunkHandler);
+ ipcRenderer.removeListener('stream-end', endHandler);
+ ipcRenderer.removeListener('stream-error', errorHandler);
+ onError(error);
+ };
+
+ ipcRenderer.on('stream-chunk', chunkHandler);
+ ipcRenderer.on('stream-end', endHandler);
+ ipcRenderer.on('stream-error', errorHandler);
ipcRenderer.send('proxy_request_streaming', payload);
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| startSidecar: (payload, onOutput) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ipcRenderer.on('sidecar-output', (event, output) => onOutput(output)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ipcRenderer.invoke('start_sidecar', payload); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+11
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Memory leak: sidecar-output listener is never removed. Similar to the streaming function, the Return a cleanup function or handle to allow the caller to stop listening: startSidecar: (payload, onOutput) => {
- ipcRenderer.on('sidecar-output', (event, output) => onOutput(output));
- return ipcRenderer.invoke('start_sidecar', payload);
+ const outputHandler = (event, output) => onOutput(output);
+ ipcRenderer.on('sidecar-output', outputHandler);
+ return ipcRenderer.invoke('start_sidecar', payload).then((result) => {
+ // Return result with a cleanup function
+ return {
+ ...result,
+ cleanup: () => ipcRenderer.removeListener('sidecar-output', outputHandler)
+ };
+ });
},Alternatively, remove the listener when the sidecar is stopped: stopSidecar: () => ipcRenderer.invoke('stop_sidecar'),
+ stopSidecar: () => {
+ ipcRenderer.removeAllListeners('sidecar-output');
+ return ipcRenderer.invoke('stop_sidecar');
+ },
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stopSidecar: () => ipcRenderer.invoke('stop_sidecar'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove TypeScript transpilation artifacts from version control.
This file appears to be the transpiled JavaScript output of
electron/main.ts. Having both the TypeScript source and its compiled JavaScript output in version control can lead to synchronization issues and merge conflicts.Consider:
electron/*.jsto.gitignore🤖 Prompt for AI Agents