lemonade-interactive-loader 1.0.2 → 1.0.3

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
@@ -169,7 +169,7 @@ Point Lemonade Server to your existing model directory (like LM Studio's):
169
169
 
170
170
  Configuration is automatically saved and loaded:
171
171
 
172
- - **Location**: `~/.lemonade-launcher/config.json` (Linux/macOS) or `%USERPROFILE%\.lemonade-launcher\config.json` (Windows)
172
+ - **Location**: `~/.lemonade-interactive-launcher/config.json` (Linux/macOS) or `%USERPROFILE%\.lemonade-interactive-launcher\config.json` (Windows)
173
173
  - **Format**: JSON
174
174
  - **Auto-saved**: After every setup or edit
175
175
 
@@ -250,7 +250,7 @@ Run Command Prompt or PowerShell as Administrator.
250
250
 
251
251
  #### Build download fails
252
252
  - Check your internet connection
253
- - Ensure you have write permissions to `~/.lemonade-launcher/`
253
+ - Ensure you have write permissions to `~/.lemonade-interactive-launcher/`
254
254
  - Try downloading the asset manually from GitHub
255
255
 
256
256
  #### Server won't start
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lemonade-interactive-loader",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
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
@@ -263,15 +263,20 @@ async function resetConfiguration() {
263
263
  /**
264
264
  * Handle main menu command
265
265
  * @param {string} command - Selected command
266
+ * @returns {Promise<boolean>} True if should exit the app
266
267
  */
267
268
  async function handleCommand(command) {
269
+ let shouldExit = false;
270
+
268
271
  switch (command) {
269
272
  case 'setup':
270
273
  await runSetupWizard(false);
271
274
  if (await askLaunchServer()) {
272
275
  const config = loadConfig();
273
276
  if (Object.keys(config).length > 0) {
277
+ setupShutdownHandlers();
274
278
  await launchLemonadeServer(config);
279
+ shouldExit = true;
275
280
  }
276
281
  }
277
282
  break;
@@ -281,7 +286,9 @@ async function handleCommand(command) {
281
286
  if (await askLaunchServer()) {
282
287
  const config = loadConfig();
283
288
  if (Object.keys(config).length > 0) {
289
+ setupShutdownHandlers();
284
290
  await launchLemonadeServer(config);
291
+ shouldExit = true;
285
292
  }
286
293
  }
287
294
  break;
@@ -294,7 +301,7 @@ async function handleCommand(command) {
294
301
  await resetConfiguration();
295
302
  break;
296
303
 
297
-
304
+
298
305
 
299
306
  case 'manage':
300
307
  let manageAction;
@@ -319,13 +326,17 @@ async function handleCommand(command) {
319
326
  const config = loadConfig();
320
327
  if (Object.keys(config).length === 0) {
321
328
  console.log('No configuration found. Please run "setup" first.');
322
- return;
329
+ return false;
323
330
  }
324
331
  // Set up shutdown handlers to kill server on exit
325
332
  setupShutdownHandlers();
326
333
  await launchLemonadeServer(config);
334
+ // After server exits, exit the app completely
335
+ shouldExit = true;
327
336
  break;
328
337
  }
338
+
339
+ return shouldExit;
329
340
  }
330
341
 
331
342
  /**
@@ -336,7 +347,12 @@ async function runCLI() {
336
347
 
337
348
  while (continueRunning) {
338
349
  const command = await showMainMenu();
339
- await handleCommand(command);
350
+ const shouldExit = await handleCommand(command);
351
+
352
+ // Exit the app if we just exited from the server
353
+ if (shouldExit) {
354
+ return;
355
+ }
340
356
 
341
357
  const { continueRunning: shouldContinue } = await inquirer.prompt([
342
358
  {
@@ -63,7 +63,7 @@ async function runSetupWizard(isEdit = false) {
63
63
  type: 'confirm',
64
64
  name: 'useCustomModelDir',
65
65
  message: 'Is there another model directory to use? (example: LM Studio)',
66
- default: false
66
+ default: hasExistingModelDir
67
67
  }
68
68
  ]);
69
69
 
@@ -2,14 +2,14 @@ const path = require('path');
2
2
  const os = require('os');
3
3
 
4
4
  // Configuration directories
5
- const USER_CONFIG_DIR = path.join(os.homedir(), '.lemonade-launcher');
5
+ const USER_CONFIG_DIR = path.join(os.homedir(), '.lemonade-interactive-launcher');
6
6
  const USER_CONFIG_FILE = path.join(USER_CONFIG_DIR, 'config.json');
7
- const DEFAULT_LLAMACPP_INSTALL_DIR = path.join(os.homedir(), '.lemonade-launcher', 'llama-cpp');
7
+ const DEFAULT_LLAMACPP_INSTALL_DIR = path.join(os.homedir(), '.lemonade-interactive-launcher', 'llama-cpp');
8
8
 
9
9
  // GitHub API
10
10
  const GITHUB_RELEASES_URL = 'https://api.github.com/repos/ggml-org/llama.cpp/releases';
11
11
  const GITHUB_API_HEADERS = {
12
- 'User-Agent': 'lemonade-launcher',
12
+ 'User-Agent': 'lemonade-interactive-launcher',
13
13
  'Accept': 'application/vnd.github.v3+json'
14
14
  };
15
15
 
@@ -20,7 +20,7 @@ function downloadFile(url, outputPath) {
20
20
 
21
21
  const file = fs.createWriteStream(outputPath);
22
22
 
23
- const req = protocol.get(url, { headers: { 'User-Agent': 'lemonade-launcher' } }, (res) => {
23
+ const req = protocol.get(url, { headers: { 'User-Agent': 'lemonade-interactive-launcher' } }, (res) => {
24
24
  if (res.statusCode === 302 || res.statusCode === 301) {
25
25
  downloadFile(res.headers.location, outputPath)
26
26
  .then(resolve)
@@ -157,25 +157,35 @@ async function launchLemonadeServer(config) {
157
157
  console.log('\nStarting server...\n');
158
158
 
159
159
  try {
160
+ // Build the command for spawning
161
+ const commandStr = formatCommand(serverPath, args, {});
162
+
160
163
  // Parse the command into executable and arguments
161
- const parts = command.trim().split(/\s+/);
164
+ const parts = commandStr.trim().split(/\s+/);
162
165
  const executable = parts[0];
163
166
  const execArgs = parts.slice(1);
164
167
 
168
+ console.log(`Spawning: ${executable} ${execArgs.join(' ')}`);
169
+
165
170
  // Spawn the server process
166
171
  serverProcess = spawn(executable, execArgs, {
167
172
  stdio: 'inherit',
168
173
  env: process.env
169
174
  });
170
175
 
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}`);
176
+ // Wait for the process to exit (blocking)
177
+ await new Promise((resolve, reject) => {
178
+ serverProcess.on('close', (code) => {
179
+ console.log(`\nServer exited with code ${code}`);
180
+ serverProcess = null;
181
+ resolve(code);
182
+ });
183
+
184
+ serverProcess.on('error', (err) => {
185
+ console.error(`Server process error: ${err.message}`);
186
+ serverProcess = null;
187
+ reject(err);
188
+ });
179
189
  });
180
190
 
181
191
  } catch (error) {