offgrid-ai 0.2.8 → 0.3.0
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 +76 -18
- package/src/profiles.mjs +1 -0
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -60,14 +60,40 @@ export async function mainFlow() {
|
|
|
60
60
|
// Fall through — they can still use managed backends
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
// 4. No models found at all (but backends exist)
|
|
63
|
+
// 4. No models found at all (but backends may exist)
|
|
64
64
|
if (!hasAnyModels && profiles.length === 0) {
|
|
65
65
|
if (!process.stdin.isTTY) {
|
|
66
|
-
throw new Error("No models found. Download
|
|
66
|
+
throw new Error("No models found. Download a model, then run offgrid-ai.");
|
|
67
67
|
}
|
|
68
68
|
console.log(pc.yellow("No models found."));
|
|
69
|
-
console.log(pc.dim("
|
|
70
|
-
|
|
69
|
+
console.log(pc.dim("You need to download a model to use offgrid-ai.\n"));
|
|
70
|
+
// Detect which backends are installed
|
|
71
|
+
const ollamaInstalled = await hasOllamaInstalled();
|
|
72
|
+
const omlxInstalled = await hasOmlxInstalled();
|
|
73
|
+
const lmStudioInstalled = existsSync("/Applications/LM Studio.app");
|
|
74
|
+
const hasBackends = llamaBinary || ollamaInstalled || omlxInstalled || lmStudioInstalled;
|
|
75
|
+
if (hasBackends) {
|
|
76
|
+
console.log(pc.bold("Backend status:"));
|
|
77
|
+
console.log(` ${lmStudioInstalled ? pc.green("✓") : pc.red("✗")} LM Studio ${lmStudioInstalled ? "— installed" : "— not installed"}`);
|
|
78
|
+
console.log(` ${ollamaInstalled ? pc.green("✓") : pc.red("✗")} Ollama ${ollamaInstalled ? "— installed" : "— not installed"}`);
|
|
79
|
+
console.log(` ${omlxInstalled ? pc.green("✓") : pc.red("✗")} oMLX ${omlxInstalled ? "— installed" : "— not installed"}`);
|
|
80
|
+
console.log(` ${llamaBinary ? pc.green("✓") : pc.red("✗")} llama-server ${llamaBinary ? "— installed" : "— not installed"}`);
|
|
81
|
+
console.log();
|
|
82
|
+
const model = recommendedModel();
|
|
83
|
+
console.log(pc.bold("Next step — download a model:"));
|
|
84
|
+
if (lmStudioInstalled) {
|
|
85
|
+
console.log(" Open LM Studio → browse models → download");
|
|
86
|
+
console.log(pc.dim(` Recommended: ${model.label}`));
|
|
87
|
+
}
|
|
88
|
+
if (ollamaInstalled) {
|
|
89
|
+
console.log(pc.bold(` ollama pull ${model.ollama}`));
|
|
90
|
+
}
|
|
91
|
+
if (omlxInstalled) {
|
|
92
|
+
console.log(pc.bold(" omlx start"));
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
console.log(pc.dim("Run offgrid-ai to install a backend and download a model."));
|
|
96
|
+
}
|
|
71
97
|
return;
|
|
72
98
|
}
|
|
73
99
|
|
|
@@ -593,7 +619,31 @@ async function onboardFlow() {
|
|
|
593
619
|
}
|
|
594
620
|
console.log(pc.green(`✓ llama-server: ${llamaBinary}`));
|
|
595
621
|
|
|
596
|
-
// 3.
|
|
622
|
+
// 3. Pi coding agent
|
|
623
|
+
const piInstalled = await hasPi();
|
|
624
|
+
if (!piInstalled) {
|
|
625
|
+
const install = await prompt.yesNo("Pi coding agent is required to chat with models. Install via npm?", true);
|
|
626
|
+
if (!install) {
|
|
627
|
+
console.log(pc.red("offgrid-ai needs Pi to run models."));
|
|
628
|
+
console.log(pc.dim("Install it manually: npm install -g --ignore-scripts @earendil-works/pi-coding-agent"));
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
console.log(pc.cyan("Installing Pi..."));
|
|
632
|
+
try {
|
|
633
|
+
await run("npm", ["install", "-g", "--ignore-scripts", "@earendil-works/pi-coding-agent"], "Pi");
|
|
634
|
+
} catch {
|
|
635
|
+
console.log(pc.red("✗ Failed to install Pi."));
|
|
636
|
+
console.log(pc.dim("Install it manually: npm install -g --ignore-scripts @earendil-works/pi-coding-agent"));
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
if (!(await hasPi())) {
|
|
640
|
+
console.log(pc.yellow("Pi was installed but not found on PATH. Restart your terminal and run offgrid-ai again."));
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
console.log(pc.green("✓ Pi found"));
|
|
645
|
+
|
|
646
|
+
// 4. Model backends — at least one is mandatory
|
|
597
647
|
const ggufModels = await scanGgufModels();
|
|
598
648
|
const managedModels = await scanManagedModels();
|
|
599
649
|
const totalManaged = managedModels.reduce((sum, m) => sum + m.models.length, 0);
|
|
@@ -629,9 +679,8 @@ async function onboardFlow() {
|
|
|
629
679
|
try {
|
|
630
680
|
await run("brew", ["install", "--cask", "lm-studio"], "LM Studio");
|
|
631
681
|
console.log(pc.green("✓ LM Studio installed"));
|
|
632
|
-
console.log(pc.yellow("\nOpen LM Studio
|
|
682
|
+
console.log(pc.yellow("\nOpen LM Studio and download a model to get started."));
|
|
633
683
|
console.log(pc.dim(`Recommended for your machine: ${model.label}`));
|
|
634
|
-
console.log(pc.bold(` lms get ${model.lms}`));
|
|
635
684
|
console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
|
|
636
685
|
} catch {
|
|
637
686
|
console.log(pc.red("✗ LM Studio installation failed."));
|
|
@@ -693,18 +742,7 @@ async function onboardFlow() {
|
|
|
693
742
|
}
|
|
694
743
|
if (installed.length > 0) {
|
|
695
744
|
console.log(pc.green(`\n✓ Installed: ${installed.join(", ")}`));
|
|
696
|
-
console.log(pc.yellow("\nDownload a model to get started:"));
|
|
697
745
|
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"));
|
|
706
|
-
}
|
|
707
|
-
console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
|
|
708
746
|
}
|
|
709
747
|
} else {
|
|
710
748
|
console.log(pc.dim("Run offgrid-ai again when you've set up a model backend."));
|
|
@@ -814,6 +852,26 @@ async function removeSelf() {
|
|
|
814
852
|
}
|
|
815
853
|
}
|
|
816
854
|
|
|
855
|
+
// ── Backend install detection (for status display) ────────────────────────
|
|
856
|
+
|
|
857
|
+
async function hasOllamaInstalled() {
|
|
858
|
+
try {
|
|
859
|
+
const { promisify } = await import("node:util");
|
|
860
|
+
const { execFile } = await import("node:child_process");
|
|
861
|
+
await promisify(execFile)("which", ["ollama"]);
|
|
862
|
+
return true;
|
|
863
|
+
} catch { return false; }
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
async function hasOmlxInstalled() {
|
|
867
|
+
try {
|
|
868
|
+
const { promisify } = await import("node:util");
|
|
869
|
+
const { execFile } = await import("node:child_process");
|
|
870
|
+
await promisify(execFile)("which", ["omlx"]);
|
|
871
|
+
return true;
|
|
872
|
+
} catch { return false; }
|
|
873
|
+
}
|
|
874
|
+
|
|
817
875
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
818
876
|
|
|
819
877
|
async function scanManagedModels() {
|
package/src/profiles.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { mkdir, readdir, rm, unlink, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { PROFILE_DIR, RUN_DIR, LOG_DIR } from "./config.mjs";
|
|
5
5
|
import { backendFor } from "./backends.mjs";
|
|
6
|
+
import { computeFlags } from "./autodetect.mjs";
|
|
6
7
|
import { readJson, writeJson } from "./json.mjs";
|
|
7
8
|
|
|
8
9
|
// ── Path helpers ───────────────────────────────────────────────────────────
|