octocode-mcp
Version:
Model Context Protocol (MCP) server for advanced GitHub repository analysis, code discovery, and npm package exploration. Provides AI assistants with powerful tools to search, analyze, and understand codebases across GitHub and npm ecosystems.
3 lines (2 loc) • 6.17 kB
JavaScript
import t from"crypto";import{C as i}from"./index-DnOJNPC2.js";import"node:process";import"events";import"child_process";import"http";import"https";import"url";import"path";import"fs";import"util";import"stream";import"assert";import"tty";import"os";import"zlib";class e{static instance;config=null;tokenCache=new Map;installationCache=new Map;static getInstance(){return this.instance||(this.instance=new e),this.instance}initialize(t){const e=i.getConfig();if(!e.githubApp?.enabled)throw new Error("GitHub App not configured or disabled");this.config={appId:t?.appId||e.githubApp.appId,privateKey:t?.privateKey||e.githubApp.privateKey,installationId:t?.installationId||e.githubApp.installationId,baseUrl:t?.baseUrl||e.githubApp.baseUrl}}generateJWT(){if(!this.config)throw new Error("GitHub App not initialized");const i=Math.floor(Date.now()/1e3),e={iat:i-60,exp:i+600,iss:this.config.appId},n=`${Buffer.from(JSON.stringify({alg:"RS256",typ:"JWT"})).toString("base64url")}.${Buffer.from(JSON.stringify(e)).toString("base64url")}`;return`${n}.${t.sign("RSA-SHA256",Buffer.from(n),this.config.privateKey).toString("base64url")}`}async getInstallationToken(t){if(!this.config)throw new Error("GitHub App not initialized");const i=t||this.config.installationId;if(!i)throw new Error("Installation ID required");const e=this.tokenCache.get(i);if(e&&e.expiresAt>new Date(Date.now()+6e4))return e;try{const t=this.generateJWT(),e=this.getBaseUrl(),n=await fetch(`${e}/app/installations/${i}/access_tokens`,{method:"POST",headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github.v3+json","User-Agent":this.getUserAgent()}});if(!n.ok){const t=await n.text();throw new Error(`Failed to get installation token: ${n.status} ${n.statusText} - ${t}`)}const s=await n.json(),a={token:s.token,expiresAt:new Date(s.expires_at),permissions:s.permissions||{},repositorySelection:s.repository_selection||"all",repositories:s.repositories||[]};return this.tokenCache.set(i,a),setTimeout(()=>{this.tokenCache.delete(i)},a.expiresAt.getTime()-Date.now()-6e4),await this.logAppEvent("installation_token_retrieved","success",{installationId:i,expiresAt:a.expiresAt.toISOString(),permissions:Object.keys(a.permissions)}),a}catch(t){throw await this.logAppEvent("installation_token_retrieval","failure",{installationId:i,error:t instanceof Error?t.message:String(t)}),t}}async listInstallations(){if(!this.config)throw new Error("GitHub App not initialized");try{const t=this.generateJWT(),i=this.getBaseUrl(),e=await fetch(`${i}/app/installations`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github.v3+json","User-Agent":this.getUserAgent()}});if(!e.ok){const t=await e.text();throw new Error(`Failed to list installations: ${e.status} ${e.statusText} - ${t}`)}const n=await e.json();return n.forEach(t=>{this.installationCache.set(t.id,t)}),n}catch(t){throw await this.logAppEvent("list_installations","failure",{error:t instanceof Error?t.message:String(t)}),t}}async getInstallation(t){if(!this.config)throw new Error("GitHub App not initialized");const i=this.installationCache.get(t);if(i)return i;try{const i=this.generateJWT(),e=this.getBaseUrl(),n=await fetch(`${e}/app/installations/${t}`,{headers:{Authorization:`Bearer ${i}`,Accept:"application/vnd.github.v3+json","User-Agent":this.getUserAgent()}});if(!n.ok){const t=await n.text();throw new Error(`Failed to get installation: ${n.status} ${n.statusText} - ${t}`)}const s=await n.json();return this.installationCache.set(t,s),s}catch(i){throw await this.logAppEvent("get_installation","failure",{installationId:t,error:i instanceof Error?i.message:String(i)}),i}}async validateInstallationPermissions(t,i){try{const e=await this.getInstallationToken(t);for(const t of i){const i=e.permissions[t];if(!i||"none"===i)return!1}return!0}catch(e){return await this.logAppEvent("permission_validation","failure",{installationId:t,requiredPermissions:i,error:e instanceof Error?e.message:String(e)}),!1}}async validateRepositoryAccess(t,i,e){try{const n=await this.getInstallationToken(t);if("all"===n.repositorySelection)return!0;if(n.repositories)return n.repositories.some(t=>t.owner.login===i&&t.name===e);const s=this.getBaseUrl(),a=await fetch(`${s}/installation/repositories`,{headers:{Authorization:`token ${n.token}`,Accept:"application/vnd.github.v3+json","User-Agent":this.getUserAgent()}});return!!a.ok&&(await a.json()).repositories.some(t=>t.owner.login===i&&t.name===e)}catch(n){return await this.logAppEvent("repository_access_validation","failure",{installationId:t,owner:i,repo:e,error:n instanceof Error?n.message:String(n)}),!1}}async getInstallationUser(t){const i=await this.getInstallationToken(t),e=this.getBaseUrl(),n=await fetch(`${e}/user`,{headers:{Authorization:`token ${i.token}`,Accept:"application/vnd.github.v3+json","User-Agent":this.getUserAgent()}});if(!n.ok)throw new Error(`Failed to get installation user: ${n.status} ${n.statusText}`);return await n.json()}async getAppInfo(){if(!this.config)throw new Error("GitHub App not initialized");try{const t=this.generateJWT(),i=this.getBaseUrl(),e=await fetch(`${i}/app`,{headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github.v3+json","User-Agent":this.getUserAgent()}});if(!e.ok){const t=await e.text();throw new Error(`Failed to get app info: ${e.status} ${e.statusText} - ${t}`)}return await e.json()}catch(t){throw await this.logAppEvent("get_app_info","failure",{error:t instanceof Error?t.message:String(t)}),t}}clearTokenCache(){this.tokenCache.clear()}clearInstallationCache(){this.installationCache.clear()}getConfig(){return this.config?{appId:this.config.appId,installationId:this.config.installationId,baseUrl:this.config.baseUrl}:null}getBaseUrl(){return this.config?.baseUrl||"https://api.github.com"}getUserAgent(){return`octocode-mcp/${i.getConfig().version} (GitHub App)`}async logAppEvent(t,e,n){try{const s=i.getConfig();if(s.enterprise?.auditLogging){const{AuditLogger:i}=await import("./security/auditLogger.js");i.logEvent({action:`github_app_${t}`,outcome:e,source:"auth",details:{...n,appId:this.config?.appId}})}}catch{}}}export{e as GitHubAppManager,e as default};