grafana-mcp-analyzer
Version:
让AI助手直接分析你的Grafana监控数据 - A Model Context Protocol server for Grafana data analysis
2 lines (1 loc) • 7.67 kB
JavaScript
import o from"fs/promises";import s from"path";import S from"os";import{createStrictChunker as D,validateChunks as $,generateChunkingGuidance as p,getMaxChunkSize as R}from"./chunk-manager.js";const h=process.env.DATA_STORE_ROOT||s.join(S.homedir(),".grafana-mcp-analyzer","data-store");async function w(t){try{await o.access(t)}catch{await o.mkdir(t,{recursive:!0})}}function A(){return`request-${Date.now()}-${Math.random().toString(36).substr(2,9)}`}async function C(t,n){const e=s.join(h,t);await w(e);const a=s.join(e,"metadata.json");await o.writeFile(a,JSON.stringify({id:t,...n},null,2))}async function y(t){const n=s.join(h,t,"metadata.json");try{const e=await o.readFile(n,"utf-8");return JSON.parse(e)}catch{throw new Error(`Request metadata not found: ${t}`)}}async function P(t,n){const e=s.join(h,t),a=s.join(e,"data");await w(a);const c=D().chunk(n),{chunks:r,metadata:l}=c;if(!$(r))throw new Error("\u5206\u5757\u9A8C\u8BC1\u5931\u8D25\uFF1A\u5B58\u5728\u8D85\u8FC7\u5927\u5C0F\u9650\u5236\u7684\u5206\u5757");if(r.length===1){const f=s.join(a,"full.json");return await o.writeFile(f,JSON.stringify(n,null,2)),{type:"full",size:l.totalSize,chunks:1,resourceUri:`monitoring-data://${t}/data`,chunkingInfo:{dataType:l.dataType,chunkCount:1,guidance:p(r)}}}else{for(let u=0;u<r.length;u++){const d=r[u],m=s.join(a,`chunk-${u+1}.json`);await o.writeFile(m,JSON.stringify(d,null,2))}const f=s.join(a,"chunking-metadata.json");return await o.writeFile(f,JSON.stringify({totalChunks:r.length,metadata:l,guidance:p(r),chunkFiles:r.map((u,d)=>`chunk-${d+1}.json`)},null,2)),{type:"chunked",chunks:r.length,size:l.totalSize,chunkingStrategy:`strict-${Math.round(R()/1024)}kb`,resourceUri:`monitoring-data://${t}/chunks`,chunkingInfo:{dataType:l.dataType,chunkCount:r.length,guidance:p(r)}}}}async function J(t,n){const e=s.join(h,t,"data");if(n){const a=s.join(e,`${n}.json`);try{const i=await o.readFile(a,"utf-8"),c=JSON.parse(i);return c.index&&c.content!==void 0?c:i}catch{throw new Error(`Data chunk not found: ${t}/${n}`)}}else{const a=s.join(e,"full.json");try{const i=await o.readFile(a,"utf-8");return JSON.parse(i)}catch{try{const c=s.join(e,"chunking-metadata.json"),r=await o.readFile(c,"utf-8");return{type:"chunked",metadata:JSON.parse(r),message:"\u6570\u636E\u5DF2\u5206\u5757\u5B58\u50A8\uFF0C\u8BF7\u4F7F\u7528chunk-1, chunk-2\u7B49\u53C2\u6570\u83B7\u53D6\u5177\u4F53\u5206\u5757"}}catch{try{const l=(await o.readdir(e)).filter(u=>u.startsWith("chunk-")&&u.endsWith(".json")).sort((u,d)=>{const m=parseInt(u.match(/chunk-(\d+)\.json$/)?.[1]||"0"),k=parseInt(d.match(/chunk-(\d+)\.json$/)?.[1]||"0");return m-k});if(l.length===0)throw new Error(`No data found for request: ${t}`);let f="";for(const u of l){const d=s.join(e,u),m=await o.readFile(d,"utf-8");f+=m}return JSON.parse(f)}catch{throw new Error(`Failed to get response data: ${t}`)}}}}}async function x(t){const n=s.join(h,t,"data");try{return(await o.readdir(n)).filter(a=>a.endsWith(".json"))}catch{return[]}}async function T(t,n){const e=s.join(h,t);await w(e);const a=s.join(e,"analysis.json");await o.writeFile(a,JSON.stringify(n,null,2))}async function v(t,n,e=15e3){await T(t,n);const a=async()=>Promise.race([F(t),new Promise((i,c)=>setTimeout(()=>c(new Error(`\u9A8C\u8BC1\u8D85\u65F6: ${e}ms\u5185\u65E0\u6CD5\u8BFB\u53D6\u5206\u6790\u6570\u636E`)),e))]);try{const i=await a();return console.log(`\u2705 \u5206\u6790\u6570\u636E\u5DF2\u6210\u529F\u5B58\u50A8\u5E76\u9A8C\u8BC1: ${t}`),i}catch(i){throw new Error(`\u5206\u6790\u6570\u636E\u5B58\u50A8\u9A8C\u8BC1\u5931\u8D25: ${i.message}`)}}async function F(t){const n=s.join(h,t,"analysis.json");try{const e=await o.readFile(n,"utf-8");return JSON.parse(e)}catch{throw new Error(`Analysis not found: ${t}`)}}async function O(){try{await w(h);const t=await o.readdir(h),n=[];for(const e of t)if(e.startsWith("request-"))try{const a=await y(e);n.push(a)}catch{}return n.sort((e,a)=>new Date(a.timestamp).getTime()-new Date(e.timestamp).getTime())}catch{return[]}}async function W(t){return(await O()).filter(e=>e.sessionId===t)}async function _(t){const n=s.join(h,t);try{return await o.rm(n,{recursive:!0}),!0}catch{return!1}}async function M(t){const n=s.join(h,t);try{return await o.access(n),!0}catch{return!1}}async function b(t){try{const n=await y(t),e=await x(t);let a=0,i="none",c=[];if(e.length>0){const l=s.join(h,t,"data");if(e.includes("full.json"))i="full",a=(await o.stat(s.join(l,"full.json"))).size,c=[`monitoring-data://${t}/data`];else{const f=e.filter(u=>u.startsWith("chunk-"));if(f.length>0){i="chunked";for(const u of f){const d=await o.stat(s.join(l,u));a+=d.size}c=f.map(u=>{const d=u.match(/chunk-(\d+)\.json$/)?.[1];return`monitoring-data://${t}/chunk-${d}`})}}}const r=await o.access(s.join(h,t,"analysis.json")).then(()=>!0).catch(()=>!1);return{requestId:t,timestamp:n.timestamp,prompt:n.prompt,sessionId:n.sessionId,dataType:i,dataFiles:e.length,totalSize:a,hasAnalysis:r,resourceUris:c}}catch{throw new Error(`Failed to get request stats: ${t}`)}}async function U(){try{const t=parseInt(process.env.DATA_EXPIRY_HOURS||"24",10);console.error(`\u23F0 \u6570\u636E\u6E05\u7406\u914D\u7F6E: ${t}\u5C0F\u65F6\u540E\u81EA\u52A8\u6E05\u7406`);const n=await g(!1,t);n>0&&console.error(`\u{1F5D1}\uFE0F \u670D\u52A1\u542F\u52A8\u6E05\u7406\u5B8C\u6210\uFF0C\u5220\u9664\u4E86 ${n} \u4E2A\u8FC7\u671F\u8BF7\u6C42`),setInterval(async()=>{try{const e=await g(!1,t);e>0&&console.error(`\u{1F5D1}\uFE0F \u5B9A\u65F6\u6E05\u7406\u5B8C\u6210\uFF0C\u5220\u9664\u4E86 ${e} \u4E2A\u8FC7\u671F\u8BF7\u6C42`)}catch(e){console.error("\u274C \u5B9A\u65F6\u6570\u636E\u6E05\u7406\u5931\u8D25:",e)}},60*60*1e3)}catch(t){console.error("\u274C \u6570\u636E\u6E05\u7406\u521D\u59CB\u5316\u5931\u8D25:",t)}}async function g(t=!1,n=24){try{await w(h);const e=await o.readdir(h);let a=0;const i=Date.now(),c=n*60*60*1e3;for(const r of e)if(r.startsWith("request-")){const l=s.join(h,r);try{if(t)await o.rm(l,{recursive:!0}),a++,console.log(`\u{1F5D1}\uFE0F \u5220\u9664\u8BF7\u6C42\u76EE\u5F55: ${r}`);else{const f=await y(r),u=new Date(f.timestamp).getTime();i-u>c&&(await o.rm(l,{recursive:!0}),a++,console.log(`\u{1F5D1}\uFE0F \u5220\u9664\u8FC7\u671F\u8BF7\u6C42: ${r} (${f.timestamp})`))}}catch(f){console.warn(`\u26A0\uFE0F \u6E05\u7406\u8BF7\u6C42 ${r} \u5931\u8D25:`,f)}}return a}catch(e){return console.error("\u274C \u6570\u636E\u6E05\u7406\u5931\u8D25:",e),0}}async function B(){try{await w(h);const t=await o.readdir(h);let n=0,e=0,a=null,i=null,c=1/0,r=0;for(const l of t)if(l.startsWith("request-")){n++;try{const f=s.join(h,l),u=await j(f);e+=u;const d=await y(l),m=new Date(d.timestamp).getTime();m<c&&(c=m,a=l),m>r&&(r=m,i=l)}catch{}}return{totalRequests:n,totalSize:e,totalSizeMB:(e/(1024*1024)).toFixed(2),oldestRequest:a?{id:a,timestamp:new Date(c).toISOString()}:null,newestRequest:i?{id:i,timestamp:new Date(r).toISOString()}:null,storageRoot:h}}catch(t){return console.error("\u274C \u83B7\u53D6\u6570\u636E\u5B58\u50A8\u7EDF\u8BA1\u5931\u8D25:",t),{totalRequests:0,totalSize:0,totalSizeMB:"0.00",oldestRequest:null,newestRequest:null,storageRoot:h}}}async function j(t){let n=0;try{const e=await o.readdir(t);for(const a of e){const i=s.join(t,a),c=await o.stat(i);c.isDirectory()?n+=await j(i):n+=c.size}}catch{}return n}export{g as cleanupExpiredData,_ as deleteRequest,A as generateRequestId,F as getAnalysis,B as getDataStoreStats,y as getRequestMetadata,b as getRequestStats,J as getResponseData,U as initializeDataCleanup,O as listAllRequests,x as listDataFiles,W as listRequestsBySession,M as requestExists,v as safeStoreAnalysis,T as storeAnalysis,C as storeRequestMetadata,P as storeResponseData};