Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
5.3 kB · 164 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165/** * Zip Cache Adapters * * I/O helpers for the plugin zip cache. These functions handle reading/writing * zip-cache-local metadata files, extracting ZIPs to session directories, * and creating ZIPs for newly installed plugins. * * The zip cache stores data on a mounted volume (e.g., Filestore) that persists * across ephemeral container lifetimes. The session cache is a local temp dir * for extracted plugins used during a single session. */
import { readFile } from 'fs/promises'import { join } from 'path'import { logForDebugging } from '../debug.js'import { jsonParse, jsonStringify } from '../slowOperations.js'import { loadKnownMarketplacesConfigSafe } from './marketplaceManager.js'import { type KnownMarketplacesFile, KnownMarketplacesFileSchema, type PluginMarketplace, PluginMarketplaceSchema,} from './schemas.js'import { atomicWriteToZipCache, getMarketplaceJsonRelativePath, getPluginZipCachePath, getZipCacheKnownMarketplacesPath,} from './zipCache.js'
// ── Metadata I/O ──
/** * Read known_marketplaces.json from the zip cache. * Returns empty object if file doesn't exist, can't be parsed, or fails schema * validation (data comes from a shared mounted volume — other containers may write). */export async function readZipCacheKnownMarketplaces(): Promise<KnownMarketplacesFile> { try { const content = await readFile(getZipCacheKnownMarketplacesPath(), 'utf-8') const parsed = KnownMarketplacesFileSchema().safeParse(jsonParse(content)) if (!parsed.success) { logForDebugging( `Invalid known_marketplaces.json in zip cache: ${parsed.error.message}`, { level: 'error' }, ) return {} } return parsed.data } catch { return {} }}
/** * Write known_marketplaces.json to the zip cache atomically. */export async function writeZipCacheKnownMarketplaces( data: KnownMarketplacesFile,): Promise<void> { await atomicWriteToZipCache( getZipCacheKnownMarketplacesPath(), jsonStringify(data, null, 2), )}
// ── Marketplace JSON ──
/** * Read a marketplace JSON file from the zip cache. */export async function readMarketplaceJson( marketplaceName: string,): Promise<PluginMarketplace | null> { const zipCachePath = getPluginZipCachePath() if (!zipCachePath) { return null } const relPath = getMarketplaceJsonRelativePath(marketplaceName) const fullPath = join(zipCachePath, relPath) try { const content = await readFile(fullPath, 'utf-8') const parsed = jsonParse(content) const result = PluginMarketplaceSchema().safeParse(parsed) if (result.success) { return result.data } logForDebugging( `Invalid marketplace JSON for ${marketplaceName}: ${result.error}`, ) return null } catch { return null }}
/** * Save a marketplace JSON to the zip cache from its install location. */export async function saveMarketplaceJsonToZipCache( marketplaceName: string, installLocation: string,): Promise<void> { const zipCachePath = getPluginZipCachePath() if (!zipCachePath) { return } const content = await readMarketplaceJsonContent(installLocation) if (content !== null) { const relPath = getMarketplaceJsonRelativePath(marketplaceName) await atomicWriteToZipCache(join(zipCachePath, relPath), content) }}
/** * Read marketplace.json content from a cloned marketplace directory or file. * For directory sources: checks .claude-plugin/marketplace.json, marketplace.json * For URL sources: the installLocation IS the marketplace JSON file itself. */async function readMarketplaceJsonContent(dir: string): Promise<string | null> { const candidates = [ join(dir, '.claude-plugin', 'marketplace.json'), join(dir, 'marketplace.json'), dir, // For URL sources, installLocation IS the marketplace JSON file ] for (const candidate of candidates) { try { return await readFile(candidate, 'utf-8') } catch { // ENOENT (doesn't exist) or EISDIR (directory) — try next } } return null}
/** * Sync marketplace data to zip cache for offline access. * Saves marketplace JSONs and merges with previously cached data * so ephemeral containers can access marketplaces without re-cloning. */export async function syncMarketplacesToZipCache(): Promise<void> { // Read-only iteration — Safe variant so a corrupted config doesn't throw. // This runs during startup paths; a throw here cascades to the same // try-block that catches loadAllPlugins failures. const knownMarketplaces = await loadKnownMarketplacesConfigSafe()
// Save marketplace JSONs to zip cache for (const [name, entry] of Object.entries(knownMarketplaces)) { if (!entry.installLocation) continue try { await saveMarketplaceJsonToZipCache(name, entry.installLocation) } catch (error) { logForDebugging(`Failed to save marketplace JSON for ${name}: ${error}`) } }
// Merge with previously cached data (ephemeral containers lose global config) const zipCacheKnownMarketplaces = await readZipCacheKnownMarketplaces() const mergedKnownMarketplaces: KnownMarketplacesFile = { ...zipCacheKnownMarketplaces, ...knownMarketplaces, } await writeZipCacheKnownMarketplaces(mergedKnownMarketplaces)}