offgrid-ai 0.2.2 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +5 -1
  2. package/package.json +1 -1
  3. package/src/cli.mjs +53 -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.3",
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 } 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,45 @@ 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 ensureLmsOnPath = () => {
591
+ const lmsBin = join(homedir(), ".lmstudio", "bin");
592
+ if (!existsSync(join(lmsBin, "lms"))) return;
593
+ if (process.env.PATH.split(":").includes(lmsBin)) return;
594
+ process.env.PATH = `${lmsBin}:${process.env.PATH}`;
595
+ const profileFiles = [join(homedir(), ".zshrc"), join(homedir(), ".bash_profile")];
596
+ const line = `export PATH="$PATH:$HOME/.lmstudio/bin"`;
597
+ for (const f of profileFiles) {
598
+ if (!existsSync(f)) continue;
599
+ const content = readFileSync(f, "utf8");
600
+ if (content.includes(".lmstudio/bin")) continue;
601
+ appendFileSync(f, `\n${line}\n`);
602
+ }
603
+ };
604
+
605
+ if (backendChoice === "lmstudio") {
606
+ console.log(pc.cyan("Installing LM Studio via Homebrew..."));
607
+ try {
608
+ await promisify(execFile)("brew", ["install", "--cask", "lm-studio"], { stdio: "inherit" });
609
+ ensureLmsOnPath();
610
+ console.log(pc.green("✓ LM Studio installed"));
611
+ console.log(pc.yellow("\nDownload your first model:"));
612
+ console.log(pc.bold(" lms get qwen/qwen3.5-9b"));
613
+ console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
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
+ } else if (backendChoice === "ollama") {
591
619
  console.log(pc.cyan("Installing Ollama via Homebrew..."));
592
620
  try {
593
621
  await promisify(execFile)("brew", ["install", "ollama"], { stdio: "inherit" });
@@ -605,16 +633,6 @@ async function onboardFlow() {
605
633
  console.log(pc.red(`Failed to install Ollama: ${err.message}`));
606
634
  console.log(pc.dim("Install it manually from https://ollama.com"));
607
635
  }
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
636
  } else if (backendChoice === "omlx") {
619
637
  console.log(pc.cyan("Installing oMLX via Homebrew..."));
620
638
  try {
@@ -630,22 +648,23 @@ async function onboardFlow() {
630
648
  }
631
649
  } else if (backendChoice === "all") {
632
650
  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
651
  // LM Studio
642
652
  console.log(pc.cyan("Installing LM Studio via Homebrew..."));
643
653
  try {
644
654
  await promisify(execFile)("brew", ["install", "--cask", "lm-studio"], { stdio: "inherit" });
655
+ ensureLmsOnPath();
645
656
  installed.push("LM Studio");
646
657
  } catch {
647
658
  console.log(pc.yellow("LM Studio installation failed. Download from https://lmstudio.ai"));
648
659
  }
660
+ // Ollama
661
+ console.log(pc.cyan("Installing Ollama via Homebrew..."));
662
+ try {
663
+ await promisify(execFile)("brew", ["install", "ollama"], { stdio: "inherit" });
664
+ installed.push("Ollama");
665
+ } catch {
666
+ console.log(pc.yellow("Ollama installation failed. Install manually from https://ollama.com"));
667
+ }
649
668
  // oMLX
650
669
  console.log(pc.cyan("Installing oMLX via Homebrew..."));
651
670
  try {
@@ -657,9 +676,17 @@ async function onboardFlow() {
657
676
  }
658
677
  if (installed.length > 0) {
659
678
  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"));
679
+ console.log(pc.yellow("Next steps — download your first model:"));
680
+ if (installed.includes("LM Studio")) {
681
+ console.log(pc.bold(" lms get qwen/qwen3.5-9b"));
682
+ }
683
+ if (installed.includes("Ollama")) {
684
+ console.log(pc.bold(" ollama pull gemma3:4b"));
685
+ }
686
+ if (installed.includes("oMLX")) {
687
+ console.log(pc.bold(" omlx start"));
688
+ }
689
+ console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
663
690
  }
664
691
  } else {
665
692
  console.log(pc.dim("Run offgrid-ai again when you've set up a model backend."));