pi-mono/packages/coding-agent/src/core/models-store.ts
Mario Zechner fed6009cc9 fix(coding-agent): make model refresh cancellation caller-owned
Add generation-safe catalog publication, cancellable auth and storage operations, locally consistent credential mutations, and cache-first interactive model flows.
2026-08-04 00:03:52 +02:00

62 lines
2.4 KiB
TypeScript

import { join } from "node:path";
import type { ModelsStore, ModelsStoreEntry, ModelsStoreOperationOptions } from "@earendil-works/pi-ai";
import { getAgentDir } from "../config.ts";
import { type AuthStorageBackend, FileAuthStorageBackend } from "./auth-storage.ts";
type StoredModels = Record<string, ModelsStoreEntry>;
export class InMemoryCodingAgentModelsStore implements ModelsStore {
private readonly entries = new Map<string, ModelsStoreEntry>();
async read(providerId: string, options?: ModelsStoreOperationOptions): Promise<ModelsStoreEntry | undefined> {
options?.signal?.throwIfAborted();
const entry = this.entries.get(providerId);
return entry ? structuredClone(entry) : undefined;
}
async write(providerId: string, entry: ModelsStoreEntry, options?: ModelsStoreOperationOptions): Promise<void> {
options?.signal?.throwIfAborted();
this.entries.set(providerId, structuredClone(entry));
}
async delete(providerId: string, options?: ModelsStoreOperationOptions): Promise<void> {
options?.signal?.throwIfAborted();
this.entries.delete(providerId);
}
}
/** Locked JSON-backed storage for dynamically refreshed provider catalogs. */
export class FileModelsStore implements ModelsStore {
private readonly storage: AuthStorageBackend;
constructor(path: string = join(getAgentDir(), "models-store.json")) {
this.storage = new FileAuthStorageBackend(path);
}
private parse(content: string | undefined): StoredModels {
return content ? (JSON.parse(content) as StoredModels) : {};
}
async read(providerId: string, options?: ModelsStoreOperationOptions): Promise<ModelsStoreEntry | undefined> {
return this.storage.withLockAsync(
async (content) => ({ result: structuredClone(this.parse(content)[providerId]) }),
options,
);
}
async write(providerId: string, entry: ModelsStoreEntry, options?: ModelsStoreOperationOptions): Promise<void> {
await this.storage.withLockAsync(async (content) => {
const current = this.parse(content);
current[providerId] = structuredClone(entry);
return { result: undefined, next: JSON.stringify(current, null, 2) };
}, options);
}
async delete(providerId: string, options?: ModelsStoreOperationOptions): Promise<void> {
await this.storage.withLockAsync(async (content) => {
const current = this.parse(content);
delete current[providerId];
return { result: undefined, next: JSON.stringify(current, null, 2) };
}, options);
}
}