lemonade-interactive-loader 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -1,27 +1,45 @@
1
- # šŸ‹ Lemonade Launcher
1
+ # šŸ‹ Lemonade Interactive Loader
2
2
 
3
- [![License: ISC](https://img.shields.io/badge/Lxicense-ISC-blue.svg)](https://opensource.org/licenses/ISC)
4
- [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org/)
3
+ [![npm version](https://badge.fury.io/js/lemonade-interactive-loader.svg)](https://www.npmjs.com/package/lemonade-interactive-loader)
4
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D14.0.0-brightgreen.svg)](https://nodejs.org/)
5
6
  [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)](#)
6
7
 
7
8
  **The easiest way to manage llama.cpp builds and run Lemonade Server**
8
9
 
9
- Lemonade Launcher is a professional, cross-platform CLI tool that simplifies downloading llama.cpp builds and launching Lemonade Server with an intuitive interactive interface.
10
+ Lemonade Interactive Loader is a professional, cross-platform CLI tool that simplifies downloading llama.cpp builds and launching Lemonade Server with an intuitive interactive interface.
10
11
 
11
12
  ## šŸš€ Quick Start
12
13
 
13
14
  ### Installation
14
15
 
16
+ **Option 1: Install globally via npm**
15
17
  ```bash
16
- git clone https://github.com/yourusername/lemonade-launcher.git
17
- cd lemonade-launcher
18
+ npm install -g lemonade-interactive-loader
19
+ ```
20
+
21
+ **Option 2: Run without installing (via npx)**
22
+ ```bash
23
+ npx lemonade-interactive-loader
24
+ ```
25
+
26
+ **Option 3: Install from source**
27
+ ```bash
28
+ git clone https://github.com/yourusername/lemonade-interactive-loader.git
29
+ cd lemonade-interactive-loader
18
30
  npm install
19
31
  ```
20
32
 
21
33
  ### Running the Tool
22
34
 
23
35
  ```bash
24
- # Start the interactive CLI
36
+ # If installed globally
37
+ lemonade-loader
38
+
39
+ # Or via npx (no installation needed)
40
+ npx lemonade-interactive-loader
41
+
42
+ # From source
25
43
  npm start
26
44
  # or
27
45
  node index.js
@@ -55,7 +73,7 @@ The main menu adapts based on whether you have a configuration saved:
55
73
  ```
56
74
 
57
75
  | Command | Description |
58
- |---------|-------------||
76
+ |---------|-------------|
59
77
  | **ā–¶ļø Start Server** | Launch Lemonade Server with saved config (when config exists) |
60
78
  | **āœļø Edit Configuration** | Update your saved settings interactively |
61
79
  | **šŸ‘ļø View Configuration** | See your current configuration and installed builds |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lemonade-interactive-loader",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Interactive CLI tool to launch Lemonade Server with custom arguments and download llama.cpp releases - Cross-platform (Windows/Linux)",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/cli/menu.js CHANGED
@@ -3,7 +3,7 @@ const { loadConfig } = require('../config');
3
3
  const { getAllInstalledAssets, getLlamaServerPath, downloadAndExtractLlamaCpp, deleteInstalledAsset } = require('../services/asset-manager');
4
4
  const { selectLlamaCppRelease, selectAsset, askLaunchServer } = require('./prompts');
5
5
  const { runSetupWizard } = require('./setup-wizard');
6
- const { launchLemonadeServer } = require('../services/server');
6
+ const { launchLemonadeServer, setupShutdownHandlers } = require('../services/server');
7
7
  const { inferBackendType, formatBytes } = require('../utils/system');
8
8
 
9
9
  /**
@@ -321,6 +321,8 @@ async function handleCommand(command) {
321
321
  console.log('No configuration found. Please run "setup" first.');
322
322
  return;
323
323
  }
324
+ // Set up shutdown handlers to kill server on exit
325
+ setupShutdownHandlers();
324
326
  await launchLemonadeServer(config);
325
327
  break;
326
328
  }
@@ -349,6 +351,10 @@ async function runCLI() {
349
351
  }
350
352
 
351
353
  console.log('\nšŸ‘‹ Goodbye!\n');
354
+
355
+ // Ensure server is shut down when exiting the CLI
356
+ const { shutdownLemonadeServer } = require('../services/server');
357
+ shutdownLemonadeServer();
352
358
  }
353
359
 
354
360
  module.exports = {
@@ -1,9 +1,12 @@
1
1
  const fs = require('fs');
2
- const { execSync } = require('child_process');
2
+ const { execSync, spawn } = require('child_process');
3
3
  const { LEMONADE_SERVER_DEFAULT_PATH } = require('../config/constants');
4
4
  const { findLlamaServer } = require('../utils/system');
5
5
  const { getLlamaServerPath } = require('./asset-manager');
6
6
 
7
+ // Track the server process for graceful shutdown
8
+ let serverProcess = null;
9
+
7
10
  /**
8
11
  * Build server command arguments
9
12
  * @param {Object} config - Server configuration
@@ -154,21 +157,80 @@ async function launchLemonadeServer(config) {
154
157
  console.log('\nStarting server...\n');
155
158
 
156
159
  try {
157
- const command = formatCommand(serverPath, args, {});
158
- execSync(command, {
160
+ // Parse the command into executable and arguments
161
+ const parts = command.trim().split(/\s+/);
162
+ const executable = parts[0];
163
+ const execArgs = parts.slice(1);
164
+
165
+ // Spawn the server process
166
+ serverProcess = spawn(executable, execArgs, {
159
167
  stdio: 'inherit',
160
168
  env: process.env
161
169
  });
170
+
171
+ // Handle process exit
172
+ serverProcess.on('close', (code) => {
173
+ console.log(`\nServer exited with code ${code}`);
174
+ serverProcess = null;
175
+ });
176
+
177
+ serverProcess.on('error', (err) => {
178
+ console.error(`Server process error: ${err.message}`);
179
+ });
180
+
162
181
  } catch (error) {
163
- console.error(`Server exited with error code: ${error.status}`);
182
+ console.error(`Server exited with error: ${error.message}`);
164
183
  if (error.status !== null) {
165
184
  process.exit(error.status);
166
185
  }
167
186
  }
168
187
  }
169
188
 
189
+ /**
190
+ * Gracefully shutdown the lemonade server
191
+ */
192
+ function shutdownLemonadeServer() {
193
+ if (serverProcess) {
194
+ console.log('\n\nShutting down Lemonade Server...');
195
+
196
+ // Try graceful shutdown first (SIGTERM)
197
+ serverProcess.on('exit', () => {
198
+ console.log('Server shut down successfully.');
199
+ });
200
+
201
+ serverProcess.on('error', (err) => {
202
+ console.error(`Error shutting down server: ${err.message}`);
203
+ });
204
+
205
+ // Send SIGTERM signal
206
+ serverProcess.kill('SIGTERM');
207
+
208
+ // Force kill after 5 seconds if still running
209
+ setTimeout(() => {
210
+ if (serverProcess && !serverProcess.killed) {
211
+ console.log('Force killing server...');
212
+ serverProcess.kill('SIGKILL');
213
+ }
214
+ }, 5000);
215
+ }
216
+ }
217
+
218
+ // Set up signal handlers for graceful shutdown
219
+ function setupShutdownHandlers() {
220
+ const shutdown = (signal) => {
221
+ console.log(`\n\nReceived ${signal}. Shutting down...`);
222
+ shutdownLemonadeServer();
223
+ setTimeout(() => process.exit(0), 2000);
224
+ };
225
+
226
+ process.on('SIGINT', () => shutdown('SIGINT')); // Ctrl+C
227
+ process.on('SIGTERM', () => shutdown('SIGTERM')); // Termination signal
228
+ }
229
+
170
230
  module.exports = {
171
231
  buildServerArgs,
172
232
  formatCommand,
173
- launchLemonadeServer
233
+ launchLemonadeServer,
234
+ shutdownLemonadeServer,
235
+ setupShutdownHandlers
174
236
  };