lemonade-interactive-loader 1.0.1 → 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
@@ -73,7 +73,7 @@ The main menu adapts based on whether you have a configuration saved:
73
73
  ```
74
74
 
75
75
  | Command | Description |
76
- |---------|-------------||
76
+ |---------|-------------|
77
77
  | **▶️ Start Server** | Launch Lemonade Server with saved config (when config exists) |
78
78
  | **✏️ Edit Configuration** | Update your saved settings interactively |
79
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.1",
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
  };