@cybertheory/mcpkit
Advanced tools
Sorry, the diff of this file is too big to display
| *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-4{bottom:1rem}.left-0{left:0}.left-3{left:.75rem}.left-4{left:1rem}.right-0{right:0}.top-0{top:0}.top-2\.5{top:.625rem}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.h-12{height:3rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[90vh\]{max-height:90vh}.min-h-screen{min-height:100vh}.w-12{width:3rem}.w-5{width:1.25rem}.w-8{width:2rem}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[260px_1fr\]{grid-template-columns:260px 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-amber-400\/20{border-color:#fbbf2433}.border-blue-400{--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.border-blue-400\/20{border-color:#60a5fa33}.border-green-400\/20{border-color:#4ade8033}.border-indigo-400{--tw-border-opacity: 1;border-color:rgb(129 140 248 / var(--tw-border-opacity, 1))}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.border-slate-400{--tw-border-opacity: 1;border-color:rgb(148 163 184 / var(--tw-border-opacity, 1))}.border-slate-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity, 1))}.border-slate-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity, 1))}.bg-amber-400\/10{background-color:#fbbf241a}.bg-black\/50{background-color:#00000080}.bg-blue-400\/10{background-color:#60a5fa1a}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-green-400\/10{background-color:#4ade801a}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-indigo-500{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity, 1))}.bg-indigo-950\/40{background-color:#1e1b4b66}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-slate-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-600{--tw-bg-opacity: 1;background-color:rgb(202 138 4 / var(--tw-bg-opacity, 1))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-slate-800{--tw-gradient-from: #1e293b var(--tw-gradient-from-position);--tw-gradient-to: rgb(30 41 59 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-slate-950\/80{--tw-gradient-from: rgb(2 6 23 / .8) var(--tw-gradient-from-position);--tw-gradient-to: rgb(2 6 23 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-slate-700{--tw-gradient-to: #334155 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to: transparent var(--tw-gradient-to-position)}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pl-9{padding-left:2.25rem}.pr-3{padding-right:.75rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.tracking-wider{letter-spacing:.05em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-indigo-400{--tw-text-opacity: 1;color:rgb(129 140 248 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-950{--tw-text-opacity: 1;color:rgb(2 6 23 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.placeholder-slate-500::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(100 116 139 / var(--tw-placeholder-opacity, 1))}.placeholder-slate-500::placeholder{--tw-placeholder-opacity: 1;color:rgb(100 116 139 / var(--tw-placeholder-opacity, 1))}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}:root{color-scheme:dark}body{margin:0}.news-ticker{margin-bottom:.5rem;overflow:hidden;border-radius:.5rem;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));padding:.5rem;position:relative}.news-ticker-content{display:flex;align-items:center;animation:scroll-left 30s linear infinite;white-space:nowrap}.news-ticker-content:hover{animation-play-state:paused}.news-item{margin-right:2rem;flex-shrink:0;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.news-item:hover{--tw-text-opacity: 1;color:rgb(254 240 138 / var(--tw-text-opacity, 1))}.news-item{text-decoration:none;font-size:14px}.news-item:hover{text-decoration:underline}@keyframes scroll-left{0%{transform:translate(100%)}to{transform:translate(-100%)}}@media (max-width: 768px){.news-ticker-content{animation-duration:20s}.news-item{font-size:13px;margin-right:6px}}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.hover\:bg-red-700:hover{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-700:hover{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-800:hover{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.hover\:bg-yellow-700:hover{--tw-bg-opacity: 1;background-color:rgb(161 98 7 / var(--tw-bg-opacity, 1))}.hover\:text-blue-300:hover{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.focus\:border-transparent:focus{border-color:transparent}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width: 640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width: 1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}} |
+409
-19
@@ -8,7 +8,30 @@ #!/usr/bin/env node | ||
| const { PostHog } = require('posthog-node'); | ||
| const crypto = require('crypto'); | ||
| const POSTHOG_KEY = process.env.MCPKIT_POSTHOG_KEY || ''; | ||
| const POSTHOG_HOST = process.env.MCPKIT_POSTHOG_HOST || 'https://us.i.posthog.com'; | ||
| const POSTHOG_PUBLIC_KEY = process.env.MCPKIT_POSTHOG_PUBLIC_KEY || POSTHOG_KEY; // allow same key by default | ||
| const ph = POSTHOG_KEY ? new PostHog(POSTHOG_KEY, { host: process.env.MCPKIT_POSTHOG_HOST || 'https://us.i.posthog.com' }) : null; | ||
| const ph = POSTHOG_KEY ? new PostHog(POSTHOG_KEY, { host: POSTHOG_HOST }) : null; | ||
| // Stable anonymous ID persisted locally for anonymous telemetry | ||
| const ANON_ID_FILE = path.join(os.homedir(), '.mcpkit', 'anonymous_id'); | ||
| function getOrCreateAnonymousId() { | ||
| try { | ||
| const dir = path.dirname(ANON_ID_FILE); | ||
| if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); | ||
| if (fs.existsSync(ANON_ID_FILE)) { | ||
| const val = fs.readFileSync(ANON_ID_FILE, 'utf8').trim(); | ||
| if (val) return val; | ||
| } | ||
| const newId = 'anon_' + Math.random().toString(36).slice(2) + Date.now().toString(36); | ||
| fs.writeFileSync(ANON_ID_FILE, newId); | ||
| return newId; | ||
| } catch { | ||
| // Fallback to volatile ID if filesystem fails | ||
| return 'anon_' + Math.random().toString(36).slice(2); | ||
| } | ||
| } | ||
| const ANONYMOUS_ID = getOrCreateAnonymousId(); | ||
| // Persistence file for manually added agents | ||
@@ -20,2 +43,9 @@ const PERSISTENCE_FILE = path.join(os.homedir(), '.mcpkit', 'agents.json'); | ||
| // OAuth config (GitHub) | ||
| const GITHUB_CLIENT_ID = process.env.MCPKIT_GITHUB_CLIENT_ID || 'Ov23liPIjxP8XsjeUoik'; | ||
| const GITHUB_CLIENT_SECRET = process.env.MCPKIT_GITHUB_CLIENT_SECRET || ''; | ||
| const OAUTH_CALLBACK_PATH = '/auth/github/callback'; | ||
| const SESSION_SECRET = process.env.MCPKIT_SESSION_SECRET || 'dev_session_secret_change_me'; | ||
| const REGISTRY_BASE = 'https://registry.modelcontextprotocol.io'; | ||
| function ensurePersistenceDir() { | ||
@@ -50,4 +80,17 @@ const dir = path.dirname(PERSISTENCE_FILE); | ||
| async function loadMcpRegistry() { | ||
| // Try to fetch from GitHub first, but silently fall back to local if it fails | ||
| // PRIMARY: Try official MCP registry first (registry.modelcontextprotocol.io) | ||
| // This ensures all installations are based on official registry data | ||
| try { | ||
| const official = await fetchFromOfficialRegistry(); | ||
| if (official && Array.isArray(official.mcps)) { | ||
| saveMcpRegistry(official); | ||
| console.log(`Loaded ${official.mcps.length} MCPs from official registry`); | ||
| return official; | ||
| } | ||
| } catch (e) { | ||
| console.warn('Failed to fetch from official registry:', e.message); | ||
| } | ||
| // FALLBACK 1: Try GitHub repository as secondary source | ||
| try { | ||
| const githubRegistry = await fetchCatalogFromGitHub(); | ||
@@ -57,9 +100,10 @@ if (githubRegistry && githubRegistry.mcps) { | ||
| saveMcpRegistry(githubRegistry); | ||
| console.log(`Loaded ${githubRegistry.mcps.length} MCPs from GitHub fallback`); | ||
| return githubRegistry; | ||
| } | ||
| } catch (e) { | ||
| // Silently fall back to local registry - don't log errors to avoid noise | ||
| console.warn('Failed to fetch from GitHub fallback:', e.message); | ||
| } | ||
| // Fallback to local file | ||
| // FALLBACK 2: Use cached local registry file | ||
| try { | ||
@@ -70,2 +114,3 @@ if (fs.existsSync(MCP_REGISTRY_FILE)) { | ||
| if (registry && typeof registry === 'object') { | ||
| console.log(`Loaded ${registry.mcps?.length || 0} MCPs from local cache`); | ||
| return { | ||
@@ -79,2 +124,4 @@ mcps: Array.isArray(registry.mcps) ? registry.mcps : [] | ||
| } | ||
| console.warn('No MCP registry data available - returning empty registry'); | ||
| return { mcps: [] }; | ||
@@ -137,3 +184,3 @@ } | ||
| if (!ph) return; | ||
| try { ph.capture({ distinctId: os.userInfo().username || 'unknown', event, properties }); } catch {} | ||
| try { ph.capture({ distinctId: ANONYMOUS_ID, event, properties }); } catch {} | ||
| } | ||
@@ -577,2 +624,126 @@ | ||
| // Fetch from official MCP registry with pagination and transform to internal format | ||
| async function fetchFromOfficialRegistry() { | ||
| const BASE = 'https://registry.modelcontextprotocol.io'; | ||
| const perPage = 100; | ||
| let cursor = null; | ||
| let all = []; | ||
| while (true) { | ||
| const url = `${BASE}/v0/servers?limit=${perPage}` + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ''); | ||
| const res = await fetch(url); | ||
| if (!res.ok) break; | ||
| const data = await res.json(); | ||
| const items = Array.isArray(data.servers) ? data.servers : []; | ||
| if (!items.length) break; | ||
| all = all.concat(items); | ||
| const nextCursor = data.metadata && data.metadata.next_cursor; | ||
| if (!nextCursor) break; | ||
| cursor = nextCursor; | ||
| // safety cap to avoid infinite loop | ||
| if (all.length > 5000) break; | ||
| } | ||
| const mcps = all | ||
| .filter(s => (s.status || 'active') === 'active') | ||
| .map(transformOfficialServerToInternal) | ||
| .filter(Boolean); | ||
| return { mcps }; | ||
| } | ||
| function transformOfficialServerToInternal(server) { | ||
| try { | ||
| const officialMeta = server._meta && server._meta['io.modelcontextprotocol.registry/official']; | ||
| const id = (officialMeta && officialMeta.id) || server.id || server.uuid || server.name || ''; | ||
| const name = server.name || server.display_name || server.title || id; | ||
| const description = server.description || ''; | ||
| const version = server.version || 'latest'; | ||
| const documentation = server.documentation || ''; | ||
| const homepage = server.homepage || ''; | ||
| // Find the best package for installation - prefer npm packages | ||
| const npmPkg = (server.packages || []).find(p => p.registry_type === 'npm'); | ||
| const identifier = npmPkg?.identifier || null; | ||
| // Build installation command from registry data | ||
| let command = null; | ||
| if (identifier) { | ||
| // Use the package version from registry, fallback to 'latest' | ||
| const packageVersion = npmPkg?.version || version || 'latest'; | ||
| command = `npx ${identifier}@${packageVersion}`; | ||
| // Add transport-specific arguments if defined | ||
| if (npmPkg?.transport?.type === 'stdio') { | ||
| // stdio transport is the default, no additional args needed | ||
| } else if (npmPkg?.transport?.type === 'sse') { | ||
| // Add SSE-specific arguments if needed | ||
| command += ' --transport sse'; | ||
| } | ||
| } | ||
| // Extract environment variables from registry packages | ||
| const env = {}; | ||
| (server.packages || []).forEach(p => { | ||
| if (p.environment_variables) { | ||
| p.environment_variables.forEach(ev => { | ||
| env[ev.name] = { | ||
| required: !!ev.is_required, | ||
| description: ev.description || '', | ||
| placeholder: ev.placeholder || '', | ||
| help: ev.help || '', | ||
| secret: !!ev.is_secret | ||
| }; | ||
| }); | ||
| } | ||
| }); | ||
| // Extract setup instructions from registry metadata | ||
| const setup_instructions = []; | ||
| if (server.setup_instructions && Array.isArray(server.setup_instructions)) { | ||
| setup_instructions.push(...server.setup_instructions); | ||
| } | ||
| // Extract uninstall steps from registry metadata | ||
| const uninstall_steps = []; | ||
| if (server.uninstall_steps && Array.isArray(server.uninstall_steps)) { | ||
| uninstall_steps.push(...server.uninstall_steps); | ||
| } | ||
| const category = 'Community'; | ||
| return { | ||
| id: String(id), | ||
| name, | ||
| category, | ||
| description, | ||
| version, | ||
| npm: identifier, | ||
| command, | ||
| env, | ||
| setup_instructions, | ||
| uninstall_steps, | ||
| documentation, | ||
| homepage, | ||
| repository: server.repository && server.repository.url ? server.repository.url : null, | ||
| remotes: Array.isArray(server.remotes) ? server.remotes : [], | ||
| packages: Array.isArray(server.packages) ? server.packages : [], | ||
| registry_type: npmPkg?.registry_type || null, | ||
| registry_base_url: npmPkg?.registry_base_url || null, | ||
| license: server.license || null, | ||
| published_at: officialMeta && officialMeta.published_at ? officialMeta.published_at : null, | ||
| updated_at: officialMeta && officialMeta.updated_at ? officialMeta.updated_at : null, | ||
| installed: false, | ||
| installation_date: null, | ||
| installed_agents: [] | ||
| }; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| // Detailed transform retains env var metadata for display | ||
| function transformOfficialServerDetail(server) { | ||
| const base = transformOfficialServerToInternal(server) || {}; | ||
| // The base transform already handles environment variables from registry | ||
| // This function is kept for backward compatibility but now just returns the base | ||
| return base; | ||
| } | ||
| function ensureMcpServersInConfig(config, agentId) { | ||
@@ -656,3 +827,41 @@ // Different platforms use different config formats | ||
| app.use(express.json()); | ||
| // simple cookie parser | ||
| app.use((req, res, next) => { | ||
| const cookie = req.headers.cookie || ''; | ||
| const map = {}; | ||
| cookie.split(';').forEach(p => { const i = p.indexOf('='); if (i>0) { const k=p.slice(0,i).trim(); const v=decodeURIComponent(p.slice(i+1)); map[k]=v; } }); | ||
| req.cookies = map; | ||
| next(); | ||
| }); | ||
| // in-memory session store | ||
| const sessions = new Map(); | ||
| function sign(value) { | ||
| return crypto.createHmac('sha256', SESSION_SECRET).update(value).digest('hex'); | ||
| } | ||
| function setSession(res, data) { | ||
| const id = crypto.randomBytes(16).toString('hex'); | ||
| sessions.set(id, { ...data, createdAt: Date.now() }); | ||
| const sig = sign(id); | ||
| res.setHeader('Set-Cookie', `mcpkit_sid=${id}.${sig}; Path=/; HttpOnly; SameSite=Lax`); | ||
| } | ||
| function getSession(req) { | ||
| const raw = req.cookies['mcpkit_sid'] || ''; | ||
| const [id, sig] = raw.split('.'); | ||
| if (!id || !sig) return null; | ||
| if (sign(id) !== sig) return null; | ||
| const data = sessions.get(id); | ||
| return data || null; | ||
| } | ||
| // Public telemetry config for frontend initialization | ||
| app.get('/api/telemetry-config', (req, res) => { | ||
| // Only expose public key/host; do not expose server secret | ||
| res.json({ | ||
| enabled: !!POSTHOG_PUBLIC_KEY, | ||
| publicKey: POSTHOG_PUBLIC_KEY || null, | ||
| host: POSTHOG_HOST, | ||
| }); | ||
| }); | ||
| app.get('/api/agents', (req, res) => { | ||
@@ -664,2 +873,152 @@ const agents = detectAgents(); | ||
| // OAuth: start GitHub login | ||
| app.get('/auth/github/login', (req, res) => { | ||
| if (!GITHUB_CLIENT_ID) return res.status(500).send('GitHub OAuth not configured'); | ||
| const redirectUri = `${req.protocol}://${req.get('host')}${OAUTH_CALLBACK_PATH}`; | ||
| const state = crypto.randomBytes(8).toString('hex'); | ||
| const url = `https://github.com/login/oauth/authorize?client_id=${encodeURIComponent(GITHUB_CLIENT_ID)}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=read:user%20user:email&state=${state}`; | ||
| res.redirect(url); | ||
| }); | ||
| // GitHub Device Flow - start | ||
| app.post('/auth/github/device/start', async (req, res) => { | ||
| try { | ||
| if (!GITHUB_CLIENT_ID) return res.status(500).json({ error: 'github_not_configured' }); | ||
| const body = new URLSearchParams(); | ||
| body.set('client_id', GITHUB_CLIENT_ID); | ||
| body.set('scope', 'read:user user:email'); | ||
| const r = await fetch('https://github.com/login/device/code', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' }, | ||
| body | ||
| }); | ||
| const data = await r.json(); | ||
| if (!r.ok) return res.status(r.status).json(data); | ||
| // returns device_code, user_code, verification_uri, interval, expires_in | ||
| res.json(data); | ||
| } catch (e) { | ||
| res.status(500).json({ error: e.message }); | ||
| } | ||
| }); | ||
| // GitHub Device Flow - poll | ||
| app.post('/auth/github/device/poll', async (req, res) => { | ||
| try { | ||
| if (!GITHUB_CLIENT_ID) return res.status(500).json({ error: 'github_not_configured' }); | ||
| const { device_code } = req.body || {}; | ||
| if (!device_code) return res.status(400).json({ error: 'device_code_required' }); | ||
| const body = new URLSearchParams(); | ||
| body.set('client_id', GITHUB_CLIENT_ID); | ||
| body.set('device_code', device_code); | ||
| body.set('grant_type', 'urn:ietf:params:oauth:grant-type:device_code'); | ||
| const r = await fetch('https://github.com/login/oauth/access_token', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' }, | ||
| body | ||
| }); | ||
| const data = await r.json(); | ||
| // Possible responses: {access_token,...} or {error: 'authorization_pending'|'slow_down'|'expired_token'|'access_denied'} | ||
| if (data.access_token) { | ||
| const ghToken = data.access_token; | ||
| const userRes = await fetch('https://api.github.com/user', { headers: { Authorization: `Bearer ${ghToken}`, 'User-Agent': 'mcpkit' } }); | ||
| const user = await userRes.json(); | ||
| // create session | ||
| setSession(res, { provider: 'github', token: ghToken, user }); | ||
| return res.json({ authenticated: true, user }); | ||
| } | ||
| if (data.error === 'authorization_pending' || data.error === 'slow_down') { | ||
| return res.json({ authenticated: false, pending: true }); | ||
| } | ||
| return res.status(400).json({ authenticated: false, error: data.error || 'unknown_error' }); | ||
| } catch (e) { | ||
| res.status(500).json({ error: e.message }); | ||
| } | ||
| }); | ||
| // OAuth: GitHub callback | ||
| app.get(OAUTH_CALLBACK_PATH, async (req, res) => { | ||
| try { | ||
| const code = req.query.code; | ||
| if (!code) return res.status(400).send('Missing code'); | ||
| const redirectUri = `${req.protocol}://${req.get('host')}${OAUTH_CALLBACK_PATH}`; | ||
| const tokenRes = await fetch('https://github.com/login/oauth/access_token', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, | ||
| body: JSON.stringify({ client_id: GITHUB_CLIENT_ID, client_secret: GITHUB_CLIENT_SECRET, code, redirect_uri: redirectUri }) | ||
| }); | ||
| const tokenJson = await tokenRes.json(); | ||
| if (!tokenRes.ok || !tokenJson.access_token) return res.status(400).send('OAuth exchange failed'); | ||
| const ghToken = tokenJson.access_token; | ||
| // fetch user | ||
| const userRes = await fetch('https://api.github.com/user', { headers: { Authorization: `Bearer ${ghToken}`, 'User-Agent': 'mcpkit' } }); | ||
| const user = await userRes.json(); | ||
| setSession(res, { provider: 'github', token: ghToken, user }); | ||
| res.send('<script>window.opener && window.opener.postMessage({type:"oauth_complete"}, "*"); window.close();</script>'); | ||
| } catch (e) { | ||
| res.status(500).send('OAuth error'); | ||
| } | ||
| }); | ||
| // Current user info | ||
| app.get('/api/me', (req, res) => { | ||
| const s = getSession(req); | ||
| if (!s) return res.json({ authenticated: false }); | ||
| res.json({ authenticated: true, provider: s.provider, user: s.user }); | ||
| }); | ||
| // Publish server proxy to official registry | ||
| app.post('/api/publish-server', async (req, res) => { | ||
| try { | ||
| const s = getSession(req); | ||
| if (!s) return res.status(401).json({ error: 'unauthorized' }); | ||
| const serverJson = req.body; | ||
| // Forward to official registry publish endpoint | ||
| const r = await fetch(`${REGISTRY_BASE}/v0/servers`, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify(serverJson) | ||
| }); | ||
| const data = await r.json().catch(()=>({})); | ||
| if (!r.ok) return res.status(r.status).json({ error: 'registry_error', data }); | ||
| res.json({ ok: true, server: data }); | ||
| } catch (e) { | ||
| res.status(500).json({ error: e.message }); | ||
| } | ||
| }); | ||
| // Proxy list from official registry with cursor | ||
| app.get('/api/official/servers', async (req, res) => { | ||
| try { | ||
| const limit = Math.min(Number(req.query.limit) || 50, 100); | ||
| const cursor = req.query.cursor ? String(req.query.cursor) : null; | ||
| const BASE = 'https://registry.modelcontextprotocol.io'; | ||
| const url = `${BASE}/v0/servers?limit=${limit}` + (cursor ? `&cursor=${encodeURIComponent(cursor)}` : ''); | ||
| const r = await fetch(url); | ||
| if (!r.ok) return res.status(r.status).json({ error: 'registry_error' }); | ||
| const data = await r.json(); | ||
| const servers = (data.servers || []).filter(s => (s.status || 'active') === 'active'); | ||
| res.json({ | ||
| servers: servers.map(s => transformOfficialServerToInternal(s)), | ||
| next_cursor: data.metadata && data.metadata.next_cursor | ||
| }); | ||
| } catch (e) { | ||
| res.status(500).json({ error: e.message }); | ||
| } | ||
| }); | ||
| // Proxy detail fetch for a server by ID | ||
| app.get('/api/official/servers/:id', async (req, res) => { | ||
| try { | ||
| const id = req.params.id; | ||
| const BASE = 'https://registry.modelcontextprotocol.io'; | ||
| const url = `${BASE}/v0/servers/${encodeURIComponent(id)}`; | ||
| const r = await fetch(url); | ||
| if (!r.ok) return res.status(r.status).json({ error: 'registry_error' }); | ||
| const data = await r.json(); | ||
| res.json({ server: transformOfficialServerDetail(data) }); | ||
| } catch (e) { | ||
| res.status(500).json({ error: e.message }); | ||
| } | ||
| }); | ||
| // News API endpoint for tech headlines - using free proxy | ||
@@ -817,3 +1176,3 @@ app.get('/api/news', async (req, res) => { | ||
| // Get MCP from registry | ||
| // Get MCP from registry - ensure we're using fresh registry data | ||
| const registry = await loadMcpRegistry(); | ||
@@ -826,4 +1185,12 @@ const mcp = registry.mcps?.find(m => m.id === mcpId); | ||
| // Validate that we have a valid installation command from registry | ||
| if (!mcp.command) { | ||
| return res.status(400).json({ | ||
| error: 'No installation command available from registry', | ||
| details: 'This MCP server does not have a valid npm package or installation command defined in the registry' | ||
| }); | ||
| } | ||
| // Validate environment variables if required | ||
| if (mcp.env) { | ||
| if (mcp.env && Object.keys(mcp.env).length > 0) { | ||
| const missing = []; | ||
@@ -845,7 +1212,30 @@ Object.entries(mcp.env).forEach(([key, config]) => { | ||
| try { | ||
| // Use registry data directly for installation | ||
| addServerToMcpConfig(agent.mcpConfigPath, mcp.id, mcp.command, envVars || {}, agentId); | ||
| updateMcpInstallationStatus(mcpId, agentId, true); | ||
| track('mcp_installed', { agentId, mcpId, source: 'registry' }); | ||
| return res.json({ ok: true, mcpConfigPath: agent.mcpConfigPath }); | ||
| // Track installation with registry metadata | ||
| track('mcp_installed', { | ||
| agentId, | ||
| mcpId, | ||
| source: 'registry', | ||
| npm_package: mcp.npm, | ||
| version: mcp.version, | ||
| registry_type: mcp.registry_type | ||
| }); | ||
| return res.json({ | ||
| ok: true, | ||
| mcpConfigPath: agent.mcpConfigPath, | ||
| installed_from: 'registry', | ||
| npm_package: mcp.npm, | ||
| version: mcp.version | ||
| }); | ||
| } catch (e) { | ||
| track('mcp_install_failed', { | ||
| agentId, | ||
| mcpId, | ||
| error: e.message, | ||
| source: 'registry' | ||
| }); | ||
| return res.status(500).json({ error: e.message }); | ||
@@ -886,10 +1276,10 @@ } | ||
| try { | ||
| const githubRegistry = await fetchCatalogFromGitHub(); | ||
| if (githubRegistry && githubRegistry.mcps) { | ||
| saveMcpRegistry(githubRegistry); | ||
| track('registry_refreshed', { source: 'manual', count: githubRegistry.mcps.length }); | ||
| const official = await fetchFromOfficialRegistry(); | ||
| if (official && official.mcps) { | ||
| saveMcpRegistry(official); | ||
| track('registry_refreshed', { source: 'official', count: official.mcps.length }); | ||
| res.json({ | ||
| success: true, | ||
| message: 'Registry updated successfully', | ||
| count: githubRegistry.mcps.length | ||
| count: official.mcps.length | ||
| }); | ||
@@ -1146,7 +1536,7 @@ } else { | ||
| try { | ||
| const githubRegistry = await fetchCatalogFromGitHub(); | ||
| if (githubRegistry && githubRegistry.mcps) { | ||
| saveMcpRegistry(githubRegistry); | ||
| console.log(`Registry updated automatically - ${githubRegistry.mcps.length} MCPs available`); | ||
| track('registry_refreshed', { source: 'automatic', count: githubRegistry.mcps.length }); | ||
| const official = await fetchFromOfficialRegistry(); | ||
| if (official && official.mcps) { | ||
| saveMcpRegistry(official); | ||
| console.log(`Registry updated automatically - ${official.mcps.length} MCPs available`); | ||
| track('registry_refreshed', { source: 'automatic_official', count: official.mcps.length }); | ||
| } | ||
@@ -1153,0 +1543,0 @@ } catch (e) { |
+1
-1
| { | ||
| "name": "@cybertheory/mcpkit", | ||
| "version": "0.2.0", | ||
| "version": "0.3.0", | ||
| "description": "Lightweight UI console to detect MCP-capable coding agents and install MCP servers", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+12
-4
@@ -52,6 +52,7 @@ # MCP Kit | ||
| - **Auto-Detection**: Automatically finds Cursor, Windsurf, Claude Desktop, Continue, Aider, Cline, Neovim, Emacs, JetBrains IDEs and other MCP-capable agents | ||
| - **Live Registry**: Always up-to-date MCP server catalog from GitHub | ||
| - **One-Click Install**: Install MCP servers with environment variable configuration | ||
| - **Official Registry Integration**: Direct integration with the official MCP registry (registry.modelcontextprotocol.io) for authentic server data | ||
| - **One-Click Install**: Install MCP servers directly from registry with proper environment variable configuration | ||
| - **Registry-First**: All installations use official registry metadata including npm packages, versions, and environment variables | ||
| - **OAuth Support**: Handle authentication flows for services like GitHub | ||
| - **Offline Fallback**: Works even when GitHub is unavailable | ||
| - **Offline Fallback**: Works even when registry is unavailable using cached data | ||
| - **Cross-Platform**: Windows, macOS, and Linux support | ||
@@ -203,4 +204,11 @@ | ||
| The MCP registry is automatically updated from [GitHub](https://github.com/cybertheory/mcpkit) every 30 minutes. You can manually refresh using the "Refresh Registry" button in the UI. | ||
| MCP Kit uses the **official MCP registry** (registry.modelcontextprotocol.io) as the primary source for all server data. This ensures: | ||
| - ✅ **Authentic Data**: All server information comes directly from the official registry | ||
| - ✅ **Latest Versions**: Automatic updates with the most recent server versions | ||
| - ✅ **Proper Metadata**: Complete package information, environment variables, and installation commands | ||
| - ✅ **Verified Sources**: Only officially registered and verified MCP servers | ||
| The registry is automatically updated every 30 minutes, and you can manually refresh using the "Refresh Registry" button in the UI. If the official registry is unavailable, MCP Kit falls back to cached data to ensure continued operation. | ||
| ## 🚀 Publishing | ||
@@ -207,0 +215,0 @@ |
@@ -7,7 +7,7 @@ <!doctype html> | ||
| <title>MCP Kit</title> | ||
| <script type="module" crossorigin src="/assets/index-D103NwT9.js"></script> | ||
| <link rel="stylesheet" crossorigin href="/assets/index-Bvzz2C6A.css"> | ||
| <script type="module" crossorigin src="/assets/index-BLJIikI0.js"></script> | ||
| <link rel="stylesheet" crossorigin href="/assets/index-BqmkP88X.css"> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <div id="root"></div> | ||
| </body> | ||
@@ -14,0 +14,0 @@ </html> |
| *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.bottom-4{bottom:1rem}.left-0{left:0}.left-3{left:.75rem}.left-4{left:1rem}.right-0{right:0}.top-0{top:0}.top-2\.5{top:.625rem}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.h-12{height:3rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-full{height:100%}.max-h-\[90vh\]{max-height:90vh}.min-h-screen{min-height:100vh}.w-12{width:3rem}.w-5{width:1.25rem}.w-8{width:2rem}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.grid-cols-\[260px_1fr\]{grid-template-columns:260px 1fr}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-r{border-right-width:1px}.border-amber-400\/20{border-color:#fbbf2433}.border-blue-400{--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.border-blue-400\/20{border-color:#60a5fa33}.border-green-400\/20{border-color:#4ade8033}.border-indigo-400{--tw-border-opacity: 1;border-color:rgb(129 140 248 / var(--tw-border-opacity, 1))}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.border-slate-400{--tw-border-opacity: 1;border-color:rgb(148 163 184 / var(--tw-border-opacity, 1))}.border-slate-700{--tw-border-opacity: 1;border-color:rgb(51 65 85 / var(--tw-border-opacity, 1))}.border-slate-800{--tw-border-opacity: 1;border-color:rgb(30 41 59 / var(--tw-border-opacity, 1))}.bg-amber-400\/10{background-color:#fbbf241a}.bg-black\/50{background-color:#00000080}.bg-blue-400\/10{background-color:#60a5fa1a}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-green-400\/10{background-color:#4ade801a}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-indigo-500{--tw-bg-opacity: 1;background-color:rgb(99 102 241 / var(--tw-bg-opacity, 1))}.bg-indigo-950\/40{background-color:#1e1b4b66}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-slate-800{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.bg-slate-950{--tw-bg-opacity: 1;background-color:rgb(2 6 23 / var(--tw-bg-opacity, 1))}.bg-yellow-600{--tw-bg-opacity: 1;background-color:rgb(202 138 4 / var(--tw-bg-opacity, 1))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-slate-800{--tw-gradient-from: #1e293b var(--tw-gradient-from-position);--tw-gradient-to: rgb(30 41 59 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-slate-950\/80{--tw-gradient-from: rgb(2 6 23 / .8) var(--tw-gradient-from-position);--tw-gradient-to: rgb(2 6 23 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-slate-700{--tw-gradient-to: #334155 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to: transparent var(--tw-gradient-to-position)}.object-cover{-o-object-fit:cover;object-fit:cover}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pl-9{padding-left:2.25rem}.pr-3{padding-right:.75rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-indigo-400{--tw-text-opacity: 1;color:rgb(129 140 248 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-slate-100{--tw-text-opacity: 1;color:rgb(241 245 249 / var(--tw-text-opacity, 1))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-950{--tw-text-opacity: 1;color:rgb(2 6 23 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.placeholder-slate-500::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(100 116 139 / var(--tw-placeholder-opacity, 1))}.placeholder-slate-500::placeholder{--tw-placeholder-opacity: 1;color:rgb(100 116 139 / var(--tw-placeholder-opacity, 1))}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}:root{color-scheme:dark}body{margin:0}.news-ticker{margin-bottom:.5rem;overflow:hidden;border-radius:.5rem;--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1));padding:.5rem;position:relative}.news-ticker-content{display:flex;align-items:center;animation:scroll-left 30s linear infinite;white-space:nowrap}.news-ticker-content:hover{animation-play-state:paused}.news-item{margin-right:2rem;flex-shrink:0;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.news-item:hover{--tw-text-opacity: 1;color:rgb(254 240 138 / var(--tw-text-opacity, 1))}.news-item{text-decoration:none;font-size:14px}.news-item:hover{text-decoration:underline}@keyframes scroll-left{0%{transform:translate(100%)}to{transform:translate(-100%)}}@media (max-width: 768px){.news-ticker-content{animation-duration:20s}.news-item{font-size:13px;margin-right:6px}}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-green-700:hover{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.hover\:bg-red-700:hover{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-700:hover{--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-800:hover{--tw-bg-opacity: 1;background-color:rgb(30 41 59 / var(--tw-bg-opacity, 1))}.hover\:bg-yellow-700:hover{--tw-bg-opacity: 1;background-color:rgb(161 98 7 / var(--tw-bg-opacity, 1))}.hover\:text-blue-300:hover{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.focus\:border-transparent:focus{border-color:transparent}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}@media (min-width: 640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width: 1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1114090
316.83%23343
831.11%244
3.39%17
30.77%33
94.12%