offgrid-ai 0.3.12 → 0.3.14

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "offgrid-ai",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
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",
package/src/cli.mjs CHANGED
@@ -13,6 +13,7 @@ import { pc, formatBytes, renderRows, renderSection, startInteractive, createPro
13
13
  import { checkForUpdate, currentPackageVersion, detectInvocation, updateCommand, runUpdateCommand } from "./updates.mjs";
14
14
  import { removeInstallerPathEntries } from "./shell-path.mjs";
15
15
  import { configureLocalProfile } from "./profile-setup.mjs";
16
+ import { buildPrettyCommand } from "./command.mjs";
16
17
 
17
18
  // ── Entry point ────────────────────────────────────────────────────────────
18
19
 
@@ -389,14 +390,12 @@ async function manageProfiles(prompt, profiles) {
389
390
  label: p.label,
390
391
  hint: `${p.modelAlias} · ${p.baseUrl}`,
391
392
  }));
392
- choices.push({ value: "__back", label: "← Back" });
393
393
 
394
394
  const selected = await prompt.choice("Which profile?", choices, choices[0].value);
395
- if (selected === "__back") return;
396
-
397
395
  const profile = await readProfile(selected);
398
396
  const backend = backendFor(profile.backend);
399
397
  const isManaged = backend.type === "managed-server";
398
+ const piConfigured = await hasPiModel(profile);
400
399
 
401
400
  // Show profile details
402
401
  console.log("");
@@ -411,32 +410,26 @@ async function manageProfiles(prompt, profiles) {
411
410
  ["Memory", existsSync(profile.modelPath) ? formatBytes(statSync(profile.modelPath).size) : "unknown"],
412
411
  ] : []),
413
412
  ["Alias", pc.cyan(profile.modelAlias)],
414
- ["Pi", (await hasPiModel(profile)) ? pc.green("configured") : pc.yellow("not synced")],
413
+ ["Pi", piConfigured ? pc.green("configured") : pc.yellow("not synced")],
415
414
  ])));
416
415
 
417
416
  if (!isManaged && profile.commandArgv) {
418
417
  console.log("");
419
- console.log(pc.bold("Auto-detected flags"));
420
- console.log(pc.dim(profile.commandArgv.join(" ")));
418
+ console.log(pc.bold("llama-server command"));
419
+ console.log(pc.dim(buildPrettyCommand(profile)));
421
420
  }
422
421
 
423
422
  const action = await prompt.choice("Action", [
424
- { value: "sync", label: "Sync Pi config", hint: "Update ~/.pi/agent/models.json" },
423
+ { value: "sync", label: piConfigured ? `${pc.green("✓")} Pi config synced` : "Sync Pi config", hint: piConfigured ? "Already in ~/.pi/agent/models.json" : "Update ~/.pi/agent/models.json" },
425
424
  { value: "run", label: "Run", hint: "Start server + Pi" },
426
425
  ...(isManaged ? [] : [{ value: "server", label: "Server only", hint: "Start server, no harness" }]),
427
426
  { value: "remove", label: "Remove", hint: "Delete profile + Pi config" },
428
- { value: "__back", label: "← Back" },
429
427
  ], "sync");
430
428
 
431
- if (action === "sync") {
432
- await syncPiConfig(profile);
433
- } else if (action === "run") {
434
- return await runProfile(profile);
435
- } else if (action === "server") {
436
- return await runProfile(profile, { with: "server" });
437
- } else if (action === "remove") {
438
- await removeProfileInteractive(profile.id);
439
- }
429
+ if (action === "sync") return await syncPiConfig(profile);
430
+ if (action === "run") return await runProfile(profile);
431
+ if (action === "server") return await runProfile(profile, { with: "server" });
432
+ if (action === "remove") return await removeProfileInteractive(profile.id);
440
433
  }
441
434
 
442
435
  async function removeProfileInteractive(id) {
@@ -0,0 +1,21 @@
1
+ export function buildPrettyCommand(profile, binary = "llama-server") {
2
+ const argv = profile.commandArgv ?? [];
3
+ const lines = [`${quoteShell(binary)} \\`];
4
+ for (let i = 0; i < argv.length; i++) {
5
+ const arg = argv[i];
6
+ const next = argv[i + 1];
7
+ const hasValue = arg.startsWith("--") && next && !next.startsWith("--");
8
+ if (hasValue) {
9
+ lines.push(` ${arg} ${quoteShell(next)}${i + 2 < argv.length ? " \\" : ""}`);
10
+ i += 1;
11
+ } else {
12
+ lines.push(` ${arg}${i + 1 < argv.length ? " \\" : ""}`);
13
+ }
14
+ }
15
+ return lines.join("\n");
16
+ }
17
+
18
+ export function quoteShell(value) {
19
+ const text = String(value);
20
+ return /^[A-Za-z0-9_/@%+=:,.-]+$/u.test(text) ? text : `'${text.replace(/'/gu, `'"'"'`)}'`;
21
+ }