diff --git a/extensions/index.ts b/extensions/index.ts index 3afa035..cb97d0a 100644 --- a/extensions/index.ts +++ b/extensions/index.ts @@ -1598,9 +1598,75 @@ const ANTG_PROVIDER_MODELS: AntgProviderModelDef[] = [ { id: "gpt-oss-120b-medium", name: "GPT-OSS 120B (Medium)", reasoning: false, images: false, contextWindow: 262144, maxTokens: 32768 }, ]; -// Modelli attualmente registrati nel provider (statici all'avvio, aggiornabili -// con /agy:refresh-models dal catalogo vivo fetchAvailableModels). -let antgRegisteredModels: AntgProviderModelDef[] = ANTG_PROVIDER_MODELS; +// --------------------------------------------------------------------------- +// Cache persistente dei modelli (~/.config/agy-pi/models-cache.json) +// Il catalogo vivo scaricato da /agy:refresh-models viene salvato qui, così la +// lista sopravvive al riavvio dell'estensione. Se il file è assente, illeggibile +// o corrotto si riparte da ANTG_PROVIDER_MODELS senza errori fatali. +// --------------------------------------------------------------------------- +const MODELS_CACHE_FILE = path.join(CONFIG_DIR, "models-cache.json"); +const MODELS_CACHE_VERSION = 1; + +interface AntgModelsCache { + version: number; + refreshedAt: string; + project?: string; + models: AntgProviderModelDef[]; +} + +function isAntgModelDef(value: any): value is AntgProviderModelDef { + return ( + value !== null && + typeof value === "object" && + typeof value.id === "string" && + value.id.trim() !== "" && + typeof value.contextWindow === "number" && + value.contextWindow > 0 && + typeof value.maxTokens === "number" && + value.maxTokens > 0 + ); +} + +function loadModelsCache(): AntgModelsCache | null { + try { + const raw = JSON.parse(fs.readFileSync(MODELS_CACHE_FILE, "utf8")); + if (raw?.version !== MODELS_CACHE_VERSION) return null; + if (!Array.isArray(raw.models) || raw.models.length === 0) return null; + if (!raw.models.every(isAntgModelDef)) return null; + return raw as AntgModelsCache; + } catch { + return null; // assente, illeggibile o corrotto → default + } +} + +function saveModelsCache(models: AntgProviderModelDef[], projectId?: string) { + try { + fs.mkdirSync(CONFIG_DIR, { recursive: true }); + const tmp = `${MODELS_CACHE_FILE}.tmp`; + fs.writeFileSync( + tmp, + JSON.stringify( + { + version: MODELS_CACHE_VERSION, + refreshedAt: new Date().toISOString(), + project: projectId, + models, + }, + null, + 2, + ), + { mode: 0o600 }, + ); + fs.renameSync(tmp, MODELS_CACHE_FILE); // scrittura atomica + } catch { + /* ignora */ + } +} + +// Modelli attualmente registrati nel provider: cache persistente se presente, +// altrimenti i default statici. Aggiornabili con /agy:refresh-models (che ora +// salva su disco) e ripristinabili con /agy:models-reset. +let antgRegisteredModels: AntgProviderModelDef[] = loadModelsCache()?.models ?? ANTG_PROVIDER_MODELS; // Configurazione del provider "antigravity" (riusata all'avvio e al refresh). function antgProviderConfig(models: AntgProviderModelDef[]) { @@ -2618,10 +2684,11 @@ export default function agyExtension(pi: ExtensionAPI) { if (!models.length) throw new Error("Il catalogo non ha restituito modelli utilizzabili"); antgRegisteredModels = models; pi.registerProvider("antigravity", antgProviderConfig(models)); + saveModelsCache(models, pid); ctx.ui.setStatus("agy:refresh-models", ""); const lines = models.map((m) => ` ${m.id} — ${m.name}${m.reasoning ? " (thinking)" : ""}`).join("\n"); ctx.ui.notify( - `✅ Provider antigravity aggiornato: ${models.length} modelli (project ${pid})\n${lines}`, + `✅ Provider antigravity aggiornato: ${models.length} modelli (project ${pid})\n${lines}\n\n💾 Lista salvata in ${MODELS_CACHE_FILE} (persistente al riavvio).`, "info", ); } catch (e: any) { @@ -2631,6 +2698,31 @@ export default function agyExtension(pi: ExtensionAPI) { }, }); + // ========================================================================= + // Comando: /agy:models-reset — rimuove la cache persistente dei modelli e + // ripristina la lista statica di default. + // ========================================================================= + pi.registerCommand("agy:models-reset", { + description: + "Rimuove la cache persistente dei modelli (~/.config/agy-pi/models-cache.json) e ri-registra il provider con la lista di default.", + handler: async (_args, ctx) => { + let removed = false; + try { + removed = fs.existsSync(MODELS_CACHE_FILE); + fs.rmSync(MODELS_CACHE_FILE, { force: true }); + } catch (e: any) { + ctx.ui.notify(`Errore rimozione cache modelli: ${e.message}`, "error"); + return; + } + antgRegisteredModels = ANTG_PROVIDER_MODELS; + pi.registerProvider("antigravity", antgProviderConfig(ANTG_PROVIDER_MODELS)); + ctx.ui.notify( + `♻️ ${removed ? "Cache rimossa" : "Nessuna cache presente"}: provider antigravity ripristinato a ${ANTG_PROVIDER_MODELS.length} modelli di default.`, + "info", + ); + }, + }); + // ========================================================================= // Comandi interattivi // =========================================================================