/** * Marketplace Manager - Manage multiple MCP marketplaces */ import { Logger } from './shared/logger.js'; type MarketplaceSourceType = 'github' | 'git-ssh' | 'url' | 'local'; export interface Marketplace { name: string; repo: string; url: string; sourceType: MarketplaceSourceType; source: string; enabled: boolean; lastUpdated?: string; } /** * Photon metadata from photons.json manifest */ export interface PhotonMetadata { name: string; version: string; description: string; author?: string; homepage?: string; repository?: string; license?: string; icon?: string; internal?: boolean; tags?: string[]; category?: string; source: string; hash?: string; contentHash?: string; tools?: string[]; assets?: string[]; } /** * Local installation metadata for tracking Photon origins */ export interface PhotonInstallMetadata { marketplace: string; marketplaceRepo: string; version: string; originalHash: string; installedAt: string; lastChecked?: string; } /** * Local metadata file structure */ interface LocalMetadata { photons: Record; } /** * Marketplace manifest (.marketplace/photons.json) */ interface MarketplaceManifest { name: string; version?: string; description?: string; owner?: { name: string; email?: string; url?: string; }; photons: PhotonMetadata[]; } /** * Calculate SHA-256 hash of file content */ export declare function calculateFileHash(filePath: string): Promise; /** * Calculate SHA-256 hash of string content */ export declare function calculateHash(content: string): string; /** * Calculate combined hash of a photon source file + its declared assets. * This ensures asset-only changes (e.g. board.html update) are detected. */ export declare function calculatePhotonHash(sourceFilePath: string, assets?: string[], baseDir?: string): Promise; /** * Read local installation metadata */ export declare function readLocalMetadata(baseDir?: string): Promise; export declare class MarketplaceManager { private config; private logger; private configDir; private configFile; private cacheDir; private metadataFile; constructor(logger?: Logger, baseDir?: string); initialize(): Promise; /** * Ensure all built-in marketplaces exist in config. * Handles upgrades where new built-in marketplaces are added. */ private ensureBuiltInMarketplaces; save(): Promise; /** * Check if a marketplace is built-in (cannot be removed) */ isBuiltIn(source: string): boolean; /** * Get all marketplaces */ getAll(): Marketplace[]; /** * Get enabled marketplaces */ getEnabled(): Marketplace[]; /** * Get marketplace by name */ get(name: string): Marketplace | undefined; /** * Parse marketplace source into structured info * Supports: * 1. GitHub shorthand: username/repo * 2. GitHub HTTPS: https://github.com/username/repo[.git] * 3. GitHub SSH: git@github.com:username/repo.git * 4. Direct URL: https://example.com/photons.json * 5. Local path: ./path/to/marketplace or /absolute/path */ private parseMarketplaceSource; /** * Get next available name with numeric suffix if name already exists * e.g., if 'photon-mcps' exists, returns 'photon-mcps-2' * if 'photon-mcps' and 'photon-mcps-2' exist, returns 'photon-mcps-3' */ private getUniqueName; /** * Check if a marketplace with the same source already exists */ private findBySource; /** * Add a new marketplace * Supports: * - GitHub: username/repo, https://github.com/username/repo, git@github.com:username/repo.git * - Direct URL: https://example.com/photons.json * - Local path: ./path/to/marketplace, /absolute/path * * If a marketplace with the same name already exists, automatically appends a numeric suffix (-2, -3, etc.) * If the exact same source already exists, returns the existing marketplace without creating a duplicate. * * @returns Object with marketplace info and 'added' flag (false if already existed) */ add(source: string): Promise<{ marketplace: Omit; added: boolean; }>; /** * Remove a marketplace */ remove(name: string): Promise; /** * Enable/disable a marketplace */ setEnabled(name: string, enabled: boolean): Promise; /** * Get cache file path for marketplace */ private getCacheFile; /** * Fetch photons.json manifest from various sources */ fetchManifest(marketplace: Marketplace): Promise; /** * Update marketplace cache (fetch and save photons.json manifest) */ updateMarketplaceCache(name: string): Promise; /** * Update all enabled marketplace caches */ updateAllCaches(): Promise>; /** * Get cached marketplace manifest */ getCachedManifest(marketplaceName: string): Promise; /** * Get Photon metadata from cached manifest */ getPhotonMetadata(photonName: string): Promise<{ metadata: PhotonMetadata; marketplace: Marketplace; } | null>; /** * Get all Photons with metadata from all enabled marketplaces */ getAllPhotons(): Promise>; /** * Get count of available Photons per marketplace */ getMarketplaceCounts(): Promise>; /** * Check if marketplace cache is stale * @param ttlMs Custom TTL in milliseconds (defaults to CACHE_TTL_MS = 24h) */ private isCacheStale; /** * Auto-update stale caches * @param ttlMs Custom TTL — caches older than this are refreshed (defaults to 24h) * Returns true if any updates were performed */ autoUpdateStaleCaches(ttlMs?: number): Promise; /** * Try to fetch MCP from all enabled marketplaces * Returns content, marketplace info, and metadata (version, hash) */ fetchMCP(mcpName: string): Promise<{ content: string; marketplace: Marketplace; metadata?: PhotonMetadata; } | null>; /** * Fetch assets for a photon from a specific marketplace */ fetchAssets(marketplace: Marketplace, assets: string[]): Promise>; /** * Repair missing assets for installed photons. * Detects photons installed before the asset-download fix and re-downloads their assets. * Safe to call on every startup — only fetches when assets are actually missing. */ repairMissingAssets(workingDir: string): Promise; /** * Fetch version from all enabled marketplaces */ fetchVersion(mcpName: string): Promise<{ version: string; marketplace: Marketplace; } | null>; /** * Search for Photon in all marketplaces * Searches in name, description, tags, and author fields */ search(query: string): Promise>; /** * List all available MCPs from a marketplace * Note: Requires marketplace to have a .marketplace/photons.json file */ listFromMarketplace(marketplaceName: string): Promise; /** * Install a photon and its assets to the working directory. * * This is the canonical installation path — used by both CLI and Beam UI. * Handles writing the .photon.ts file, saving metadata, and downloading * all declared asset files (UI templates, etc.) with path-traversal protection. * * @param result - The result from fetchMCP() * @param name - Photon name (used for filename) * @param workingDir - Directory to install into (e.g., ~/.photon) * @returns Installed file path and list of asset paths written */ installPhoton(result: { content: string; marketplace: Marketplace; metadata?: PhotonMetadata; }, name: string, workingDir: string): Promise<{ photonPath: string; assetsInstalled: string[]; }>; /** * Parse @ui tags from photon content and return repo-relative asset paths. * e.g., "@ui dashboard ./ui/dashboard.html" → "claw/ui/dashboard.html" * or "@ui dashboard" → conventional "claw/ui/dashboard.*" candidates. */ private extractUIAssetPaths; private extractPathlessUIIds; /** * After installing a photon, resolve and install its @photon dependencies * from the same marketplace. Handles transitive deps with circular guard. */ installTransitiveDeps(content: string, marketplace: Marketplace, workingDir: string, visited?: Set): Promise; /** * Fetch a specific photon from a specific marketplace (not searching all). */ private fetchMCPFromMarketplace; /** * Extract namespace from marketplace repo. * e.g., 'portel-dev/photons' → 'portel-dev' * e.g., 'portel-dev/skills' → 'portel-dev' */ private extractNamespace; /** * Save installation metadata for a Photon */ savePhotonMetadata(fileName: string, marketplace: Marketplace, metadata: PhotonMetadata, combinedHash: string): Promise; /** * Get local installation metadata for a Photon */ getPhotonInstallMetadata(fileName: string): Promise; private readMetadata; private writeMetadata; /** * Check if a Photon file has been modified since installation * Compares combined hash (source + assets) against stored originalHash */ isPhotonModified(filePath: string, fileName: string): Promise; /** * Find all marketplaces that have a specific MCP (for conflict detection) */ findAllSources(mcpName: string): Promise>; /** * Detect all MCP conflicts across marketplaces */ detectAllConflicts(): Promise>>; /** * Check if adding/upgrading an MCP would create a conflict */ checkConflict(mcpName: string, targetMarketplace?: string): Promise<{ hasConflict: boolean; sources: Array<{ marketplace: Marketplace; metadata?: PhotonMetadata; }>; recommendation?: string; }>; /** * Get marketplace sources suitable as fork targets */ getForkTargets(): Promise>; /** * Fork a photon — remove marketplace tracking, optionally push to a target repo. * Shared logic used by both CLI and Beam. */ forkPhoton(name: string, workingDir: string, options?: { targetRepo?: string; createRepo?: string; newName?: string; }): Promise<{ success: boolean; message: string; requiresName?: boolean; suggestedName?: string; }>; private toMetadataKey; private copyPhotonAssetDir; private movePhotonAssetDir; /** * Contribute a photon back upstream via PR. * Shared logic used by both CLI and Beam. */ contributePhoton(name: string, workingDir: string, options?: { dryRun?: boolean; branch?: string; }): Promise<{ success: boolean; prUrl?: string; message: string; }>; /** * Resolve and install a photon from a GitHub shorthand reference. * Format: owner/repo (photon name = repo name) * or owner/repo/photon-name * * - Adds the marketplace to config (idempotent) * - Fetches and installs the photon to workingDir * - If already installed, skips fetch * - Returns the resolved photon name */ fetchAndInstallFromRef(ref: string, workingDir: string): Promise<{ photonName: string; alreadyInstalled: boolean; }>; /** * Recursively fetch a photon and its @photon dependencies from a GitHub repo. * Tracks visited set to prevent circular dependency loops. */ private fetchAndInstallWithDeps; /** * Compare two semver versions * Returns: positive if v1 > v2, negative if v1 < v2, 0 if equal */ private compareVersions; } export {}; //# sourceMappingURL=marketplace-manager.d.ts.map