- Add catalog metadata lookup for better error messages with hints (e.g., for missing manifests or tags) - Persist downloaded model references in saved remote models for future reuse - Replace searchRemoteModels import with getSavedRemoteModels in search route - Enhance pull progress UI to display download bytes, digest, and fallback message when data is unavailable - Improve error payload with model details, URL, and tags count to guide users toward valid tags or models
63 lines
1.7 KiB
TypeScript
63 lines
1.7 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
|
|
export type SavedRemoteModel = {
|
|
name: string; // full ref, e.g., owner/model:tag or model:tag
|
|
description?: string;
|
|
url?: string;
|
|
tags_count?: number;
|
|
installed?: boolean;
|
|
lastUsedAt?: string; // ISO
|
|
};
|
|
|
|
type SavedRemoteFile = {
|
|
models: SavedRemoteModel[];
|
|
};
|
|
|
|
function getFilePath() {
|
|
return path.join(process.cwd(), "database", "user-remote-models.json");
|
|
}
|
|
|
|
async function ensureFile(): Promise<void> {
|
|
const file = getFilePath();
|
|
try {
|
|
await fs.access(file);
|
|
} catch {
|
|
const initial: SavedRemoteFile = { models: [] };
|
|
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
await fs.writeFile(file, JSON.stringify(initial, null, 2), "utf-8");
|
|
}
|
|
}
|
|
|
|
export async function getSavedRemoteModels(): Promise<SavedRemoteModel[]> {
|
|
await ensureFile();
|
|
const raw = await fs.readFile(getFilePath(), "utf-8");
|
|
try {
|
|
const json = JSON.parse(raw) as SavedRemoteFile;
|
|
return Array.isArray(json.models) ? json.models : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export async function addSavedRemoteModel(entry: SavedRemoteModel): Promise<void> {
|
|
await ensureFile();
|
|
const current = await getSavedRemoteModels();
|
|
const nowIso = new Date().toISOString();
|
|
const idx = current.findIndex((m) => m.name === entry.name);
|
|
const merged: SavedRemoteModel = {
|
|
...entry,
|
|
installed: entry.installed ?? false,
|
|
lastUsedAt: entry.lastUsedAt ?? nowIso,
|
|
};
|
|
if (idx >= 0) {
|
|
current[idx] = { ...current[idx], ...merged, lastUsedAt: nowIso };
|
|
} else {
|
|
current.unshift(merged);
|
|
}
|
|
const toWrite: SavedRemoteFile = { models: current.slice(0, 500) };
|
|
await fs.writeFile(getFilePath(), JSON.stringify(toWrite, null, 2), "utf-8");
|
|
}
|
|
|
|
|