2026-01-26 11:53:41 +09:00
|
|
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs"
|
|
|
|
|
import { join } from "path"
|
|
|
|
|
import { log } from "./logger"
|
2026-03-09 23:37:16 +09:00
|
|
|
import * as dataPath from "./data-path"
|
2026-01-26 11:53:41 +09:00
|
|
|
|
|
|
|
|
const CONNECTED_PROVIDERS_CACHE_FILE = "connected-providers.json"
|
|
|
|
|
const PROVIDER_MODELS_CACHE_FILE = "provider-models.json"
|
|
|
|
|
|
|
|
|
|
interface ConnectedProvidersCache {
|
|
|
|
|
connected: string[]
|
|
|
|
|
updatedAt: string
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 15:32:08 +09:00
|
|
|
interface ModelMetadata {
|
|
|
|
|
id: string
|
|
|
|
|
provider?: string
|
|
|
|
|
context?: number
|
|
|
|
|
output?: number
|
|
|
|
|
name?: string
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-26 11:53:41 +09:00
|
|
|
interface ProviderModelsCache {
|
2026-02-05 15:32:08 +09:00
|
|
|
models: Record<string, string[] | ModelMetadata[]>
|
2026-01-26 11:53:41 +09:00
|
|
|
connected: string[]
|
|
|
|
|
updatedAt: string
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
export function createConnectedProvidersCacheStore(
|
|
|
|
|
getCacheDir: () => string = dataPath.getOmoOpenCodeCacheDir
|
|
|
|
|
) {
|
|
|
|
|
function getCacheFilePath(filename: string): string {
|
|
|
|
|
return join(getCacheDir(), filename)
|
2026-01-26 11:53:41 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 14:19:00 +09:00
|
|
|
let memConnected: string[] | null | undefined
|
|
|
|
|
let memProviderModels: ProviderModelsCache | null | undefined
|
|
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
function ensureCacheDir(): void {
|
|
|
|
|
const cacheDir = getCacheDir()
|
|
|
|
|
if (!existsSync(cacheDir)) {
|
|
|
|
|
mkdirSync(cacheDir, { recursive: true })
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
function readConnectedProvidersCache(): string[] | null {
|
2026-03-18 14:19:00 +09:00
|
|
|
if (memConnected !== undefined) return memConnected
|
2026-03-18 13:17:01 +09:00
|
|
|
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE)
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
if (!existsSync(cacheFile)) {
|
|
|
|
|
log("[connected-providers-cache] Cache file not found", { cacheFile })
|
2026-03-18 14:19:00 +09:00
|
|
|
memConnected = null
|
2026-03-18 13:17:01 +09:00
|
|
|
return null
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
try {
|
|
|
|
|
const content = readFileSync(cacheFile, "utf-8")
|
|
|
|
|
const data = JSON.parse(content) as ConnectedProvidersCache
|
|
|
|
|
log("[connected-providers-cache] Read cache", { count: data.connected.length, updatedAt: data.updatedAt })
|
2026-03-18 14:19:00 +09:00
|
|
|
memConnected = data.connected
|
2026-03-18 13:17:01 +09:00
|
|
|
return data.connected
|
|
|
|
|
} catch (err) {
|
|
|
|
|
log("[connected-providers-cache] Error reading cache", { error: String(err) })
|
2026-03-18 14:19:00 +09:00
|
|
|
memConnected = null
|
2026-03-18 13:17:01 +09:00
|
|
|
return null
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
function hasConnectedProvidersCache(): boolean {
|
|
|
|
|
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE)
|
|
|
|
|
return existsSync(cacheFile)
|
2026-01-26 11:53:41 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
function writeConnectedProvidersCache(connected: string[]): void {
|
|
|
|
|
ensureCacheDir()
|
|
|
|
|
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE)
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
const data: ConnectedProvidersCache = {
|
|
|
|
|
connected,
|
|
|
|
|
updatedAt: new Date().toISOString(),
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
try {
|
|
|
|
|
writeFileSync(cacheFile, JSON.stringify(data, null, 2))
|
2026-03-18 14:19:00 +09:00
|
|
|
memConnected = connected
|
2026-03-18 13:17:01 +09:00
|
|
|
log("[connected-providers-cache] Cache written", { count: connected.length })
|
|
|
|
|
} catch (err) {
|
|
|
|
|
log("[connected-providers-cache] Error writing cache", { error: String(err) })
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
function readProviderModelsCache(): ProviderModelsCache | null {
|
2026-03-18 14:19:00 +09:00
|
|
|
if (memProviderModels !== undefined) return memProviderModels
|
2026-03-18 13:17:01 +09:00
|
|
|
const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE)
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
if (!existsSync(cacheFile)) {
|
|
|
|
|
log("[connected-providers-cache] Provider-models cache file not found", { cacheFile })
|
2026-03-18 14:19:00 +09:00
|
|
|
memProviderModels = null
|
2026-03-18 13:17:01 +09:00
|
|
|
return null
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
try {
|
|
|
|
|
const content = readFileSync(cacheFile, "utf-8")
|
|
|
|
|
const data = JSON.parse(content) as ProviderModelsCache
|
|
|
|
|
log("[connected-providers-cache] Read provider-models cache", {
|
|
|
|
|
providerCount: Object.keys(data.models).length,
|
|
|
|
|
updatedAt: data.updatedAt,
|
|
|
|
|
})
|
2026-03-18 14:19:00 +09:00
|
|
|
memProviderModels = data
|
2026-03-18 13:17:01 +09:00
|
|
|
return data
|
|
|
|
|
} catch (err) {
|
|
|
|
|
log("[connected-providers-cache] Error reading provider-models cache", { error: String(err) })
|
2026-03-18 14:19:00 +09:00
|
|
|
memProviderModels = null
|
2026-03-18 13:17:01 +09:00
|
|
|
return null
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
function hasProviderModelsCache(): boolean {
|
|
|
|
|
const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE)
|
|
|
|
|
return existsSync(cacheFile)
|
2026-01-26 11:53:41 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
function writeProviderModelsCache(data: { models: Record<string, string[]>; connected: string[] }): void {
|
|
|
|
|
ensureCacheDir()
|
|
|
|
|
const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE)
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
const cacheData: ProviderModelsCache = {
|
|
|
|
|
...data,
|
|
|
|
|
updatedAt: new Date().toISOString(),
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
try {
|
|
|
|
|
writeFileSync(cacheFile, JSON.stringify(cacheData, null, 2))
|
2026-03-18 14:19:00 +09:00
|
|
|
memProviderModels = cacheData
|
2026-03-18 13:17:01 +09:00
|
|
|
log("[connected-providers-cache] Provider-models cache written", {
|
|
|
|
|
providerCount: Object.keys(data.models).length,
|
|
|
|
|
})
|
|
|
|
|
} catch (err) {
|
|
|
|
|
log("[connected-providers-cache] Error writing provider-models cache", { error: String(err) })
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
async function updateConnectedProvidersCache(client: {
|
|
|
|
|
provider?: {
|
|
|
|
|
list?: () => Promise<{
|
|
|
|
|
data?: {
|
|
|
|
|
connected?: string[]
|
|
|
|
|
all?: Array<{ id: string; models?: Record<string, unknown> }>
|
|
|
|
|
}
|
|
|
|
|
}>
|
|
|
|
|
}
|
|
|
|
|
}): Promise<void> {
|
|
|
|
|
if (!client?.provider?.list) {
|
|
|
|
|
log("[connected-providers-cache] client.provider.list not available")
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
try {
|
|
|
|
|
const result = await client.provider.list()
|
|
|
|
|
const connected = result.data?.connected ?? []
|
|
|
|
|
log("[connected-providers-cache] Fetched connected providers", {
|
|
|
|
|
count: connected.length,
|
|
|
|
|
providers: connected,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
writeConnectedProvidersCache(connected)
|
|
|
|
|
|
|
|
|
|
const modelsByProvider: Record<string, string[]> = {}
|
|
|
|
|
const allProviders = result.data?.all ?? []
|
|
|
|
|
|
|
|
|
|
for (const provider of allProviders) {
|
|
|
|
|
if (provider.models) {
|
|
|
|
|
const modelIds = Object.keys(provider.models)
|
|
|
|
|
if (modelIds.length > 0) {
|
|
|
|
|
modelsByProvider[provider.id] = modelIds
|
|
|
|
|
}
|
2026-02-10 13:25:49 +09:00
|
|
|
}
|
2026-01-26 11:53:41 +09:00
|
|
|
}
|
2026-01-30 16:14:14 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
log("[connected-providers-cache] Extracted models from provider list", {
|
|
|
|
|
providerCount: Object.keys(modelsByProvider).length,
|
|
|
|
|
totalModels: Object.values(modelsByProvider).reduce((sum, ids) => sum + ids.length, 0),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
writeProviderModelsCache({
|
|
|
|
|
models: modelsByProvider,
|
|
|
|
|
connected,
|
|
|
|
|
})
|
|
|
|
|
} catch (err) {
|
|
|
|
|
log("[connected-providers-cache] Error updating cache", { error: String(err) })
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-10 13:25:49 +09:00
|
|
|
|
2026-03-18 13:17:01 +09:00
|
|
|
return {
|
|
|
|
|
readConnectedProvidersCache,
|
|
|
|
|
hasConnectedProvidersCache,
|
|
|
|
|
readProviderModelsCache,
|
|
|
|
|
hasProviderModelsCache,
|
|
|
|
|
writeProviderModelsCache,
|
|
|
|
|
updateConnectedProvidersCache,
|
2026-01-26 11:53:41 +09:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-18 13:17:01 +09:00
|
|
|
|
|
|
|
|
const defaultConnectedProvidersCacheStore = createConnectedProvidersCacheStore(
|
|
|
|
|
() => dataPath.getOmoOpenCodeCacheDir()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
export const {
|
|
|
|
|
readConnectedProvidersCache,
|
|
|
|
|
hasConnectedProvidersCache,
|
|
|
|
|
readProviderModelsCache,
|
|
|
|
|
hasProviderModelsCache,
|
|
|
|
|
writeProviderModelsCache,
|
|
|
|
|
updateConnectedProvidersCache,
|
|
|
|
|
} = defaultConnectedProvidersCacheStore
|