{"version":3,"file":"marketplace.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/marketplace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin marketplaces — install plugins from a curated index hosted in a git repo\n * (or a local directory).\n *\n * A marketplace is a directory containing an index manifest in one of three formats:\n *  - Native:  `.agents-plugin/marketplace.json`  (preferred; the `.agents` surface)\n *  - Claude:  `.claude-plugin/marketplace.json`\n *  - Copilot: `.github/marketplace.json`  (Copilot-style git index)\n *\n * All three use the same shape: `{ name?, owner?, plugins: [{ name, source, description? }] }`.\n * A plugin `source` is either a path relative to the marketplace root (a plugin\n * directory inside the repo), a git URL, or an `npm:<spec>` reference.\n *\n * When more than one is present the native `.agents-plugin` format wins, then\n * Claude, then Copilot (no merge) — matching the \"`.agents/` first\" policy used\n * across hoocode's resource surfaces.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { PLUGIN_FORMATS } from \"./formats/index.js\";\nimport { normalizePlatformToken } from \"./formats/platform-targets.js\";\nimport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/**\n * Canonical platform token used by the optional `supportPlatform` field. The\n * `.github/marketplace.json` (Copilot-style) format is surfaced as `\"github\"` —\n * friendlier than the internal `format: \"copilot\"` and matching where the file\n * lives. Authored aliases (`copilot`, `gh`) normalize to `github`.\n *\n * Re-exported from the format registry so the platform vocabulary has a single\n * source of truth.\n */\nexport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/** Map an internal marketplace `format` to its public platform token. */\nfunction formatToPlatform(format: NormalizedMarketplace[\"format\"]): MarketplacePlatform {\n\treturn format === \"copilot\" ? \"github\" : format;\n}\n\n/** Normalize an authored `supportPlatform` (string | string[]) to canonical tokens. */\nexport function normalizePlatforms(value: unknown): MarketplacePlatform[] {\n\tconst raw = Array.isArray(value) ? value : value == null ? [] : [value];\n\tconst out: MarketplacePlatform[] = [];\n\tfor (const v of raw) {\n\t\tif (typeof v !== \"string\") continue;\n\t\tconst canonical = normalizePlatformToken(v);\n\t\tif (canonical && !out.includes(canonical)) out.push(canonical);\n\t}\n\treturn out;\n}\n\n/** Structured source pointing at an entire git repository. */\ninterface MarketplacePluginSourceUrl {\n\tsource: \"url\";\n\turl: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Structured source pointing at a subdirectory within a git repository. */\ninterface MarketplacePluginSourceGitSubdir {\n\tsource: \"git-subdir\";\n\turl: string;\n\t/** Subdirectory path inside the repository. */\n\tpath: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Structured source pointing at an npm package. */\ninterface MarketplacePluginSourceNpm {\n\tsource: \"npm\";\n\tpackage: string;\n\tversion?: string;\n\tregistry?: string;\n}\n\n/** Structured source pointing at a zip archive served over HTTPS. */\ninterface MarketplacePluginSourceArchive {\n\tsource: \"archive\";\n\turl: string;\n\tsha256?: string;\n}\n\n/**\n * Source value authored in a marketplace manifest.\n *\n * `npm` and `archive` are in the union despite not being installable here. They\n * are documented vendor source types, so an entry using one is a *valid* catalog\n * entry hoocode cannot fetch — which is a different thing from a malformed entry,\n * and has to read differently. Dropping them at parse time made the plugin vanish\n * from SearchPlugins entirely, so the failure mode was \"that plugin does not\n * exist\" rather than \"hoocode cannot install that kind yet\".\n */\nexport type MarketplacePluginSource =\n\t| string\n\t| MarketplacePluginSourceUrl\n\t| MarketplacePluginSourceGitSubdir\n\t| MarketplacePluginSourceNpm\n\t| MarketplacePluginSourceArchive;\n\ninterface MarketplacePluginEntry {\n\tname: string;\n\t/** Relative path, git URL, `npm:<spec>`, or structured source object. */\n\tsource: MarketplacePluginSource;\n\tdescription?: string;\n\t/**\n\t * Optional platform(s) this entry targets. Only set when authored on the\n\t * entry; absent means \"no per-entry restriction\" (the marketplace's\n\t * platforms apply). Purely informational today — nothing filters on it.\n\t */\n\tsupportPlatform?: MarketplacePlatform[];\n}\n\nexport interface NormalizedMarketplace {\n\tname: string;\n\t/** Precedence winner when several index files conflict (agents > claude > copilot). */\n\tformat: \"agents\" | \"claude\" | \"copilot\";\n\t/**\n\t * Every platform this marketplace directory offers, so a conflict between\n\t * multiple index files (e.g. both `.github/` and `.claude-plugin/`) is\n\t * recorded rather than hidden. Always includes the resolved `format`'s\n\t * platform; also folds in any authored top-level `supportPlatform`. Never\n\t * empty.\n\t */\n\tsupportPlatform: MarketplacePlatform[];\n\t/** Absolute path to the marketplace directory. */\n\troot: string;\n\tmanifestPath: string;\n\tplugins: MarketplacePluginEntry[];\n}\n\ninterface RawMarketplace {\n\tname?: string;\n\t/** Both Claude Code and Copilot CLI define `owner` as `{ name, email? }`; a bare string is tolerated. */\n\towner?: string | { name?: string; email?: string };\n\t/** Shared optional metadata: `pluginRoot` is a base dir prepended to relative plugin source paths. */\n\tmetadata?: { description?: string; version?: string; pluginRoot?: string };\n\t/** Optional authored platform hint (string | string[]); folded into supportPlatform. */\n\tsupportPlatform?: unknown;\n\tplugins?: Array<{ name?: string; source?: unknown; description?: string; supportPlatform?: unknown }>;\n}\n\n/** True when a string source is a path (not npm:/git/URL) that `pluginRoot` should prefix. */\nfunction isRelativePathSource(source: string): boolean {\n\tconst s = source.trim();\n\treturn (\n\t\t!s.startsWith(\"npm:\") &&\n\t\t!/^https?:\\/\\//.test(s) &&\n\t\t!s.startsWith(\"git@\") &&\n\t\t!s.endsWith(\".git\") &&\n\t\t!path.isAbsolute(s)\n\t);\n}\n\n/**\n * Apply `metadata.pluginRoot` (Claude Code and Copilot CLI share the field):\n * a base directory prepended to relative plugin source paths, so\n * `\"source\": \"formatter\"` with `\"pluginRoot\": \"./plugins\"` resolves like\n * `\"./plugins/formatter\"`.\n */\nfunction applyPluginRoot(source: MarketplacePluginSource, pluginRoot: string | undefined): MarketplacePluginSource {\n\tif (!pluginRoot || typeof source !== \"string\" || !isRelativePathSource(source)) return source;\n\treturn `${pluginRoot.replace(/\\/+$/, \"\")}/${source.trim().replace(/^\\.\\//, \"\")}`;\n}\n\n/** Validate and normalize an authored source value. Returns null for unsupported/invalid shapes. */\nfunction normalizeSource(source: unknown): MarketplacePluginSource | null {\n\tif (typeof source === \"string\") return source;\n\tif (!source || typeof source !== \"object\") return null;\n\tconst obj = source as Record<string, unknown>;\n\tconst src = obj.source;\n\tconst url = obj.url;\n\t// `commit` is not in the documented schema but appears in the wild alongside\n\t// `sha` (anthropics/claude-plugins-official ships two such entries). It names\n\t// the same thing, so it stands in as the pin when `sha` is absent rather than\n\t// silently leaving the entry unpinned.\n\tconst shaValue = typeof obj.sha === \"string\" ? obj.sha : typeof obj.commit === \"string\" ? obj.commit : undefined;\n\tconst pin = {\n\t\t...(typeof obj.ref === \"string\" ? { ref: obj.ref } : {}),\n\t\t...(shaValue ? { sha: shaValue } : {}),\n\t};\n\tconst subdir = typeof obj.path === \"string\" && obj.path.length > 0 ? obj.path : undefined;\n\tif (src === \"url\" && typeof url === \"string\") {\n\t\t// A `url` entry carrying `path` means the plugin is a subdirectory of that\n\t\t// repo, exactly like `git-subdir`. Dropping the `path` used to install the\n\t\t// whole repository instead — six entries in the official Claude marketplace\n\t\t// resolve that way, and each produced a plugin directory with none of the\n\t\t// plugin in it.\n\t\tif (subdir) return { source: \"git-subdir\", url, path: subdir, ...pin };\n\t\treturn { source: \"url\", url, ...pin };\n\t}\n\tif (src === \"git-subdir\" && typeof url === \"string\" && subdir) {\n\t\treturn { source: \"git-subdir\", url, path: subdir, ...pin };\n\t}\n\t// Copilot marketplace shorthand: { source: \"github\", repo: \"owner/name\", path? }\n\t// (used by github/copilot-plugins and github/awesome-copilot). Normalize to the\n\t// equivalent git source.\n\tif (src === \"github\" && typeof obj.repo === \"string\" && /^[\\w.-]+\\/[\\w.-]+$/.test(obj.repo)) {\n\t\tconst repoUrl = `https://github.com/${obj.repo}.git`;\n\t\tif (subdir) return { source: \"git-subdir\", url: repoUrl, path: subdir, ...pin };\n\t\treturn { source: \"url\", url: repoUrl, ...pin };\n\t}\n\t// Documented types hoocode cannot fetch. Kept so the entry stays visible and\n\t// install can explain itself; see the MarketplacePluginSource docstring.\n\tif (src === \"npm\" && typeof obj.package === \"string\") {\n\t\treturn {\n\t\t\tsource: \"npm\",\n\t\t\tpackage: obj.package,\n\t\t\t...(typeof obj.version === \"string\" ? { version: obj.version } : {}),\n\t\t\t...(typeof obj.registry === \"string\" ? { registry: obj.registry } : {}),\n\t\t};\n\t}\n\tif (src === \"archive\" && typeof url === \"string\") {\n\t\treturn { source: \"archive\", url, ...(typeof obj.sha256 === \"string\" ? { sha256: obj.sha256 } : {}) };\n\t}\n\t// Anything else is malformed rather than merely unsupported, and stays dropped.\n\treturn null;\n}\n\n/** A resolved plugin source. The last two are recognized but not fetchable here. */\nexport type ResolvedPluginSource =\n\t| { kind: \"local\"; path: string }\n\t| { kind: \"git\"; url: string; ref?: string; sha?: string }\n\t| { kind: \"git-subdir\"; url: string; path: string; ref?: string; sha?: string }\n\t| { kind: \"npm\"; spec: string }\n\t| { kind: \"archive\"; url: string; sha256?: string };\n\nfunction readJson<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Parse a marketplace directory. Native `.agents-plugin` wins, then Claude, then\n * Copilot when more than one file is present. Returns null if no manifest exists\n * or it is invalid.\n */\nexport function parseMarketplaceDir(dir: string): NormalizedMarketplace | null {\n\t// Record every index format present so a conflict (more than one file) is\n\t// visible via supportPlatform, not silently dropped. PLUGIN_FORMATS is\n\t// precedence-ordered (native > Claude > Copilot), so `present` inherits it.\n\tconst present: Array<{ format: NormalizedMarketplace[\"format\"]; path: string }> = [];\n\tfor (const fmt of PLUGIN_FORMATS) {\n\t\tfor (const candidate of fmt.marketplaceFiles) {\n\t\t\tconst p = path.join(dir, candidate);\n\t\t\tif (fs.existsSync(p)) {\n\t\t\t\tpresent.push({ format: fmt.id, path: p });\n\t\t\t\tbreak; // first candidate wins within a format\n\t\t\t}\n\t\t}\n\t}\n\tif (present.length === 0) return null;\n\n\t// Precedence winner (present is built in agents > claude > copilot order).\n\tconst { format, path: manifestPath } = present[0];\n\n\tconst raw = readJson<RawMarketplace>(manifestPath);\n\tif (!raw) return null;\n\n\tconst pluginRoot =\n\t\ttypeof raw.metadata?.pluginRoot === \"string\" && raw.metadata.pluginRoot.trim()\n\t\t\t? raw.metadata.pluginRoot.trim()\n\t\t\t: undefined;\n\n\tconst plugins: MarketplacePluginEntry[] = [];\n\tfor (const entry of raw.plugins ?? []) {\n\t\tif (!entry?.name || !entry.source) continue;\n\t\tconst normalized = normalizeSource(entry.source);\n\t\tif (!normalized) continue;\n\t\tconst entryPlatforms = normalizePlatforms(entry.supportPlatform);\n\t\tplugins.push({\n\t\t\tname: entry.name,\n\t\t\tsource: applyPluginRoot(normalized, pluginRoot),\n\t\t\tdescription: entry.description,\n\t\t\t...(entryPlatforms.length > 0 ? { supportPlatform: entryPlatforms } : {}),\n\t\t});\n\t}\n\n\t// supportPlatform = platforms of all present index files, plus any authored\n\t// top-level hint, deduped. Always non-empty (at least the resolved format).\n\tconst supportPlatform: MarketplacePlatform[] = [];\n\tfor (const p of [...present.map((e) => formatToPlatform(e.format)), ...normalizePlatforms(raw.supportPlatform)]) {\n\t\tif (!supportPlatform.includes(p)) supportPlatform.push(p);\n\t}\n\n\treturn {\n\t\tname: (raw.name ?? path.basename(dir)).trim() || path.basename(dir),\n\t\tformat,\n\t\tsupportPlatform,\n\t\troot: dir,\n\t\tmanifestPath,\n\t\tplugins,\n\t};\n}\n\n/** Classify and resolve a plugin `source` against its marketplace root. */\nexport function resolvePluginSource(source: MarketplacePluginSource, marketplaceRoot: string): ResolvedPluginSource {\n\tif (typeof source === \"string\") {\n\t\tconst s = source.trim();\n\t\tif (s.startsWith(\"npm:\")) return { kind: \"npm\", spec: s.slice(4) };\n\t\tif (/^https?:\\/\\//.test(s) || s.startsWith(\"git@\") || s.endsWith(\".git\")) return { kind: \"git\", url: s };\n\t\t// Otherwise a path relative to (or absolute within) the marketplace repo.\n\t\treturn { kind: \"local\", path: path.isAbsolute(s) ? s : path.resolve(marketplaceRoot, s) };\n\t}\n\tif (source.source === \"url\") {\n\t\treturn { kind: \"git\", url: source.url, ref: source.ref, sha: source.sha };\n\t}\n\tif (source.source === \"npm\") {\n\t\treturn { kind: \"npm\", spec: source.version ? `${source.package}@${source.version}` : source.package };\n\t}\n\tif (source.source === \"archive\") {\n\t\treturn { kind: \"archive\", url: source.url, sha256: source.sha256 };\n\t}\n\treturn { kind: \"git-subdir\", url: source.url, path: source.path, ref: source.ref, sha: source.sha };\n}\n\n// ============================================================================\n// Marketplace registry (persisted list of added marketplaces)\n// ============================================================================\n\nexport interface MarketplaceRecord {\n\t/** Original location the user added (git URL or local path). */\n\tlocation: string;\n\t/** Local directory to read the manifest from (clone dir for git, else location). */\n\tdir: string;\n}\n\ninterface MarketplaceStoreFile {\n\tmarketplaces?: MarketplaceRecord[];\n}\n\n/** Read the persisted marketplace list from `<storePath>`. */\nexport function readMarketplaceStore(storePath: string): MarketplaceRecord[] {\n\tconst parsed = readJson<MarketplaceStoreFile>(storePath);\n\treturn Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : [];\n}\n\n/** Write the marketplace list to `<storePath>`. */\nexport function writeMarketplaceStore(storePath: string, records: MarketplaceRecord[]): void {\n\tfs.mkdirSync(path.dirname(storePath), { recursive: true });\n\tfs.writeFileSync(storePath, `${JSON.stringify({ marketplaces: records }, null, 2)}\\n`, \"utf8\");\n}\n"]}