offgrid-ai 0.2.6 → 0.2.7
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 +42 -23
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
1
|
+
import { homedir, totalmem } from "node:os";
|
|
2
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";
|
|
@@ -489,6 +489,22 @@ async function runningProfiles() {
|
|
|
489
489
|
|
|
490
490
|
// ── Onboarding ──────────────────────────────────────────────────────────────
|
|
491
491
|
|
|
492
|
+
// ── Model recommendations by RAM ───────────────────────────────────────
|
|
493
|
+
// Tier → { lms: [...], ollama: string, label }
|
|
494
|
+
// lms entries are tried in order (first staff-pick match wins, or @quant forces it)
|
|
495
|
+
const MODEL_TIERS = [
|
|
496
|
+
{ maxGB: 8, lms: "google/gemma-4-e2b", ollama: "gemma4:e2b", label: "Gemma 4 E2B (2B effective)" },
|
|
497
|
+
{ maxGB: 16, lms: "google/gemma-4-e4b", ollama: "gemma4:e4b", label: "Gemma 4 E4B (4B effective)" },
|
|
498
|
+
{ maxGB: 32, lms: "qwen/qwen3.5-9b", ollama: "qwen3.5:9b-q4_K_M", label: "Qwen 3.5 9B" },
|
|
499
|
+
{ maxGB: Infinity, lms: "qwen/qwen3.6-35b-a3b", ollama: "qwen3.6:35b-a3b", label: "Qwen 3.6 35B-A3B" },
|
|
500
|
+
];
|
|
501
|
+
|
|
502
|
+
function recommendedModel() {
|
|
503
|
+
const gb = totalmem() / (1024 ** 3);
|
|
504
|
+
const tier = MODEL_TIERS.find(t => gb <= t.maxGB) || MODEL_TIERS[MODEL_TIERS.length - 1];
|
|
505
|
+
return tier;
|
|
506
|
+
}
|
|
507
|
+
|
|
492
508
|
async function onboardFlow() {
|
|
493
509
|
startInteractive("offgrid-ai setup");
|
|
494
510
|
const prompt = createPrompt();
|
|
@@ -634,28 +650,29 @@ async function onboardFlow() {
|
|
|
634
650
|
};
|
|
635
651
|
|
|
636
652
|
if (backendChoice === "lmstudio") {
|
|
653
|
+
const model = recommendedModel();
|
|
637
654
|
console.log(pc.cyan("Installing LM Studio via Homebrew..."));
|
|
638
655
|
try {
|
|
639
656
|
await run("brew", ["install", "--cask", "lm-studio"], "LM Studio");
|
|
640
657
|
const lmsReady = ensureLmsOnPath();
|
|
641
658
|
console.log(pc.green("✓ LM Studio installed"));
|
|
642
659
|
if (lmsReady) {
|
|
643
|
-
const download = await prompt.yesNo(
|
|
660
|
+
const download = await prompt.yesNo(`Download a model now? (${model.label})`, true);
|
|
644
661
|
if (download) {
|
|
645
|
-
console.log(pc.cyan(
|
|
662
|
+
console.log(pc.cyan(`Downloading ${model.lms} via lms...`));
|
|
646
663
|
try {
|
|
647
|
-
await run("lms", ["get",
|
|
664
|
+
await run("lms", ["get", model.lms, "-y"], `lms get ${model.lms}`);
|
|
648
665
|
console.log(pc.green("✓ Model downloaded."));
|
|
649
666
|
} catch {
|
|
650
|
-
console.log(pc.yellow(
|
|
667
|
+
console.log(pc.yellow(`Model download failed. Run manually: lms get ${model.lms}`));
|
|
651
668
|
}
|
|
652
669
|
} else {
|
|
653
670
|
console.log(pc.yellow("Download a model later:"));
|
|
654
|
-
console.log(pc.bold(
|
|
671
|
+
console.log(pc.bold(` lms get ${model.lms}`));
|
|
655
672
|
}
|
|
656
673
|
} else {
|
|
657
674
|
console.log(pc.yellow("\nOpen LM Studio once to finish setup, then download a model:"));
|
|
658
|
-
console.log(pc.bold(
|
|
675
|
+
console.log(pc.bold(` lms get ${model.lms}`));
|
|
659
676
|
}
|
|
660
677
|
console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
|
|
661
678
|
} catch {
|
|
@@ -663,6 +680,7 @@ async function onboardFlow() {
|
|
|
663
680
|
console.log(pc.dim("Download it manually from https://lmstudio.ai"));
|
|
664
681
|
}
|
|
665
682
|
} else if (backendChoice === "ollama") {
|
|
683
|
+
const model = recommendedModel();
|
|
666
684
|
console.log(pc.cyan("Installing Ollama via Homebrew..."));
|
|
667
685
|
try {
|
|
668
686
|
await run("brew", ["install", "ollama"], "Ollama");
|
|
@@ -673,18 +691,18 @@ async function onboardFlow() {
|
|
|
673
691
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
674
692
|
} catch { /* may already be running */ }
|
|
675
693
|
console.log(pc.green("Ollama is running."));
|
|
676
|
-
const download = await prompt.yesNo(
|
|
694
|
+
const download = await prompt.yesNo(`Pull a model now? (${model.label})`, true);
|
|
677
695
|
if (download) {
|
|
678
|
-
console.log(pc.cyan(
|
|
696
|
+
console.log(pc.cyan(`Pulling ${model.ollama} via Ollama...`));
|
|
679
697
|
try {
|
|
680
|
-
await run("ollama", ["pull",
|
|
698
|
+
await run("ollama", ["pull", model.ollama], `ollama pull ${model.ollama}`);
|
|
681
699
|
console.log(pc.green("✓ Model pulled."));
|
|
682
700
|
} catch {
|
|
683
|
-
console.log(pc.yellow(
|
|
701
|
+
console.log(pc.yellow(`Model pull failed. Run manually: ollama pull ${model.ollama}`));
|
|
684
702
|
}
|
|
685
703
|
} else {
|
|
686
704
|
console.log(pc.yellow("Pull a model later:"));
|
|
687
|
-
console.log(pc.bold(
|
|
705
|
+
console.log(pc.bold(` ollama pull ${model.ollama}`));
|
|
688
706
|
}
|
|
689
707
|
console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
|
|
690
708
|
} catch {
|
|
@@ -705,6 +723,7 @@ async function onboardFlow() {
|
|
|
705
723
|
console.log(pc.dim("Install manually: brew tap jundot/omlx && brew install omlx"));
|
|
706
724
|
}
|
|
707
725
|
} else if (backendChoice === "all") {
|
|
726
|
+
const model = recommendedModel();
|
|
708
727
|
let installed = [];
|
|
709
728
|
// LM Studio
|
|
710
729
|
console.log(pc.cyan("Installing LM Studio via Homebrew..."));
|
|
@@ -739,37 +758,37 @@ async function onboardFlow() {
|
|
|
739
758
|
if (installed.length > 0) {
|
|
740
759
|
console.log(pc.green(`\n✓ Installed: ${installed.join(", ")}`));
|
|
741
760
|
if (lmsReady) {
|
|
742
|
-
const download = await prompt.yesNo(
|
|
761
|
+
const download = await prompt.yesNo(`Download a model now? (${model.label})`, true);
|
|
743
762
|
if (download) {
|
|
744
|
-
console.log(pc.cyan(
|
|
763
|
+
console.log(pc.cyan(`Downloading ${model.lms} via lms...`));
|
|
745
764
|
try {
|
|
746
|
-
await run("lms", ["get",
|
|
765
|
+
await run("lms", ["get", model.lms, "-y"], `lms get ${model.lms}`);
|
|
747
766
|
console.log(pc.green("✓ Model downloaded."));
|
|
748
767
|
} catch {
|
|
749
|
-
console.log(pc.yellow(
|
|
768
|
+
console.log(pc.yellow(`Model download failed. Run manually: lms get ${model.lms}`));
|
|
750
769
|
}
|
|
751
770
|
}
|
|
752
771
|
} else if (ollamaInstalled) {
|
|
753
772
|
// Start Ollama and offer to pull
|
|
754
773
|
console.log(pc.cyan("Starting Ollama..."));
|
|
755
774
|
try { await run("ollama", ["serve"], "Ollama serve"); await new Promise(r => setTimeout(r, 2000)); } catch { /* may already be running */ }
|
|
756
|
-
const download = await prompt.yesNo(
|
|
775
|
+
const download = await prompt.yesNo(`Pull a model now? (${model.label})`, true);
|
|
757
776
|
if (download) {
|
|
758
|
-
console.log(pc.cyan(
|
|
777
|
+
console.log(pc.cyan(`Pulling ${model.ollama} via Ollama...`));
|
|
759
778
|
try {
|
|
760
|
-
await run("ollama", ["pull",
|
|
779
|
+
await run("ollama", ["pull", model.ollama], `ollama pull ${model.ollama}`);
|
|
761
780
|
console.log(pc.green("✓ Model pulled."));
|
|
762
781
|
} catch {
|
|
763
|
-
console.log(pc.yellow(
|
|
782
|
+
console.log(pc.yellow(`Model pull failed. Run manually: ollama pull ${model.ollama}`));
|
|
764
783
|
}
|
|
765
784
|
}
|
|
766
785
|
} else {
|
|
767
|
-
console.log(pc.yellow(
|
|
786
|
+
console.log(pc.yellow(`Recommended model for your machine (${model.label}):`));
|
|
768
787
|
if (installed.some(i => i.includes("LM Studio"))) {
|
|
769
|
-
console.log(pc.bold(lmsReady ?
|
|
788
|
+
console.log(pc.bold(lmsReady ? ` lms get ${model.lms}` : ` Open LM Studio once, then: lms get ${model.lms}`));
|
|
770
789
|
}
|
|
771
790
|
if (installed.includes("Ollama")) {
|
|
772
|
-
console.log(pc.bold(
|
|
791
|
+
console.log(pc.bold(` ollama pull ${model.ollama}`));
|
|
773
792
|
}
|
|
774
793
|
if (installed.includes("oMLX")) {
|
|
775
794
|
console.log(pc.bold(" omlx start"));
|