aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
110 lines • 4.09 kB
JavaScript
/**
* Marketplace Local Cache
*
* Manages the ~/.aiwg/marketplace-cache/ directory that stores fetched plugins.
*
* Layout: ~/.aiwg/marketplace-cache/<source>/<packageId>/<version>/
*
* @implements #787
*/
import fs from 'fs';
import path from 'path';
import os from 'os';
/**
* Resolve the cache root. Supports AIWG_CACHE_DIR override for testing.
*/
export function getCacheRoot() {
if (process.env.AIWG_CACHE_DIR) {
return process.env.AIWG_CACHE_DIR;
}
return path.join(os.homedir(), '.aiwg', 'marketplace-cache');
}
/**
* Resolve the path for a specific package version
*/
export function getPackagePath(source, packageId, version) {
// Sanitize package ID for filesystem safety (replace / and : with -)
const safePackageId = packageId.replace(/[/:]/g, '__');
return path.join(getCacheRoot(), source, safePackageId, version);
}
/**
* Check whether a package version is cached
*/
export function isCached(source, packageId, version) {
const packagePath = getPackagePath(source, packageId, version);
const manifestPath = path.join(packagePath, 'manifest.json');
return fs.existsSync(manifestPath);
}
/**
* Write a package bundle to the cache
*/
export function cachePackage(bundle) {
const packagePath = getPackagePath(bundle.metadata.source, bundle.metadata.name, bundle.metadata.version);
fs.mkdirSync(packagePath, { recursive: true });
// Write normalized manifest
fs.writeFileSync(path.join(packagePath, 'manifest.json'), JSON.stringify(bundle.metadata, null, 2) + '\n', 'utf-8');
// Write raw source manifest for update detection
fs.writeFileSync(path.join(packagePath, 'raw-manifest.json'), JSON.stringify(bundle.rawManifest, null, 2) + '\n', 'utf-8');
// Note: actual artifact files are expected to be in bundle.localPath already;
// in a complete implementation we'd copy them into packagePath here.
return packagePath;
}
export function listCachedPackages() {
const root = getCacheRoot();
if (!fs.existsSync(root))
return [];
const entries = [];
// Walk <source>/<packageId>/<version>/manifest.json
const sources = fs.readdirSync(root);
for (const source of sources) {
const sourcePath = path.join(root, source);
if (!fs.statSync(sourcePath).isDirectory())
continue;
const packages = fs.readdirSync(sourcePath);
for (const pkg of packages) {
const pkgPath = path.join(sourcePath, pkg);
if (!fs.statSync(pkgPath).isDirectory())
continue;
const versions = fs.readdirSync(pkgPath);
for (const version of versions) {
const manifestPath = path.join(pkgPath, version, 'manifest.json');
if (!fs.existsSync(manifestPath))
continue;
try {
const metadata = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
const stat = fs.statSync(manifestPath);
entries.push({
source: source,
packageId: pkg.replace(/__/g, '/'),
version,
cachedAt: stat.mtime,
metadata,
});
}
catch {
// Skip malformed entries
}
}
}
}
return entries;
}
/**
* Remove a specific cached package (all versions or a specific one)
*/
export function uncachePackage(source, packageId, version) {
const safePackageId = packageId.replace(/[/:]/g, '__');
const pkgPath = path.join(getCacheRoot(), source, safePackageId);
if (version) {
const versionPath = path.join(pkgPath, version);
if (!fs.existsSync(versionPath))
return false;
fs.rmSync(versionPath, { recursive: true, force: true });
return true;
}
if (!fs.existsSync(pkgPath))
return false;
fs.rmSync(pkgPath, { recursive: true, force: true });
return true;
}
//# sourceMappingURL=cache.js.map