MCPcopy
hub / github.com/codeaashu/claude-code / parseMarketplaceInput

Function parseMarketplaceInput

src/utils/plugins/parseMarketplaceInput.ts:23–162  ·  view source on GitHub ↗
(
  input: string,
)

Source from the content-addressed store, hash-verified

21 * @returns MarketplaceSource object, error object, or null if format is unrecognized
22 */
23export async function parseMarketplaceInput(
24 input: string,
25): Promise<MarketplaceSource | { error: string } | null> {
26 const trimmed = input.trim()
27 const fs = getFsImplementation()
28
29 // Handle git SSH URLs with any valid username (not just 'git')
30 // Supports: user@host:path, user@host:path.git, and with #ref suffix
31 // Username can contain: alphanumeric, dots, underscores, hyphens
32 const sshMatch = trimmed.match(
33 /^([a-zA-Z0-9._-]+@[^:]+:.+?(?:\.git)?)(#(.+))?$/,
34 )
35 if (sshMatch?.[1]) {
36 const url = sshMatch[1]
37 const ref = sshMatch[3]
38 return ref ? { source: 'git', url, ref } : { source: 'git', url }
39 }
40
41 // Handle URLs
42 if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
43 // Extract fragment (ref) from URL if present
44 const fragmentMatch = trimmed.match(/^([^#]+)(#(.+))?$/)
45 const urlWithoutFragment = fragmentMatch?.[1] || trimmed
46 const ref = fragmentMatch?.[3]
47
48 // When user explicitly provides an HTTPS/HTTP URL that looks like a git
49 // repo, use the git source type so we clone rather than fetch-as-JSON.
50 // The .git suffix is a GitHub/GitLab/Bitbucket convention. Azure DevOps
51 // uses /_git/ in the path with NO suffix (appending .git breaks ADO:
52 // TF401019 "repo does not exist"). Without this check, an ADO URL falls
53 // through to source:'url' below, which tries to fetch it as a raw
54 // marketplace.json — the HTML response parses as "expected object,
55 // received string". (gh-31256 / CC-299)
56 if (
57 urlWithoutFragment.endsWith('.git') ||
58 urlWithoutFragment.includes('/_git/')
59 ) {
60 return ref
61 ? { source: 'git', url: urlWithoutFragment, ref }
62 : { source: 'git', url: urlWithoutFragment }
63 }
64 // Parse URL to check hostname
65 let url: URL
66 try {
67 url = new URL(urlWithoutFragment)
68 } catch (_err) {
69 // Not a valid URL for parsing, treat as generic URL
70 // new URL() throws TypeError for invalid URLs
71 return { source: 'url', url: urlWithoutFragment }
72 }
73
74 if (url.hostname === 'github.com' || url.hostname === 'www.github.com') {
75 const match = url.pathname.match(/^\/([^/]+\/[^/]+?)(\/|\.git|$)/)
76 if (match?.[1]) {
77 // User explicitly provided HTTPS URL - keep it as HTTPS via 'git' type
78 // Add .git suffix if not present for proper git clone
79 const gitUrl = urlWithoutFragment.endsWith('.git')
80 ? urlWithoutFragment

Callers 2

handleAddFunction · 0.85
marketplaceAddHandlerFunction · 0.85

Calls 3

getFsImplementationFunction · 0.85
getErrnoCodeFunction · 0.85
resolveFunction · 0.50

Tested by

no test coverage detected