offgrid-ai 0.2.2 → 0.2.4

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.
Files changed (3) hide show
  1. package/README.md +5 -1
  2. package/package.json +1 -1
  3. package/src/cli.mjs +61 -26
package/README.md CHANGED
@@ -59,6 +59,7 @@ curl -fsSL https://raw.githubusercontent.com/eeshansrivastava89/offgrid-ai/main/
59
59
 
60
60
  | Backend | Type | Auto-detected |
61
61
  |---|---|---|
62
+ | **LM Studio** | Visual model browser + CLI (`lms`) | ✓ models in `~/.lmstudio/models/` |
62
63
  | **llama.cpp** | Local server | ✓ GGUF models in `~/.lmstudio/models/` |
63
64
  | **llama.cpp MTP** | Local server (speculative decoding) | ✓ MTP detected from model metadata |
64
65
  | **Ollama** | Managed server | ✓ via `localhost:11434` |
@@ -70,7 +71,10 @@ When you run `offgrid-ai` for the first time on a fresh machine:
70
71
 
71
72
  1. **Homebrew** — Required. Offered to install if missing.
72
73
  2. **llama-server** — Required for GGUF models. Offered to install via Homebrew.
73
- 3. **Model backend** — At least one is needed: LM Studio, Ollama, or oMLX.
74
+ 3. **Model backend** — At least one is needed (LM Studio recommended):
75
+ - **LM Studio** — visual model browser + `lms` CLI, download models with `lms get qwen/qwen3.5-9b`
76
+ - **Ollama** — models download on demand with `ollama pull`
77
+ - **oMLX** — Apple Silicon optimized
74
78
  4. **Models** — If no models found, tells you where to get them.
75
79
 
76
80
  Subsequent runs skip everything that's already installed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "offgrid-ai",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Privacy-first CLI for running local LLMs — discover, configure, run, benchmark",
5
5
  "author": "Eeshan Srivastava (https://eeshans.com)",
6
6
  "type": "module",
package/src/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { homedir } from "node:os";
2
- import { existsSync, statSync, rmSync } from "node:fs";
2
+ import { existsSync, statSync, rmSync, readFileSync, appendFileSync, mkdirSync, copyFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { ensureDirs, findLlamaServer, hasHomebrew, DATA_DIR } from "./config.mjs";
5
5
  import { scanGgufModels } from "./scan.mjs";
@@ -577,17 +577,53 @@ async function onboardFlow() {
577
577
  console.log(pc.dim("You need at least one model backend to use offgrid-ai.\n"));
578
578
 
579
579
  const backendChoice = await prompt.choice("Install a model backend?", [
580
+ { value: "lmstudio", label: "LM Studio (recommended)", hint: "brew install --cask lm-studio — visual model browser + CLI" },
580
581
  { value: "ollama", label: "Ollama", hint: "brew install ollama — models download on demand" },
581
- { value: "lmstudio", label: "LM Studio", hint: "brew install --cask lm-studio — visual model browser" },
582
582
  { value: "omlx", label: "oMLX", hint: "brew tap jundot/omlx && brew install omlx — Apple Silicon optimized" },
583
- { value: "all", label: "Install all three", hint: "Ollama + LM Studio + oMLX" },
583
+ { value: "all", label: "Install all three", hint: "LM Studio + Ollama + oMLX" },
584
584
  { value: "skip", label: "Skip for now", hint: "I'll set up models myself" },
585
- ], "ollama");
585
+ ], "lmstudio");
586
586
 
587
587
  const { execFile } = await import("node:child_process");
588
588
  const { promisify } = await import("node:util");
589
589
 
