import { existsSync, readFileSync } from "node:fs"; import { join, relative, resolve, sep } from "node:path"; export function readConfiguredNpmPackageNames(settingsPath: string): Set { const names = new Set(); if (!existsSync(settingsPath)) return names; try { const raw = JSON.parse(readFileSync(settingsPath, "utf8")); const packages = raw?.packages; if (!Array.isArray(packages)) return names; for (const entry of packages) { const source = typeof entry === "string" ? entry : entry?.source; if (typeof source === "string" && source.startsWith("npm:")) { const name = source.slice("npm:".length).trim(); if (name) names.add(name); } } } catch { /* ignore invalid settings */ } return names; } export function extractNpmPackageNameFromPath(nodeModulesRoot: string, pathValue: string): string | null { if (!pathValue) return null; const root = resolve(nodeModulesRoot); const normalized = resolve(pathValue); if (normalized !== root && !normalized.startsWith(`${root}${sep}`)) { return null; } const rel = relative(root, normalized); const parts = rel.split(sep).filter(Boolean); if (parts.length === 0) return null; if (parts[0].startsWith("@") && parts.length >= 2) { return `${parts[0]}/${parts[1]}`; } return parts[0] || null; } export function isLocalAgentExtension(pathValue: string, agentExtensionsDir: string): boolean { if (!pathValue) return false; const resolved = resolve(pathValue); const root = resolve(agentExtensionsDir); return resolved === root || resolved.startsWith(`${root}${sep}`); } export function isConfiguredNpmExtension( pathValue: string, nodeModulesRoot: string, configuredPackages: Set, ): boolean { const packageName = extractNpmPackageNameFromPath(nodeModulesRoot, pathValue); return packageName !== null && configuredPackages.has(packageName); } export function getExtensionCategoryFromPath( pathValue: string, agentExtensionsDir: string, nodeModulesRoot: string, configuredPackages: Set, ): "local" | "npm" | null { if (isConfiguredNpmExtension(pathValue, nodeModulesRoot, configuredPackages)) { return "npm"; } if (isLocalAgentExtension(pathValue, agentExtensionsDir)) { return "local"; } return null; } export function resolveNpmSource(pathValue: string, nodeModulesRoot: string, fallbackSource: string): string { if (fallbackSource.startsWith("npm:")) return fallbackSource; const packageName = extractNpmPackageNameFromPath(nodeModulesRoot, pathValue); return packageName ? `npm:${packageName}` : fallbackSource; } export function readNpmPackageVersion( nodeModulesRoot: string, pathValue: string, source: string, ): string | undefined { let packageName = extractNpmPackageNameFromPath(nodeModulesRoot, pathValue); if (!packageName && source.startsWith("npm:")) { packageName = source.slice("npm:".length).trim() || null; } if (!packageName) return undefined; const pkgJsonPath = join(nodeModulesRoot, packageName, "package.json"); if (!existsSync(pkgJsonPath)) return undefined; try { const raw = JSON.parse(readFileSync(pkgJsonPath, "utf8")) as { version?: unknown }; return typeof raw.version === "string" && raw.version.trim() ? raw.version.trim() : undefined; } catch { return undefined; } }