Make provider catalogs compact and content-addressed so repeat launches can reuse the persisted snapshot instead of transferring it again. Remove eager global catalog loading and the stopped-agent defensive tail fetch, while keeping sequence-gap and reconnect recovery as the correctness paths. Add an opt-in production-ID Android profiling build for local startup traces.
130 lines
3.8 KiB
TypeScript
130 lines
3.8 KiB
TypeScript
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
import type { ProviderSnapshotEntry } from "@getpaseo/protocol/agent-types";
|
|
import {
|
|
expandProviderSnapshot,
|
|
type CompactProviderSnapshot,
|
|
} from "@getpaseo/protocol/provider-snapshot-codec";
|
|
import { CompactProviderSnapshotSchema } from "@getpaseo/protocol/messages";
|
|
|
|
const CACHE_VERSION = 1;
|
|
const CACHE_KEY_PREFIX = "@paseo/provider-snapshot/v1";
|
|
|
|
interface ProviderSnapshotStorage {
|
|
getItem(key: string): Promise<string | null>;
|
|
setItem(key: string, value: string): Promise<void>;
|
|
removeItem(key: string): Promise<void>;
|
|
}
|
|
|
|
interface StoredProviderSnapshot {
|
|
version: typeof CACHE_VERSION;
|
|
hash: string;
|
|
generatedAt: string;
|
|
compactSnapshot: CompactProviderSnapshot;
|
|
}
|
|
|
|
export interface CachedProviderSnapshot extends StoredProviderSnapshot {
|
|
entries: ProviderSnapshotEntry[];
|
|
}
|
|
|
|
export interface ProviderSnapshotCache {
|
|
read(serverId: string, cwd: string | null): Promise<CachedProviderSnapshot | null>;
|
|
write(input: {
|
|
serverId: string;
|
|
cwd: string | null;
|
|
hash: string;
|
|
generatedAt: string;
|
|
compactSnapshot: CompactProviderSnapshot;
|
|
}): Promise<void>;
|
|
}
|
|
|
|
export class ProviderSnapshotCacheMissError extends Error {
|
|
constructor() {
|
|
super("Daemon returned not-modified without a cached provider snapshot");
|
|
this.name = "ProviderSnapshotCacheMissError";
|
|
}
|
|
}
|
|
|
|
function cacheKey(serverId: string, cwd: string | null): string {
|
|
return `${CACHE_KEY_PREFIX}:${JSON.stringify([serverId, cwd])}`;
|
|
}
|
|
|
|
function parseStoredProviderSnapshot(value: string): StoredProviderSnapshot | null {
|
|
const parsed: unknown = JSON.parse(value);
|
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
const version = Reflect.get(parsed, "version");
|
|
const hash = Reflect.get(parsed, "hash");
|
|
const generatedAt = Reflect.get(parsed, "generatedAt");
|
|
const compactSnapshot = CompactProviderSnapshotSchema.safeParse(
|
|
Reflect.get(parsed, "compactSnapshot"),
|
|
);
|
|
if (
|
|
version !== CACHE_VERSION ||
|
|
typeof hash !== "string" ||
|
|
typeof generatedAt !== "string" ||
|
|
!compactSnapshot.success
|
|
) {
|
|
return null;
|
|
}
|
|
return {
|
|
version,
|
|
hash,
|
|
generatedAt,
|
|
compactSnapshot: compactSnapshot.data,
|
|
};
|
|
}
|
|
|
|
async function discardCachedSnapshot(storage: ProviderSnapshotStorage, key: string): Promise<void> {
|
|
try {
|
|
await storage.removeItem(key);
|
|
} catch (error) {
|
|
if (!(error instanceof Error)) throw error;
|
|
}
|
|
}
|
|
|
|
export function createProviderSnapshotCache(
|
|
storage: ProviderSnapshotStorage,
|
|
): ProviderSnapshotCache {
|
|
return {
|
|
async read(serverId, cwd) {
|
|
const key = cacheKey(serverId, cwd);
|
|
let value: string | null;
|
|
try {
|
|
value = await storage.getItem(key);
|
|
} catch (error) {
|
|
if (error instanceof Error) return null;
|
|
throw error;
|
|
}
|
|
if (value === null) return null;
|
|
try {
|
|
const stored = parseStoredProviderSnapshot(value);
|
|
if (!stored) {
|
|
await discardCachedSnapshot(storage, key);
|
|
return null;
|
|
}
|
|
return { ...stored, entries: expandProviderSnapshot(stored.compactSnapshot) };
|
|
} catch (error) {
|
|
if (!(error instanceof SyntaxError) && !(error instanceof RangeError)) {
|
|
throw error;
|
|
}
|
|
await discardCachedSnapshot(storage, key);
|
|
return null;
|
|
}
|
|
},
|
|
async write(input) {
|
|
const stored: StoredProviderSnapshot = {
|
|
version: CACHE_VERSION,
|
|
hash: input.hash,
|
|
generatedAt: input.generatedAt,
|
|
compactSnapshot: input.compactSnapshot,
|
|
};
|
|
try {
|
|
await storage.setItem(cacheKey(input.serverId, input.cwd), JSON.stringify(stored));
|
|
} catch (error) {
|
|
if (!(error instanceof Error)) throw error;
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
export const providerSnapshotCache = createProviderSnapshotCache(AsyncStorage);
|