590
- if (backendChoice === "ollama") {
590
+ const lmsAppBundle = "/Applications/LM Studio.app/Contents/Resources/app/.webpack/lms";
591
+ const lmsBin = join(homedir(), ".lmstudio", "bin");
592
+
593
+ const ensureLmsOnPath = () => {
594
+ const lmsDest = join(lmsBin, "lms");
595
+ // Bootstrap lms from app bundle if not yet in ~/.lmstudio/bin
596
+ if (!existsSync(lmsDest) && existsSync(lmsAppBundle)) {
597
+ mkdirSync(lmsBin, { recursive: true });
598
+ copyFileSync(lmsAppBundle, lmsDest);
599
+ }
600
+ if (!existsSync(lmsDest)) return;
601
+ if (process.env.PATH.split(":").includes(lmsBin)) return;
602
+ process.env.PATH = `${lmsBin}:${process.env.PATH}`;
603
+ const profileFiles = [join(homedir(), ".zshrc"), join(homedir(), ".bash_profile")];
604
+ const line = `export PATH="$PATH:$HOME/.lmstudio/bin"`;
605
+ for (const f of profileFiles) {
606
+ if (!existsSync(f)) continue;
607
+ const content = readFileSync(f, "utf8");
608
+ if (content.includes(".lmstudio/bin")) continue;
609
+ appendFileSync(f, `\n${line}\n`);
610
+ }
611
+ };
612
+
613
+ if (backendChoice === "lmstudio") {
614
+ console.log(pc.cyan("Installing LM Studio via Homebrew..."));
615
+ try {
616
+ await promisify(execFile)("brew", ["install", "--cask", "lm-studio"], { stdio: "inherit" });
617
+ ensureLmsOnPath();
618
+ console.log(pc.green("✓ LM Studio installed"));
619
+ console.log(pc.yellow("\nDownload your first model:"));
620
+ console.log(pc.bold(" lms get qwen/qwen3.5-9b"));
621
+ console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
622
+ } catch (err) {
623
+ console.log(pc.red(`Failed to install LM Studio: ${err.message}`));
624
+ console.log(pc.dim("Download it manually from https://lmstudio.ai"));
625
+ }
626
+ } else if (backendChoice === "ollama") {
591
627
  console.log(pc.cyan("Installing Ollama via Homebrew..."));
592
628
  try {
593
629
  await promisify(execFile)("brew", ["install", "ollama"], { stdio: "inherit" });
@@ -605,16 +641,6 @@ async function onboardFlow() {
605
641
  console.log(pc.red(`Failed to install Ollama: ${err.message}`));
606
642
  console.log(pc.dim("Install it manually from https://ollama.com"));
607
643
  }
608
- } else if (backendChoice === "lmstudio") {
609
- console.log(pc.cyan("Installing LM Studio via Homebrew..."));
610
- try {
611
- await promisify(execFile)("brew", ["install", "--cask", "lm-studio"], { stdio: "inherit" });
612
- console.log(pc.green("✓ LM Studio installed"));
613
- console.log(pc.yellow("\nOpen LM Studio to browse and download models, then run offgrid-ai again."));
614
- } catch (err) {
615
- console.log(pc.red(`Failed to install LM Studio: ${err.message}`));
616
- console.log(pc.dim("Download it manually from https://lmstudio.ai"));
617
- }
618
644
  } else if (backendChoice === "omlx") {
619
645
  console.log(pc.cyan("Installing oMLX via Homebrew..."));
620
646
  try {
@@ -630,22 +656,23 @@ async function onboardFlow() {
630
656
  }
631
657
  } else if (backendChoice === "all") {
632
658
  let installed = [];
633
- // Ollama
634
- console.log(pc.cyan("Installing Ollama via Homebrew..."));
635
- try {
636
- await promisify(execFile)("brew", ["install", "ollama"], { stdio: "inherit" });
637
- installed.push("Ollama");
638
- } catch {
639
- console.log(pc.yellow("Ollama installation failed. Install manually from https://ollama.com"));
640
- }
641
659
  // LM Studio
642
660
  console.log(pc.cyan("Installing LM Studio via Homebrew..."));
643
661
  try {
644
662
  await promisify(execFile)("brew", ["install", "--cask", "lm-studio"], { stdio: "inherit" });
663
+ ensureLmsOnPath();
645
664
  installed.push("LM Studio");
646
665
  } catch {
647
666
  console.log(pc.yellow("LM Studio installation failed. Download from https://lmstudio.ai"));
648
667
  }
668
+ // Ollama
669
+ console.log(pc.cyan("Installing Ollama via Homebrew..."));
670
+ try {
671
+ await promisify(execFile)("brew", ["install", "ollama"], { stdio: "inherit" });
672
+ installed.push("Ollama");
673
+ } catch {
674
+ console.log(pc.yellow("Ollama installation failed. Install manually from https://ollama.com"));
675
+ }
649
676
  // oMLX
650
677
  console.log(pc.cyan("Installing oMLX via Homebrew..."));
651
678
  try {
@@ -657,9 +684,17 @@ async function onboardFlow() {
657
684
  }
658
685
  if (installed.length > 0) {
659
686
  console.log(pc.green(`\n✓ Installed: ${installed.join(", ")}`));
660
- console.log(pc.yellow("Next steps:"));
661
- console.log(pc.bold(" ollama pull gemma3:4b"));
662
- console.log(pc.dim("Or open LM Studio to browse models, or run: omlx start"));
687
+ console.log(pc.yellow("Next steps — download your first model:"));
688
+ if (installed.includes("LM Studio")) {
689
+ console.log(pc.bold(" lms get qwen/qwen3.5-9b"));
690
+ }
691
+ if (installed.includes("Ollama")) {
692
+ console.log(pc.bold(" ollama pull gemma3:4b"));
693
+ }
694
+ if (installed.includes("oMLX")) {
695
+ console.log(pc.bold(" omlx start"));
696
+ }
697
+ console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
663
698
  }
664
699
  } else {
665
700
  console.log(pc.dim("Run offgrid-ai again when you've set up a model backend."));