offgrid-ai 0.17.0 → 0.18.1

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/src/runtime.mjs CHANGED
@@ -3,7 +3,7 @@ import { execFile } from "node:child_process";
3
3
  import { existsSync } from "node:fs";
4
4
  import { chmod, mkdir, mkdtemp, readFile, rm, symlink, unlink, writeFile } from "node:fs/promises";
5
5
  import { tmpdir } from "node:os";
6
- import { basename, join } from "node:path";
6
+ import { join } from "node:path";
7
7
  import { promisify } from "node:util";
8
8
  import { MANAGED_LLAMA_SERVER, RUNTIME_DIR } from "./config.mjs";
9
9
  import { compareVersions } from "./updates.mjs";
@@ -137,5 +137,5 @@ function verifyDigest(bytes, digest) {
137
137
  if (!digest?.startsWith("sha256:")) return;
138
138
  const expected = digest.slice("sha256:".length);
139
139
  const actual = createHash("sha256").update(bytes).digest("hex");
140
- if (actual !== expected) throw new Error(`${basename("llama.cpp")}: checksum mismatch`);
140
+ if (actual !== expected) throw new Error("llama.cpp: checksum mismatch");
141
141
  }
package/src/ui.mjs CHANGED
@@ -237,6 +237,8 @@ export function createPrompt() {
237
237
 
238
238
  export async function modelSelect(label, groups, { defaultKey, pageSize = 20 } = {}) {
239
239
  const choices = [];
240
+ // Blank line after the prompt message for visual separation
241
+ choices.push(new Separator(" "));
240
242
  for (let i = 0; i < groups.length; i++) {
241
243
  const group = groups[i];
242
244
  // Add blank line before each group (except the first)
@@ -1,79 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Download a HuggingFace model into the standard HF cache.
4
-
5
- Usage:
6
- python3 hf-download.py --repo mlx-community/gemma-4-e2b-it-4bit
7
- python3 hf-download.py --repo unsloth/gemma-4-E2B-it-GGUF --file gemma-4-E2B-it-Q4_K_S.gguf
8
-
9
- Streams NDJSON progress events to stdout.
10
- """
11
- import argparse
12
- import json
13
- import os
14
- import sys
15
-
16
-
17
- def emit(event):
18
- print(json.dumps(event), flush=True)
19
-
20
-
21
- def progress_callback(relative_path, downloaded, total):
22
- emit({
23
- "type": "progress",
24
- "file": relative_path,
25
- "downloadedBytes": downloaded,
26
- "totalBytes": total,
27
- })
28
-
29
-
30
- def main():
31
- parser = argparse.ArgumentParser(description="Download a HuggingFace model into the standard cache.")
32
- parser.add_argument("--repo", required=True, help="HuggingFace repo ID (e.g. mlx-community/gemma-4-e2b-it-4bit)")
33
- parser.add_argument("--file", help="Specific filename to download (for GGUF). Omit to download the full repo (MLX).")
34
- parser.add_argument("--cache-dir", help="HF hub cache directory (where models--org--name/... live). Defaults to $HF_HUB_CACHE or $HF_HOME/hub or ~/.cache/huggingface/hub.")
35
- args = parser.parse_args()
36
-
37
- try:
38
- from huggingface_hub import hf_hub_download, snapshot_download
39
- except ImportError as e:
40
- emit({"type": "error", "message": f"huggingface_hub is not installed: {e}"})
41
- sys.exit(1)
42
-
43
- cache_dir = args.cache_dir or os.environ.get("HF_HUB_CACHE") or os.path.join(
44
- os.environ.get("HF_HOME") or os.path.join(os.path.expanduser("~"), ".cache", "huggingface"),
45
- "hub",
46
- )
47
-
48
- try:
49
- if args.file:
50
- local_path = hf_hub_download(
51
- repo_id=args.repo,
52
- filename=args.file,
53
- cache_dir=cache_dir,
54
- resume_download=True,
55
- )
56
- emit({
57
- "type": "complete",
58
- "localDir": os.path.dirname(local_path),
59
- "localPath": local_path,
60
- "format": "gguf",
61
- })
62
- else:
63
- local_dir = snapshot_download(
64
- repo_id=args.repo,
65
- cache_dir=cache_dir,
66
- resume_download=True,
67
- )
68
- emit({
69
- "type": "complete",
70
- "localDir": local_dir,
71
- "format": "mlx",
72
- })
73
- except Exception as e:
74
- emit({"type": "error", "message": str(e)})
75
- sys.exit(1)
76
-
77
-
78
- if __name__ == "__main__":
79
- main()
@@ -1,42 +0,0 @@
1
- import { pc } from "./ui.mjs";
2
-
3
- export const BACKEND_INSTALLERS = {
4
- lmstudio: {
5
- label: "LM Studio",
6
- choiceLabel: "LM Studio (recommended)",
7
- hint: "brew install --cask lm-studio — visual model browser + CLI",
8
- commands: [["brew", ["install", "--cask", "lm-studio"], "LM Studio"]],
9
- success(model) {
10
- console.log(pc.green("✓ LM Studio installed"));
11
- console.log(pc.yellow("\nOpen LM Studio and download a model to get started."));
12
- console.log(pc.dim(`Recommended for your machine: ${model.label}`));
13
- console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
14
- },
15
- failure: "Download it manually from https://lmstudio.ai",
16
- allFailure: "✗ LM Studio installation failed. Download from https://lmstudio.ai",
17
- },
18
- omlx: {
19
- label: "oMLX",
20
- choiceLabel: "oMLX",
21
- hint: "brew tap jundot/omlx && brew install omlx — Apple Silicon optimized",
22
- commands: [
23
- ["brew", ["tap", "jundot/omlx", "https://github.com/jundot/omlx"], "oMLX tap"],
24
- ["brew", ["install", "omlx"], "oMLX"],
25
- ],
26
- success(model) {
27
- console.log(pc.green("✓ oMLX installed"));
28
- console.log(pc.yellow("\nStart oMLX and download a model:"));
29
- console.log(pc.bold(" omlx start"));
30
- console.log(pc.dim(`Recommended for your machine: ${model.label}`));
31
- console.log(pc.dim("Then run offgrid-ai again to pick and run a model."));
32
- },
33
- failure: "Install manually: brew tap jundot/omlx && brew install omlx",
34
- allFailure: "✗ oMLX installation failed. Install manually: brew tap jundot/omlx && brew install omlx",
35
- },
36
- };
37
-
38
- export const BACKEND_INSTALL_CHOICES = [
39
- ...Object.entries(BACKEND_INSTALLERS).map(([value, installer]) => ({ value, label: installer.choiceLabel, hint: installer.hint })),
40
- { value: "all", label: "Install both", hint: "LM Studio + oMLX" },
41
- { value: "skip", label: "Skip for now", hint: "I'll set up models myself" },
42
- ];