offgrid-ai 0.3.2 → 0.3.5
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 +11 -4
- package/src/cli.mjs +24 -14
- package/src/estimate.mjs +0 -1
- package/src/process.mjs +1 -1
- package/src/profiles.mjs +5 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "offgrid-ai",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Privacy-first CLI for running local LLMs — discover, configure, run, benchmark",
|
|
5
5
|
"author": "Eeshan Srivastava (https://eeshans.com)",
|
|
6
6
|
"type": "module",
|
|
@@ -29,10 +29,12 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"start": "node bin/offgrid-ai.mjs",
|
|
31
31
|
"test": "node --test test/*.mjs",
|
|
32
|
+
"lint": "eslint src/*.mjs bin/*.mjs",
|
|
32
33
|
"check:privacy": "node scripts/privacy-gate.mjs",
|
|
33
34
|
"release:check": "bash scripts/release-check.sh",
|
|
34
35
|
"release:check:fast": "bash scripts/release-check.sh --skip-install --skip-manual",
|
|
35
|
-
"prepack": "npm run check:privacy"
|
|
36
|
+
"prepack": "npm run check:privacy",
|
|
37
|
+
"pretest": "npm run lint"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
38
40
|
"@clack/prompts": "^1.4.0",
|
|
@@ -47,5 +49,10 @@
|
|
|
47
49
|
"llm",
|
|
48
50
|
"ai"
|
|
49
51
|
],
|
|
50
|
-
"license": "MIT"
|
|
51
|
-
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@eslint/js": "^10.0.1",
|
|
55
|
+
"eslint": "^10.4.1",
|
|
56
|
+
"globals": "^17.6.0"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/cli.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { totalmem } from "node:os";
|
|
2
2
|
import { existsSync, statSync, rmSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
3
|
import { ensureDirs, findLlamaServer, hasHomebrew, DATA_DIR } from "./config.mjs";
|
|
5
4
|
import { scanGgufModels } from "./scan.mjs";
|
|
6
5
|
import { createProfileFromModel, normalizeProfile } from "./profiles.mjs";
|
|
@@ -145,9 +144,8 @@ export async function mainFlow() {
|
|
|
145
144
|
console.log(pc.bold("\nSaved profiles"));
|
|
146
145
|
for (const profile of profiles) {
|
|
147
146
|
const backend = backendFor(profile.backend);
|
|
148
|
-
const running = await isProfileRunning(profile);
|
|
149
|
-
const idx = items.length;
|
|
150
147
|
const colorMap = { "llama-cpp": pc.yellow, "llama-cpp-mtp": pc.blue, "ollama": pc.magenta, "omlx": pc.cyan };
|
|
148
|
+
const running = await isProfileRunning(profile);
|
|
151
149
|
const c = colorMap[profile.backend] ?? pc.magenta;
|
|
152
150
|
console.log(` ${running ? pc.green("●") : pc.dim("○")} ${pc.bold(profile.label)} ${c(`[${backend.label}]`)} · ${pc.cyan(profile.modelAlias)}`);
|
|
153
151
|
}
|
|
@@ -294,13 +292,22 @@ async function runProfile(profile, options = {}) {
|
|
|
294
292
|
console.log(pc.dim("Use --reuse-existing to reuse this server."));
|
|
295
293
|
} else if (!ready) {
|
|
296
294
|
console.log(pc.dim(`Starting ${backend.label} for ${profile.label}...`));
|
|
297
|
-
|
|
298
|
-
const tail = state?.rawLogPath ? tailFriendly(state.rawLogPath, state.friendlyLogPath) : { stop() {} };
|
|
295
|
+
let state;
|
|
299
296
|
try {
|
|
300
|
-
await
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
297
|
+
state = await startServer(profile);
|
|
298
|
+
const tail = state?.rawLogPath ? tailFriendly(state.rawLogPath, state.friendlyLogPath) : { stop() {} };
|
|
299
|
+
try {
|
|
300
|
+
await waitForReady(profile, state?.pid, state?.rawLogPath);
|
|
301
|
+
console.log(pc.green(`[ready] ${profile.baseUrl}/models`));
|
|
302
|
+
} finally {
|
|
303
|
+
tail.stop();
|
|
304
|
+
}
|
|
305
|
+
} catch (err) {
|
|
306
|
+
// Clean up orphaned server process if startup failed
|
|
307
|
+
if (state?.pid) {
|
|
308
|
+
try { await stopProfile(profile); } catch { /* best effort */ }
|
|
309
|
+
}
|
|
310
|
+
throw err;
|
|
304
311
|
}
|
|
305
312
|
}
|
|
306
313
|
}
|
|
@@ -420,7 +427,7 @@ async function removeProfileInteractive(id) {
|
|
|
420
427
|
|
|
421
428
|
// ── Benchmark (stub) ────────────────────────────────────────────────────────
|
|
422
429
|
|
|
423
|
-
async function benchmarkFlow(
|
|
430
|
+
async function benchmarkFlow() {
|
|
424
431
|
console.log(pc.yellow("Benchmark support coming soon."));
|
|
425
432
|
console.log(pc.dim("This will require the local-llm-visual-benchmark repo."));
|
|
426
433
|
console.log(pc.dim("For now, start a model with offgrid-ai and run benchmarks manually."));
|
|
@@ -598,7 +605,7 @@ async function onboardFlow() {
|
|
|
598
605
|
break;
|
|
599
606
|
}
|
|
600
607
|
}
|
|
601
|
-
} catch
|
|
608
|
+
} catch {
|
|
602
609
|
console.log(pc.red(`✗ Homebrew installation failed.`));
|
|
603
610
|
console.log(pc.dim("Install it manually from https://brew.sh, then run offgrid-ai again."));
|
|
604
611
|
return;
|
|
@@ -775,8 +782,11 @@ async function onboardFlow() {
|
|
|
775
782
|
// ── Uninstall ───────────────────────────────────────────────────────────────
|
|
776
783
|
|
|
777
784
|
async function uninstallCommand(argv) {
|
|
778
|
-
|
|
779
|
-
|
|
785
|
+
const { options } = parseOptions(argv);
|
|
786
|
+
const force = options.force || options.f;
|
|
787
|
+
|
|
788
|
+
if (!process.stdin.isTTY || force) {
|
|
789
|
+
// Non-interactive / forced: remove everything
|
|
780
790
|
await removeDataDir();
|
|
781
791
|
await removeSelf();
|
|
782
792
|
return;
|
package/src/estimate.mjs
CHANGED
package/src/process.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFile, spawn } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
|
-
import {
|
|
3
|
+
import { openSync } from "node:fs";
|
|
4
4
|
import { readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { LOG_DIR } from "./config.mjs";
|
package/src/profiles.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync
|
|
2
|
-
import { mkdir, readdir, rm, unlink, writeFile } from "node:fs/promises";
|
|
3
|
-
import {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, readdir, rm, unlink, writeFile, readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
4
|
import { PROFILE_DIR, RUN_DIR, LOG_DIR } from "./config.mjs";
|
|
5
5
|
import { backendFor } from "./backends.mjs";
|
|
6
6
|
import { computeFlags } from "./autodetect.mjs";
|
|
@@ -113,7 +113,7 @@ export async function deleteProfile(id, options = {}) {
|
|
|
113
113
|
|
|
114
114
|
// ── Normalize / auto-detect ────────────────────────────────────────────────
|
|
115
115
|
|
|
116
|
-
export function normalizeProfile(profile
|
|
116
|
+
export function normalizeProfile(profile) {
|
|
117
117
|
const backend = backendFor(profile.backend);
|
|
118
118
|
const flags = {
|
|
119
119
|
host: "127.0.0.1",
|
|
@@ -152,7 +152,7 @@ export async function createProfileFromModel(model, backendId = "llama-cpp") {
|
|
|
152
152
|
preset: null, // no presets — auto-detected
|
|
153
153
|
flags,
|
|
154
154
|
commandArgv: argv,
|
|
155
|
-
}
|
|
155
|
+
});
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// ── State files (for running servers) ──────────────────────────────────────
|