{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/registry/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAE,KAAK,QAAQ,EAAoB,MAAM,aAAa,CAAC;AAS9D,sDAAsD;AACtD,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAoB/C;AAMD,MAAM,MAAM,kBAAkB,GAAG,eAAe,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvE,MAAM,MAAM,kBAAkB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAC5D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAMjD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAqCnE","sourcesContent":["/**\n * WS15: Registry loader — three-tier fallback chain.\n *\n * Resolution order (first readable + valid wins):\n *   1. ~/.cave/agent/registry.json      (user override)\n *   2. ~/.cave/agent/registry-cache.json (fetched cache)\n *   3. <package>/registry/registry.json  (bundled fallback)\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { type Registry, validateRegistry } from \"./schema.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// ---------------------------------------------------------------------------\n// Path helpers\n// ---------------------------------------------------------------------------\n\n/** User override path: ~/.cave/agent/registry.json */\nexport function getUserOverridePath(configDir?: string): string {\n\tconst base = configDir ?? join(homedir(), \".cave\", \"agent\");\n\treturn join(base, \"registry.json\");\n}\n\n/** Fetched cache path: ~/.cave/agent/registry-cache.json */\nexport function getCachePath(configDir?: string): string {\n\tconst base = configDir ?? join(homedir(), \".cave\", \"agent\");\n\treturn join(base, \"registry-cache.json\");\n}\n\n/**\n * Bundled fallback path: <package-root>/registry/registry.json\n *\n * Works from both src/ (ts-node / tsx) and dist/ (compiled) since the\n * registry/ dir lives at the package root, one or two levels up from\n * src/registry/ or dist/registry/.\n */\nexport function getBundledRegistryPath(): string {\n\t// Preferred: registry copied next to the compiled loader at build time\n\t// (dist/registry/registry.json). This is the only path that resolves when\n\t// the package is installed from npm, since the repo-root registry/ dir is\n\t// not published.\n\tconst adjacentPath = join(__dirname, \"registry.json\");\n\tif (existsSync(adjacentPath)) return adjacentPath;\n\n\t// Monorepo dev/tests: loader runs from src/registry or dist/registry.\n\t// __dirname is:\n\t//   src:  packages/ai/src/registry    (4 levels up = repo root)\n\t//   dist: packages/ai/dist/registry   (4 levels up = repo root)\n\t// Structure: .../registry -> .../src|dist -> packages/ai -> packages -> repo-root\n\tconst repoRoot = join(__dirname, \"..\", \"..\", \"..\", \"..\");\n\tconst repoPath = join(repoRoot, \"registry\", \"registry.json\");\n\tif (existsSync(repoPath)) return repoPath;\n\n\t// Secondary fallback: probe 3 levels up (edge case for flattened dist)\n\tconst altRoot = join(__dirname, \"..\", \"..\", \"..\");\n\treturn join(altRoot, \"..\", \"registry\", \"registry.json\");\n}\n\n// ---------------------------------------------------------------------------\n// Load result type\n// ---------------------------------------------------------------------------\n\nexport type LoadRegistrySource = \"user-override\" | \"cache\" | \"bundled\";\n\nexport type LoadRegistryResult =\n\t| { ok: true; registry: Registry; source: LoadRegistrySource }\n\t| { ok: false; error: string; tried: string[] };\n\n// ---------------------------------------------------------------------------\n// Core loader\n// ---------------------------------------------------------------------------\n\n/**\n * Load the active registry following the three-tier fallback chain.\n *\n * @param configDir  Override ~/.cave/agent base dir (useful in tests).\n */\nexport function loadRegistry(configDir?: string): LoadRegistryResult {\n\tconst candidates: Array<{ source: LoadRegistrySource; path: string }> = [\n\t\t{ source: \"user-override\", path: getUserOverridePath(configDir) },\n\t\t{ source: \"cache\", path: getCachePath(configDir) },\n\t\t{ source: \"bundled\", path: getBundledRegistryPath() },\n\t];\n\n\tconst tried: string[] = [];\n\n\tfor (const { source, path } of candidates) {\n\t\tif (!existsSync(path)) {\n\t\t\ttried.push(`${source}: ${path} (not found)`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet raw: unknown;\n\t\ttry {\n\t\t\traw = JSON.parse(readFileSync(path, \"utf-8\"));\n\t\t} catch (err) {\n\t\t\ttried.push(`${source}: ${path} (parse error: ${err instanceof Error ? err.message : String(err)})`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst result = validateRegistry(raw);\n\t\tif (!result.ok) {\n\t\t\ttried.push(`${source}: ${path} (invalid: ${result.errors.join(\"; \")})`);\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn { ok: true, registry: result.registry, source };\n\t}\n\n\treturn {\n\t\tok: false,\n\t\terror: \"No valid registry found in any location\",\n\t\ttried,\n\t};\n}\n"]}