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.
- package/README.md +5 -1
- package/package.json +1 -1
- 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
|
|
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
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: "
|
|
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
|
-
], "
|
|
585
|
+
], "lmstudio");
|
|
586
586
|
|
|
587
587
|
const { execFile } = await import("node:child_process");
|
|
588
588
|
const { promisify } = await import("node:util");
|
|
589
589
|
|
|
590
|
-
|
|
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
|
-
|
|
662
|
-
|
|
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."));
|