offgrid-ai 0.4.1 → 0.4.2
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 +27 -25
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { startServer, stopProfile, waitForReady, serverReady, serverMatchesProfi
|
|
|
9
9
|
import { syncPiConfig, removeFromPiConfig, hasPiModel, launchPi, hasPi } from "./harness-pi.mjs";
|
|
10
10
|
import { tailFriendly } from "./logs.mjs";
|
|
11
11
|
import { estimateMemory } from "./estimate.mjs";
|
|
12
|
-
import { pc, formatBytes, renderRows, renderSection, renderCard, humanCapabilitySummary,
|
|
12
|
+
import { pc, formatBytes, renderRows, renderSection, renderCard, humanCapabilitySummary, startInteractive, createPrompt, parseOptions } from "./ui.mjs";
|
|
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";
|
|
@@ -243,13 +243,14 @@ async function printModelCatalog({ profiles, newModels, managedItems }, items =
|
|
|
243
243
|
|
|
244
244
|
const fileMissingCount = profiles.filter((p) => isProfileFileMissing(p)).length;
|
|
245
245
|
|
|
246
|
+
const summaryBorder = fileMissingCount > 0 ? pc.red : newModels.length > 0 ? pc.yellow : pc.dim;
|
|
246
247
|
console.log("\n" + renderCard("Your local AI workspace", renderRows([
|
|
247
|
-
["
|
|
248
|
-
["Need setup", newModels.length > 0 ? pc.yellow(`${newModels.length}
|
|
249
|
-
["Running
|
|
250
|
-
["File missing", fileMissingCount > 0 ? pc.red(`${fileMissingCount} setup${fileMissingCount === 1 ? "" : "s"}`) : pc.
|
|
251
|
-
["Next step", fileMissingCount > 0 ? pc.red("Remove or fix missing setups") : profiles.length > 0 ? "Start chatting" : newModels.length > 0 ? "Set up a downloaded model" : "Download a model"],
|
|
252
|
-
]), { formatBorder:
|
|
248
|
+
["Setups", `${profiles.length} saved`],
|
|
249
|
+
["Need setup", newModels.length > 0 ? pc.yellow(`${newModels.length} model${newModels.length === 1 ? "" : "s"}`) : pc.dim("none")],
|
|
250
|
+
["Running", runningProfilesNow.length > 0 ? pc.green(String(runningProfilesNow.length)) : pc.dim("none")],
|
|
251
|
+
["File missing", fileMissingCount > 0 ? pc.red(`${fileMissingCount} setup${fileMissingCount === 1 ? "" : "s"}`) : pc.dim("none")],
|
|
252
|
+
["Next step", fileMissingCount > 0 ? pc.red("Remove or fix missing setups") : profiles.length > 0 ? "Start chatting" : newModels.length > 0 ? pc.yellow("Set up a downloaded model") : "Download a model"],
|
|
253
|
+
]), { formatBorder: summaryBorder }));
|
|
253
254
|
|
|
254
255
|
console.log("\n" + pc.bold("Ready to chat"));
|
|
255
256
|
if (profiles.length === 0) {
|
|
@@ -264,9 +265,9 @@ async function printModelCatalog({ profiles, newModels, managedItems }, items =
|
|
|
264
265
|
}
|
|
265
266
|
}
|
|
266
267
|
|
|
267
|
-
console.log("\n" + pc.bold("
|
|
268
|
+
console.log("\n" + pc.bold("Needs one-time setup"));
|
|
268
269
|
if (newModels.length === 0) {
|
|
269
|
-
console.log(renderCard("All set", "Every downloaded local model already has a saved setup.", { formatBorder: pc.
|
|
270
|
+
console.log(renderCard("All set", "Every downloaded local model already has a saved setup.", { formatBorder: pc.dim }));
|
|
270
271
|
} else {
|
|
271
272
|
for (const model of newModels.slice(0, 20)) {
|
|
272
273
|
const caps = detectCapabilities(model.path, model.mmprojPath);
|
|
@@ -303,21 +304,22 @@ function profileCatalogCard(num, profile, { running, piConfigured, fileMissing }
|
|
|
303
304
|
if (fileMissing) {
|
|
304
305
|
status = pc.red("File missing");
|
|
305
306
|
} else if (running) {
|
|
306
|
-
status =
|
|
307
|
+
status = pc.green("Running now");
|
|
307
308
|
} else {
|
|
308
|
-
status =
|
|
309
|
+
status = "Ready";
|
|
309
310
|
}
|
|
311
|
+
const border = fileMissing ? pc.red : running ? pc.green : pc.dim;
|
|
310
312
|
return renderCard(`${num}. ${profile.label}`, renderRows([
|
|
311
313
|
["Status", status],
|
|
312
314
|
["Good for", fileMissing ? pc.red("Model file not found") : humanCapabilitySummary(caps)],
|
|
313
|
-
["Pi", piConfigured ? pc.
|
|
315
|
+
["Pi", piConfigured ? pc.dim("synced") : pc.yellow("Needs sync")],
|
|
314
316
|
["Runs with", backend.label],
|
|
315
|
-
]), { formatBorder:
|
|
317
|
+
]), { formatBorder: border });
|
|
316
318
|
}
|
|
317
319
|
|
|
318
320
|
function downloadedModelCard(num, model, caps) {
|
|
319
321
|
return renderCard(`${num}. ${model.label}`, renderRows([
|
|
320
|
-
["Status",
|
|
322
|
+
["Status", pc.yellow("Needs setup")],
|
|
321
323
|
["Good for", humanCapabilitySummary(caps)],
|
|
322
324
|
["Size", formatBytes(model.sizeBytes)],
|
|
323
325
|
]), { formatBorder: pc.yellow });
|
|
@@ -325,11 +327,11 @@ function downloadedModelCard(num, model, caps) {
|
|
|
325
327
|
|
|
326
328
|
function managedModelCard(num, model, backend) {
|
|
327
329
|
return renderCard(`${num}. ${model.label}`, renderRows([
|
|
328
|
-
["Status",
|
|
330
|
+
["Status", pc.dim(`Via ${backend.label}`)],
|
|
329
331
|
["Runs with", backend.label],
|
|
330
|
-
["Model ID",
|
|
332
|
+
["Model ID", model.id],
|
|
331
333
|
...(model.quant ? [["Size/type", model.quant]] : []),
|
|
332
|
-
]), { formatBorder: pc.
|
|
334
|
+
]), { formatBorder: pc.dim });
|
|
333
335
|
}
|
|
334
336
|
|
|
335
337
|
function modelCatalogItems({ profiles, newModels, managedItems }) {
|
|
@@ -410,19 +412,19 @@ async function printProfileDetails(profile) {
|
|
|
410
412
|
const fileMissing = !isManaged && isProfileFileMissing(profile);
|
|
411
413
|
const statusRow = fileMissing
|
|
412
414
|
? pc.red("File missing")
|
|
413
|
-
: running ? pc.green("Running now") :
|
|
415
|
+
: running ? pc.green("Running now") : "Ready";
|
|
414
416
|
console.log("\n" + renderSection("Model overview", renderRows([
|
|
415
417
|
["Name", pc.bold(profile.label)],
|
|
416
418
|
["Status", statusRow],
|
|
417
419
|
["Good for", fileMissing ? pc.red("Model file not found — remove or fix this setup") : humanCapabilitySummary(profile.capabilities ?? {})],
|
|
418
|
-
["Pi", piConfigured ? pc.
|
|
419
|
-
["Server", fileMissing ? pc.red(profile.baseUrl) :
|
|
420
|
+
["Pi", piConfigured ? pc.dim("synced") : pc.yellow("Needs sync")],
|
|
421
|
+
["Server", fileMissing ? pc.red(profile.baseUrl) : profile.baseUrl],
|
|
420
422
|
])));
|
|
421
423
|
|
|
422
424
|
console.log("\n" + renderSection("Model details", renderRows([
|
|
423
|
-
["Setup ID",
|
|
425
|
+
["Setup ID", profile.id],
|
|
424
426
|
["Runs with", backend.label],
|
|
425
|
-
["Model alias",
|
|
427
|
+
["Model alias", profile.modelAlias],
|
|
426
428
|
...(profile.capabilities ? [["Detected", capabilitySummary(profile.capabilities)]] : []),
|
|
427
429
|
...(!isManaged ? [
|
|
428
430
|
["Local file", fileMissing ? pc.red(`${profile.modelPath} (not found)`) : profile.modelPath ?? "unknown"],
|
|
@@ -659,9 +661,9 @@ async function statusCommand() {
|
|
|
659
661
|
if (running.length === 0) {
|
|
660
662
|
console.log(renderCard("Status", renderRows([
|
|
661
663
|
["Running now", pc.dim("none")],
|
|
662
|
-
["Ready setups", profiles.length > 0 ?
|
|
663
|
-
["Next step", profiles.length > 0 ? "Run offgrid-ai to start chatting" : "Run offgrid-ai to set up a model"],
|
|
664
|
-
]), { formatBorder:
|
|
664
|
+
["Ready setups", profiles.length > 0 ? String(profiles.length) : pc.dim("none")],
|
|
665
|
+
["Next step", profiles.length > 0 ? "Run offgrid-ai to start chatting" : pc.yellow("Run offgrid-ai to set up a model")],
|
|
666
|
+
]), { formatBorder: pc.dim }));
|
|
665
667
|
return;
|
|
666
668
|
}
|
|
667
669
|
|