offgrid-ai 0.2.7 → 0.2.8
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/package.json +1 -1
- package/src/cli.mjs +22 -112
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { homedir, totalmem } from "node:os";
|
|
2
|
-
import { existsSync, statSync, rmSync
|
|
2
|
+
import { existsSync, statSync, rmSync } 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";
|
|
@@ -622,88 +622,29 @@ async function onboardFlow() {
|
|
|
622
622
|
{ value: "skip", label: "Skip for now", hint: "I'll set up models myself" },
|
|
623
623
|
], "lmstudio");
|
|
624
624
|
|
|
625
|
-
const
|
|
626
|
-
const lmsBin = join(homedir(), ".lmstudio", "bin");
|
|
627
|
-
|
|
628
|
-
const ensureLmsOnPath = () => {
|
|
629
|
-
const lmsDest = join(lmsBin, "lms");
|
|
630
|
-
// Bootstrap lms from app bundle if not yet in ~/.lmstudio/bin
|
|
631
|
-
if (!existsSync(lmsDest) && existsSync(lmsAppBundle)) {
|
|
632
|
-
mkdirSync(lmsBin, { recursive: true });
|
|
633
|
-
copyFileSync(lmsAppBundle, lmsDest);
|
|
634
|
-
}
|
|
635
|
-
if (!existsSync(lmsDest)) {
|
|
636
|
-
console.log(pc.yellow(" Note: lms CLI will be available after opening LM Studio once."));
|
|
637
|
-
return false;
|
|
638
|
-
}
|
|
639
|
-
if (process.env.PATH.split(":").includes(lmsBin)) return true;
|
|
640
|
-
process.env.PATH = `${lmsBin}:${process.env.PATH}`;
|
|
641
|
-
const profileFiles = [join(homedir(), ".zshrc"), join(homedir(), ".bash_profile")];
|
|
642
|
-
const line = `export PATH="$PATH:$HOME/.lmstudio/bin"`;
|
|
643
|
-
for (const f of profileFiles) {
|
|
644
|
-
if (!existsSync(f)) continue;
|
|
645
|
-
const content = readFileSync(f, "utf8");
|
|
646
|
-
if (content.includes(".lmstudio/bin")) continue;
|
|
647
|
-
appendFileSync(f, `\n${line}\n`);
|
|
648
|
-
}
|
|
649
|
-
return true;
|
|
650
|
-
};
|
|
625
|
+
const model = recommendedModel();
|
|
651
626
|
|
|
652
627
|
if (backendChoice === "lmstudio") {
|
|
653
|
-
const model = recommendedModel();
|
|
654
628
|
console.log(pc.cyan("Installing LM Studio via Homebrew..."));
|
|
655
629
|
try {
|
|
656
630
|
await run("brew", ["install", "--cask", "lm-studio"], "LM Studio");
|
|
657
|
-
const lmsReady = ensureLmsOnPath();
|
|
658
631
|
console.log(pc.green("✓ LM Studio installed"));
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
console.log(pc.cyan(`Downloading ${model.lms} via lms...`));
|
|
663
|
-
try {
|
|
664
|
-
await run("lms", ["get", model.lms, "-y"], `lms get ${model.lms}`);
|
|
665
|
-
console.log(pc.green("✓ Model downloaded."));
|
|
666
|
-
} catch {
|
|
667
|
-
console.log(pc.yellow(`Model download failed. Run manually: lms get ${model.lms}`));
|
|
668
|
-
}
|
|
669
|
-
} else {
|
|
670
|
-
console.log(pc.yellow("Download a model later:"));
|
|
671
|
-
console.log(pc.bold(` lms get ${model.lms}`));
|
|
672
|
-
}
|
|
673
|
-
} else {
|
|
674
|
-
console.log(pc.yellow("\nOpen LM Studio once to finish setup, then download a model:"));
|
|
675
|
-
console.log(pc.bold(` lms get ${model.lms}`));
|
|
676
|
-
}
|
|
632
|
+
console.log(pc.yellow("\nOpen LM Studio, set up the app, and download a model."));
|
|
633
|
+
console.log(pc.dim(`Recommended for your machine: ${model.label}`));
|
|
634
|
+
console.log(pc.bold(` lms get ${model.lms}`));
|
|
677
635
|
console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
|
|
678
636
|
} catch {
|
|
679
637
|
console.log(pc.red("✗ LM Studio installation failed."));
|
|
680
638
|
console.log(pc.dim("Download it manually from https://lmstudio.ai"));
|
|
681
639
|
}
|
|
682
640
|
} else if (backendChoice === "ollama") {
|
|
683
|
-
const model = recommendedModel();
|
|
684
641
|
console.log(pc.cyan("Installing Ollama via Homebrew..."));
|
|
685
642
|
try {
|
|
686
643
|
await run("brew", ["install", "ollama"], "Ollama");
|
|
687
644
|
console.log(pc.green("✓ Ollama installed"));
|
|
688
|
-
console.log(pc.
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
692
|
-
} catch { /* may already be running */ }
|
|
693
|
-
console.log(pc.green("Ollama is running."));
|
|
694
|
-
const download = await prompt.yesNo(`Pull a model now? (${model.label})`, true);
|
|
695
|
-
if (download) {
|
|
696
|
-
console.log(pc.cyan(`Pulling ${model.ollama} via Ollama...`));
|
|
697
|
-
try {
|
|
698
|
-
await run("ollama", ["pull", model.ollama], `ollama pull ${model.ollama}`);
|
|
699
|
-
console.log(pc.green("✓ Model pulled."));
|
|
700
|
-
} catch {
|
|
701
|
-
console.log(pc.yellow(`Model pull failed. Run manually: ollama pull ${model.ollama}`));
|
|
702
|
-
}
|
|
703
|
-
} else {
|
|
704
|
-
console.log(pc.yellow("Pull a model later:"));
|
|
705
|
-
console.log(pc.bold(` ollama pull ${model.ollama}`));
|
|
706
|
-
}
|
|
645
|
+
console.log(pc.yellow("\nStart Ollama and pull a model:"));
|
|
646
|
+
console.log(pc.bold(` ollama serve \u0026 ollama pull ${model.ollama}`));
|
|
647
|
+
console.log(pc.dim(`Recommended for your machine: ${model.label}`));
|
|
707
648
|
console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
|
|
708
649
|
} catch {
|
|
709
650
|
console.log(pc.red("✗ Ollama installation failed."));
|
|
@@ -715,33 +656,29 @@ async function onboardFlow() {
|
|
|
715
656
|
await run("brew", ["tap", "jundot/omlx", "https://github.com/jundot/omlx"], "oMLX tap");
|
|
716
657
|
await run("brew", ["install", "omlx"], "oMLX");
|
|
717
658
|
console.log(pc.green("✓ oMLX installed"));
|
|
718
|
-
console.log(pc.yellow("\nStart oMLX
|
|
659
|
+
console.log(pc.yellow("\nStart oMLX and download a model:"));
|
|
719
660
|
console.log(pc.bold(" omlx start"));
|
|
661
|
+
console.log(pc.dim(`Recommended for your machine: ${model.label}`));
|
|
720
662
|
console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
|
|
721
|
-
} catch
|
|
722
|
-
console.log(pc.red(
|
|
663
|
+
} catch {
|
|
664
|
+
console.log(pc.red("✗ oMLX installation failed."));
|
|
723
665
|
console.log(pc.dim("Install manually: brew tap jundot/omlx && brew install omlx"));
|
|
724
666
|
}
|
|
725
667
|
} else if (backendChoice === "all") {
|
|
726
|
-
const model = recommendedModel();
|
|
727
668
|
let installed = [];
|
|
728
669
|
// LM Studio
|
|
729
670
|
console.log(pc.cyan("Installing LM Studio via Homebrew..."));
|
|
730
|
-
let lmsReady = false;
|
|
731
671
|
try {
|
|
732
672
|
await run("brew", ["install", "--cask", "lm-studio"], "LM Studio");
|
|
733
|
-
lmsReady = ensureLmsOnPath();
|
|
734
673
|
installed.push("LM Studio");
|
|
735
674
|
} catch {
|
|
736
675
|
console.log(pc.yellow("✗ LM Studio installation failed. Download from https://lmstudio.ai"));
|
|
737
676
|
}
|
|
738
677
|
// Ollama
|
|
739
678
|
console.log(pc.cyan("Installing Ollama via Homebrew..."));
|
|
740
|
-
let ollamaInstalled = false;
|
|
741
679
|
try {
|
|
742
680
|
await run("brew", ["install", "ollama"], "Ollama");
|
|
743
681
|
installed.push("Ollama");
|
|
744
|
-
ollamaInstalled = true;
|
|
745
682
|
} catch {
|
|
746
683
|
console.log(pc.yellow("✗ Ollama installation failed. Install manually from https://ollama.com"));
|
|
747
684
|
}
|
|
@@ -754,45 +691,18 @@ async function onboardFlow() {
|
|
|
754
691
|
} catch {
|
|
755
692
|
console.log(pc.yellow("✗ oMLX installation failed. Install manually: brew tap jundot/omlx && brew install omlx"));
|
|
756
693
|
}
|
|
757
|
-
// Auto-download model with best available backend
|
|
758
694
|
if (installed.length > 0) {
|
|
759
695
|
console.log(pc.green(`\n✓ Installed: ${installed.join(", ")}`));
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
}
|
|
771
|
-
} else if (ollamaInstalled) {
|
|
772
|
-
// Start Ollama and offer to pull
|
|
773
|
-
console.log(pc.cyan("Starting Ollama..."));
|
|
774
|
-
try { await run("ollama", ["serve"], "Ollama serve"); await new Promise(r => setTimeout(r, 2000)); } catch { /* may already be running */ }
|
|
775
|
-
const download = await prompt.yesNo(`Pull a model now? (${model.label})`, true);
|
|
776
|
-
if (download) {
|
|
777
|
-
console.log(pc.cyan(`Pulling ${model.ollama} via Ollama...`));
|
|
778
|
-
try {
|
|
779
|
-
await run("ollama", ["pull", model.ollama], `ollama pull ${model.ollama}`);
|
|
780
|
-
console.log(pc.green("✓ Model pulled."));
|
|
781
|
-
} catch {
|
|
782
|
-
console.log(pc.yellow(`Model pull failed. Run manually: ollama pull ${model.ollama}`));
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
} else {
|
|
786
|
-
console.log(pc.yellow(`Recommended model for your machine (${model.label}):`));
|
|
787
|
-
if (installed.some(i => i.includes("LM Studio"))) {
|
|
788
|
-
console.log(pc.bold(lmsReady ? ` lms get ${model.lms}` : ` Open LM Studio once, then: lms get ${model.lms}`));
|
|
789
|
-
}
|
|
790
|
-
if (installed.includes("Ollama")) {
|
|
791
|
-
console.log(pc.bold(` ollama pull ${model.ollama}`));
|
|
792
|
-
}
|
|
793
|
-
if (installed.includes("oMLX")) {
|
|
794
|
-
console.log(pc.bold(" omlx start"));
|
|
795
|
-
}
|
|
696
|
+
console.log(pc.yellow("\nDownload a model to get started:"));
|
|
697
|
+
console.log(pc.dim(`Recommended for your machine (${(totalmem() / (1024 ** 3)).toFixed(0)}GB RAM): ${model.label}`));
|
|
698
|
+
if (installed.includes("LM Studio")) {
|
|
699
|
+
console.log(pc.bold(` LM Studio → lms get ${model.lms}`));
|
|
700
|
+
}
|
|
701
|
+
if (installed.includes("Ollama")) {
|
|
702
|
+
console.log(pc.bold(` Ollama → ollama pull ${model.ollama}`));
|
|
703
|
+
}
|
|
704
|
+
if (installed.includes("oMLX")) {
|
|
705
|
+
console.log(pc.bold(" oMLX → omlx start"));
|
|
796
706
|
}
|
|
797
707
|
console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
|
|
798
708
|
}
|