toolnet-memory 0.3.11 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +0 -4
- package/README.md +428 -202
- package/bin/toolnet-memory +15 -42
- package/bundle/agy-hook.js +47 -47
- package/bundle/agy.js +31 -31
- package/bundle/api.js +15 -52
- package/bundle/auto-integrate.js +34 -33
- package/bundle/claude-hook.js +9 -9
- package/bundle/codex.js +28 -28
- package/bundle/config.js +17 -12
- package/bundle/context-runtime.js +9 -9
- package/bundle/context.js +12 -12
- package/bundle/continuity-certify.js +13 -50
- package/bundle/copilot-hook.js +13 -13
- package/bundle/cursor-hook.js +13 -13
- package/bundle/doctor.js +4 -6
- package/bundle/full-index.js +21 -22
- package/bundle/graph-index.js +3 -3
- package/bundle/graph-ui.js +3 -3
- package/bundle/grok-hook.js +13 -13
- package/bundle/help-cli.js +4 -4
- package/bundle/impact.js +3 -3
- package/bundle/incremental.js +3 -3
- package/bundle/init.js +37 -36
- package/bundle/kilo.js +2 -0
- package/bundle/kiro-hook.js +14 -14
- package/bundle/learner.js +4 -4
- package/bundle/mcp.js +18 -56
- package/bundle/memory-agent.js +7 -47
- package/bundle/opencode.js +27 -27
- package/bundle/project-manual.js +2 -2
- package/bundle/recovery-certify.js +13 -53
- package/bundle/runtime.js +11 -11
- package/bundle/semantic.js +5 -6
- package/bundle/snapshot.js +3 -3
- package/bundle/status-cli.js +4 -4
- package/bundle/toolnet-cli.js +2 -0
- package/bundle/work-continuity.js +5 -5
- package/package.json +1 -3
- package/bundle/model-cli.js +0 -2
- package/bundle/provider.js +0 -6
- package/bundle/setup.js +0 -1159
package/bundle/graph-ui.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import"dotenv/config";import{createReadStream as ue}from"node:fs";import{createServer as ut}from"node:http";import{dirname as
|
|
2
|
-
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function
|
|
3
|
-
`,{encoding:"utf8",mode:384}),ve(a,s)}function se(n,t){return{id:n.id,name:n.name,remote:n.remote,rootPath:t,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}var z=class{detect(t=process.cwd()){let e=I(t),s=Me(e),c=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(v=>J(B(s,v))),p=Ae(e,c?s:void 0);if(p){let v=Y(p),j=Ne(v);return j.rootPath!==p&&(j.rootPath=p,j.updatedAt=new Date().toISOString(),re(p,j)),se(j,p)}let k=new Date().toISOString(),b=ke(s),E={version:1,id:Re(s),name:b,remote:b,rootPath:s,createdAt:k,updatedAt:k,graphVersion:0,memoryVersion:0};return re(s,E),se(E,s)}};import*as w from"typescript";var A=class{symbols=new Map;edges=new Map;addSymbol(t){this.symbols.set(t.id,t)}addEdge(t){this.edges.set(t.id,t)}getSymbol(t){return this.symbols.get(t)}findByName(t,e){return[...this.symbols.values()].filter(s=>s.projectId===t&&(s.name===e||s.qualifiedName===e))}allSymbols(t){return[...this.symbols.values()].filter(e=>e.projectId===t)}allEdges(t){return[...this.edges.values()].filter(e=>e.projectId===t)}import(t,e){for(let s of t)this.addSymbol(s);for(let s of e)this.addEdge(s)}clearProject(t){for(let[e,s]of this.symbols)s.projectId===t&&this.symbols.delete(e);for(let[e,s]of this.edges)s.projectId===t&&this.edges.delete(e)}};import{execFile as $e}from"node:child_process";import{promisify as Fe}from"node:util";var kr=Fe($e);import*as P from"typescript";import*as m from"typescript";var Gs=new Set([m.SyntaxKind.EqualsToken,m.SyntaxKind.PlusEqualsToken,m.SyntaxKind.MinusEqualsToken,m.SyntaxKind.AsteriskEqualsToken,m.SyntaxKind.SlashEqualsToken,m.SyntaxKind.PercentEqualsToken,m.SyntaxKind.QuestionQuestionEqualsToken,m.SyntaxKind.AmpersandAmpersandEqualsToken,m.SyntaxKind.BarBarEqualsToken]);var H=class{constructor(t){this.graph=t}graph;build(t,e,s,a){let c=this.graph.allSymbols(t),p=this.graph.allEdges(t),k=new Map(e?.layers.map(o=>[o.filePath,o.layer])??[]),b=new Map;for(let o of e?.clusters??[])for(let r of o.files)b.set(r,{id:o.id,label:o.label});let E=new Map(e?.hotspots.map(o=>[o.filePath,o.score])??[]),v=new Map(s?.deadCode.map(o=>[o.symbolId,{confidence:o.confidence,score:o.score}])??[]),j=new Map,M=new Map;for(let o of p)M.set(o.from,(M.get(o.from)??0)+1),j.set(o.to,(j.get(o.to)??0)+1);let R=[];for(let o=0;o<c.length;o++){let r=c[o],l=b.get(r.filePath);R.push({id:r.id,name:r.name,qualifiedName:r.qualifiedName,type:r.type,filePath:r.filePath,layer:k.get(r.filePath),cluster:l?.id,clusterLabel:l?.label,hotspotScore:E.get(r.filePath),deadCode:v.get(r.id),incoming:j.get(r.id)??0,outgoing:M.get(r.id)??0}),((o+1)%100===0||o===c.length-1)&&a?.({current:o+1,total:c.length,phase:"nodes"})}let F=new Set(R.map(o=>o.id)),T=p.filter(o=>F.has(o.from)&&F.has(o.to)),D=[];for(let o=0;o<T.length;o++){let r=T[o];D.push({source:r.from,target:r.to,type:r.type,confidence:typeof r.metadata?.confidence=="string"?r.metadata.confidence:void 0,resolver:typeof r.metadata?.resolver=="string"?r.metadata.resolver:void 0}),((o+1)%100===0||o===T.length-1)&&a?.({current:o+1,total:T.length,phase:"links"})}return{version:1,projectId:t,generatedAt:new Date().toISOString(),summary:{nodes:R.length,links:D.length,files:new Set(R.map(o=>o.filePath)).size,clusters:new Set(R.map(o=>o.cluster).filter(Boolean)).size},nodes:R,links:D}}};import{homedir as ct}from"node:os";import{join as lt}from"node:path";import{DeleteObjectCommand as Ge,GetObjectCommand as Ke,HeadObjectCommand as ze,ListObjectsV2Command as Be,PutObjectCommand as He,S3Client as Ve}from"@aws-sdk/client-s3";import{getSignedUrl as Ue}from"@aws-sdk/s3-request-presigner";var V=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Ve({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new He({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await Ue(this.client,new Ke({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new ze({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Ge({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new Be({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};import{access as ie,mkdir as We,readFile as Je,readdir as Ye,rm as qe,stat as ae,writeFile as Qe}from"node:fs/promises";import{dirname as Xe,join as Ze,relative as ce,resolve as et}from"node:path";var O=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return et(this.root,e)}async put(t,e){let s=this.path(t);await We(Xe(s),{recursive:!0}),await Qe(s,e)}async get(t){try{return await Je(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await ie(this.path(t)),!0}catch{return!1}}async delete(t){await qe(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),s=[];try{await ie(e)}catch{return s}let a=async p=>{let k=await Ye(p,{withFileTypes:!0});for(let b of k){let E=Ze(p,b.name);if(b.isDirectory()){await a(E);continue}let v=await ae(E);s.push({key:ce(this.root,E),size:v.size,updatedAt:v.mtime.toISOString()})}},c=await ae(e);return c.isDirectory()?await a(e):s.push({key:ce(this.root,e),size:c.size,updatedAt:c.mtime.toISOString()}),s}};import{DeleteObjectCommand as tt,GetObjectCommand as rt,HeadObjectCommand as st,ListObjectsV2Command as ot,PutObjectCommand as nt,S3Client as it}from"@aws-sdk/client-s3";import{getSignedUrl as at}from"@aws-sdk/s3-request-presigner";var _=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new it({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new nt({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await at(this.client,new rt({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new st({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new tt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new ot({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};function q(n,t){return console.warn(t),new O(n)}function le(n){let t=n.localRoot??lt(ct(),".toolnet-memory","storage");if(n.provider==="r2"){let e=n.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let e=n.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let e=n.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new V({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new O(t)}function dt(n){return new Promise(t=>setTimeout(t,n))}async function de(n,t={}){let e=Math.max(1,t.attempts??3),s=t.baseDelayMs??150,a=t.maxDelayMs??2e3,c;for(let p=1;p<=e;p++)try{return await n()}catch(k){if(c=k,p>=e)break;let b=Math.min(a,s*2**(p-1)),E=Math.floor(Math.random()*Math.max(1,b*.2));await dt(b+E)}throw c}var pt=new Set(["put","get","getText","delete","list"]);function pe(n,t={}){return new Proxy(n,{get(e,s){let a=Reflect.get(e,s,e);return typeof a!="function"?a:pt.has(s)?(...c)=>de(()=>Promise.resolve(a.apply(e,c)),t):a.bind(e)}})}var me=Number(process.env.TOOLNET_GRAPH_PORT??9749),fe=process.env.TOOLNET_GRAPH_HOST??"127.0.0.1";function gt(n){if(typeof n=="string")return n;if(!n||typeof n!="object")return null;let t=n;for(let e of["key","Key","path","name"])if(typeof t[e]=="string")return t[e];return null}async function ht(n,t){return(await n.list(t)).map(gt).filter(s=>!!s)}async function $(n,t){let e=await n.getText(t);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function yt(n){let t=await ht(n,"projects/"),e=[...new Set(t.filter(c=>/^projects\/[^/]+\/project\.json$/.test(c)).map(c=>c.split("/")[1]))],s=new Set(t),a=[];for(let c of e){let p=await $(n,`projects/${c}/project.json`);!p||typeof p.id!="string"||a.push({id:p.id,name:typeof p.name=="string"?p.name:c,remote:c,hasGraph:s.has(`projects/${c}/code/graph/current.json`),hasArchitecture:s.has(`projects/${c}/code/architecture/current.json`),hasAnalysis:s.has(`projects/${c}/code/analysis/current.json`),hasVisualization:s.has(`projects/${c}/code/visualization/graph.json`)})}return a.sort((c,p)=>c.name.localeCompare(p.name))}async function St(){let n=te(),t=new z().detect(),e=pe(le({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:3});e.name!=="huggingface"&&console.warn("[graph-ui] Remote Hugging Face storage is not active.");let s=mt(ft(import.meta.url)),a=Q(s,"public"),c=Q(a,"vendor","3d-force-graph.min.js");async function p(){let o=await yt(e),r=t.remote??t.name,l=o.find(f=>f.remote===r&&f.hasGraph),S=o.find(f=>f.hasGraph);return{projects:o,defaultProject:l?.remote??S?.remote??null}}let k=new Map;function b(o){return String(typeof o=="object"&&o!==null?o.id:o)}function E(o){return o.clusterLabel||o.cluster||o.layer||"Unclustered"}function v(o,r={}){return{id:o.id,name:o.name,remote:o.remote,hasArchitecture:o.hasArchitecture,hasAnalysis:o.hasAnalysis,...r}}async function j(o){let r=`projects/${o.remote}`,l=await $(e,`${r}/code/graph/current.json`);if(!l||!Array.isArray(l.symbols)||!Array.isArray(l.edges))throw new Error(`Code graph missing for project ${o.name}`);let S=await $(e,`${r}/code/architecture/current.json`),f=await $(e,`${r}/code/analysis/current.json`),y=new A;y.import(l.symbols,l.edges);let x=new H(y).build(o.id,S,f);return await e.put(`${r}/code/visualization/graph.json`,JSON.stringify(x),"application/json"),{...x,dashboard:v(o)}}async function M(o,r=!1){r&&k.delete(o.remote);let l=k.get(o.remote);if(l)return l;let S=`projects/${o.remote}`,f=await $(e,`${S}/code/visualization/graph.json`);if(f&&Array.isArray(f.nodes)&&Array.isArray(f.links)){let x={...f,dashboard:v(o)};return k.set(o.remote,x),x}let y=await j(o);return k.set(o.remote,y),y}function R(o,r){let l=new Map,S=new Map;for(let d of r.nodes){let u=E(d),g=`cluster:${u}`;S.set(d.id,g);let i=l.get(g);i||(i={id:g,name:u,qualifiedName:u,type:"subsystem",filePath:"",clusterLabel:u,incoming:0,outgoing:0,memberCount:0,files:new Set,viewKind:"cluster"},l.set(g,i)),i.memberCount+=1,d.filePath&&i.files.add(d.filePath)}let f=new Map;for(let d of r.links){let u=S.get(b(d.source)),g=S.get(b(d.target));if(!u||!g||u===g)continue;let i=`${u}\0${g}`,h=f.get(i);h||(h={source:u,target:g,type:"SUBSYSTEM_DEPENDENCY",count:0},f.set(i,h)),h.count+=1}for(let d of f.values()){let u=l.get(d.source),g=l.get(d.target);u&&(u.outgoing+=d.count),g&&(g.incoming+=d.count)}let y=[...l.values()].map(d=>({...d,filePath:`${d.files.size} files \xB7 ${d.memberCount} symbols`,files:void 0})),x=[...f.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:y.length,links:x.length,files:r.summary?.files??0,clusters:y.length},nodes:y,links:x,dashboard:v(o,{viewMode:"overview",totalNodes:r.nodes.length,totalLinks:r.links.length})}}function F(o,r,l){let S=r.nodes.filter(i=>!l||E(i)===l),f=new Set(S.map(i=>i.id)),y=new Map,x=new Map;for(let i of S){let h=i.filePath||"(unknown)";y.set(i.id,h);let C=x.get(h);C||(C={id:`file:${h}`,name:h.split("/").pop()||h,qualifiedName:h,type:"file",filePath:h,clusterLabel:E(i),incoming:0,outgoing:0,memberCount:0,viewKind:"file"},x.set(h,C)),C.memberCount+=1}let d=new Map;for(let i of r.links){let h=b(i.source),C=b(i.target);if(!f.has(h)||!f.has(C))continue;let U=y.get(h),W=y.get(C);if(!U||!W||U===W)continue;let X=`file:${U}`,Z=`file:${W}`,ee=`${X}\0${Z}`,G=d.get(ee);G||(G={source:X,target:Z,type:"FILE_DEPENDENCY",count:0},d.set(ee,G)),G.count+=1}for(let i of d.values()){let h=x.get(String(i.source).slice(5)),C=x.get(String(i.target).slice(5));h&&(h.outgoing+=i.count),C&&(C.incoming+=i.count)}let u=[...x.values()],g=[...d.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:u.length,clusters:l?1:0},nodes:u,links:g,dashboard:v(o,{viewMode:"files",selection:l,totalNodes:r.nodes.length,totalLinks:r.links.length})}}function T(o,r,l){let y=r.nodes.filter(i=>i.filePath===l),x=new Set(y.map(i=>i.id)),d=new Set;for(let i of y.slice(0,1800))d.add(i.id);for(let i of r.links){if(d.size>=1800)break;let h=b(i.source),C=b(i.target);(x.has(h)||x.has(C))&&(d.add(h),d.size<1800&&d.add(C))}let u=r.nodes.filter(i=>d.has(i.id)).map(i=>({...i,viewKind:"symbol"})),g=[];for(let i of r.links){if(g.length>=12e3)break;let h=b(i.source),C=b(i.target);d.has(h)&&d.has(C)&&g.push({...i,source:h,target:C})}return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:new Set(u.map(i=>i.filePath)).size,clusters:new Set(u.map(i=>E(i))).size},nodes:u,links:g,dashboard:v(o,{viewMode:"symbols",selection:l,focused:!0,truncated:u.length>=1800||g.length>=12e3,totalNodes:r.nodes.length,totalLinks:r.links.length})}}ut(async(o,r)=>{try{let l=new URL(o.url??"/",`http://${o.headers.host??"localhost"}`);if(l.pathname==="/api/health"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify({ok:!0,mode:"multi-project",projects:S.projects.length,indexedProjects:S.projects.filter(f=>f.hasGraph).length,defaultProject:S.defaultProject}));return}if(l.pathname==="/api/projects"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(S));return}if(l.pathname==="/api/graph"){let S=await p(),f=l.searchParams.get("project")??S.defaultProject;if(!f){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"No indexed ToolNet project found."}));return}let y=S.projects.find(i=>i.remote===f);if(!y){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project not found.",project:f}));return}if(!y.hasGraph){r.statusCode=409,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project is registered but has not been indexed yet.",project:y}));return}let x=l.searchParams.get("refresh")==="1",d=await M(y,x),u=l.searchParams.get("mode")??"full",g=d;if(u==="overview")g=R(y,d);else if(u==="files")g=F(y,d,l.searchParams.get("cluster")??"");else if(u==="symbols"){let i=l.searchParams.get("file");if(!i){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"file is required for symbols mode."}));return}g=T(y,d,i)}else if(u!=="full"){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:`Unknown graph mode: ${u}`}));return}r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(g));return}if(l.pathname==="/vendor/3d-force-graph.min.js"){r.setHeader("content-type","application/javascript; charset=utf-8"),ue(c).pipe(r);return}if(l.pathname==="/"||l.pathname==="/index.html"){r.setHeader("content-type","text/html; charset=utf-8"),r.setHeader("cache-control","no-store"),ue(Q(a,"index.html")).pipe(r);return}r.statusCode=404,r.end("Not found")}catch(l){console.error(l),r.statusCode=500,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:l instanceof Error?l.message:String(l)}))}}).listen(me,fe,()=>{console.log("ToolNet Multi-Project 3D Graph"),console.log(`Listening: http://${fe}:${me}`)})}St().catch(n=>{console.error(n),process.exit(1)});
|
|
1
|
+
import"dotenv/config";import{createReadStream as ue}from"node:fs";import{createServer as ut}from"node:http";import{dirname as ft,resolve as Q}from"node:path";import{fileURLToPath as mt}from"node:url";import{existsSync as ge,readFileSync as he}from"node:fs";import{homedir as ye}from"node:os";import{join as Se}from"node:path";function be(o){let t=o.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function Ce(){let o=process.env.TOOLNET_GLOBAL_ENV??Se(ye(),".config","toolnet-memory",".env");if(!ge(o))return;let t=he(o,"utf8");for(let e of t.split(/\r?\n/)){let s=e.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let a=s.indexOf("=");if(a<=0)continue;let c=s.slice(0,a).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(c)&&process.env[c]===void 0&&(process.env[c]=be(s.slice(a+1)))}}Ce();function M(o,t){return o===void 0?t:["1","true","yes","on"].includes(o.toLowerCase())}function L(o,t){if(!o)return t;let e=Number(o);return Number.isFinite(e)?e:t}function te(){return{memory:{autoCapture:M(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:M(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:M(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:M(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:L(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:L(process.env.MEMORY_RERANK_TOP,10),finalContext:L(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:L(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:M(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:L(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as xe}from"node:crypto";import{existsSync as J,mkdirSync as we,readFileSync as Ee,renameSync as ve,writeFileSync as ke}from"node:fs";import{basename as Pe,dirname as K,join as B,parse as ne,resolve as I}from"node:path";var oe=".toolnet",Re="project.json";function je(o){return xe("sha256").update(o).digest("hex").slice(0,16)}function Y(o){return B(o,oe,Re)}function Te(o){return J(Y(o))}function Ae(o,t){let e=I(o),s=ne(e).root;for(;;){if(Te(e))return e;if(e===s||t&&e===I(t))break;let a=K(e);if(a===e)break;e=a}return null}function Ne(o){let t=I(o),e=ne(t).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(c=>J(B(t,c))))return t;if(t===e)break;let a=K(t);if(a===t)break;t=a}return I(o)}function Me(o){let t;try{t=JSON.parse(Ee(o,"utf8"))}catch(a){throw new Error(`Invalid ToolNet project manifest: ${o}: ${a instanceof Error?a.message:String(a)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${o}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${o}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${o}`);let s=new Date().toISOString();return{version:1,id:e.id,name:e.name,remote:typeof e.remote=="string"&&e.remote.trim()?e.remote:e.name,rootPath:typeof e.rootPath=="string"?e.rootPath:K(K(o)),createdAt:typeof e.createdAt=="string"?e.createdAt:s,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:s,graphVersion:typeof e.graphVersion=="number"?e.graphVersion:0,memoryVersion:typeof e.memoryVersion=="number"?e.memoryVersion:0,metadata:e.metadata&&typeof e.metadata=="object"?e.metadata:void 0}}function re(o,t){let e=B(o,oe);we(e,{recursive:!0});let s=Y(o),a=`${s}.tmp-${process.pid}`;ke(a,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),ve(a,s)}function se(o,t){return{id:o.id,name:o.name,remote:o.remote,rootPath:t,createdAt:o.createdAt,updatedAt:o.updatedAt,graphVersion:o.graphVersion,memoryVersion:o.memoryVersion,metadata:o.metadata}}var z=class{detect(t=process.cwd()){let e=I(t),s=Ne(e),c=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(v=>J(B(s,v))),p=Ae(e,c?s:void 0);if(p){let v=Y(p),R=Me(v);return R.rootPath!==p&&(R.rootPath=p,R.updatedAt=new Date().toISOString(),re(p,R)),se(R,p)}let P=new Date().toISOString(),b=Pe(s),E={version:1,id:je(s),name:b,remote:b,rootPath:s,createdAt:P,updatedAt:P,graphVersion:0,memoryVersion:0};return re(s,E),se(E,s)}};import*as w from"typescript";var A=class{symbols=new Map;edges=new Map;addSymbol(t){this.symbols.set(t.id,t)}addEdge(t){this.edges.set(t.id,t)}getSymbol(t){return this.symbols.get(t)}findByName(t,e){return[...this.symbols.values()].filter(s=>s.projectId===t&&(s.name===e||s.qualifiedName===e))}allSymbols(t){return[...this.symbols.values()].filter(e=>e.projectId===t)}allEdges(t){return[...this.edges.values()].filter(e=>e.projectId===t)}import(t,e){for(let s of t)this.addSymbol(s);for(let s of e)this.addEdge(s)}clearProject(t){for(let[e,s]of this.symbols)s.projectId===t&&this.symbols.delete(e);for(let[e,s]of this.edges)s.projectId===t&&this.edges.delete(e)}};import{execFile as $e}from"node:child_process";import{promisify as Fe}from"node:util";var Pr=Fe($e);import{DatabaseSync as gs}from"node:sqlite";import*as k from"typescript";import*as f from"typescript";var _s=new Set([f.SyntaxKind.EqualsToken,f.SyntaxKind.PlusEqualsToken,f.SyntaxKind.MinusEqualsToken,f.SyntaxKind.AsteriskEqualsToken,f.SyntaxKind.SlashEqualsToken,f.SyntaxKind.PercentEqualsToken,f.SyntaxKind.QuestionQuestionEqualsToken,f.SyntaxKind.AmpersandAmpersandEqualsToken,f.SyntaxKind.BarBarEqualsToken]);var H=class{constructor(t){this.graph=t}graph;build(t,e,s,a){let c=this.graph.allSymbols(t),p=this.graph.allEdges(t),P=new Map(e?.layers.map(n=>[n.filePath,n.layer])??[]),b=new Map;for(let n of e?.clusters??[])for(let r of n.files)b.set(r,{id:n.id,label:n.label});let E=new Map(e?.hotspots.map(n=>[n.filePath,n.score])??[]),v=new Map(s?.deadCode.map(n=>[n.symbolId,{confidence:n.confidence,score:n.score}])??[]),R=new Map,N=new Map;for(let n of p)N.set(n.from,(N.get(n.from)??0)+1),R.set(n.to,(R.get(n.to)??0)+1);let j=[];for(let n=0;n<c.length;n++){let r=c[n],l=b.get(r.filePath);j.push({id:r.id,name:r.name,qualifiedName:r.qualifiedName,type:r.type,filePath:r.filePath,layer:P.get(r.filePath),cluster:l?.id,clusterLabel:l?.label,hotspotScore:E.get(r.filePath),deadCode:v.get(r.id),incoming:R.get(r.id)??0,outgoing:N.get(r.id)??0}),((n+1)%100===0||n===c.length-1)&&a?.({current:n+1,total:c.length,phase:"nodes"})}let F=new Set(j.map(n=>n.id)),T=p.filter(n=>F.has(n.from)&&F.has(n.to)),D=[];for(let n=0;n<T.length;n++){let r=T[n];D.push({source:r.from,target:r.to,type:r.type,confidence:typeof r.metadata?.confidence=="string"?r.metadata.confidence:void 0,resolver:typeof r.metadata?.resolver=="string"?r.metadata.resolver:void 0}),((n+1)%100===0||n===T.length-1)&&a?.({current:n+1,total:T.length,phase:"links"})}return{version:1,projectId:t,generatedAt:new Date().toISOString(),summary:{nodes:j.length,links:D.length,files:new Set(j.map(n=>n.filePath)).size,clusters:new Set(j.map(n=>n.cluster).filter(Boolean)).size},nodes:j,links:D}}};import{homedir as ct}from"node:os";import{join as lt}from"node:path";import{DeleteObjectCommand as Ge,GetObjectCommand as Ke,HeadObjectCommand as ze,ListObjectsV2Command as Be,PutObjectCommand as He,S3Client as Ue}from"@aws-sdk/client-s3";import{getSignedUrl as We}from"@aws-sdk/s3-request-presigner";var U=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Ue({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new He({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await We(this.client,new Ke({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new ze({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Ge({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new Be({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};import{access as ie,mkdir as Ve,readFile as Je,readdir as Ye,rm as qe,stat as ae,writeFile as Qe}from"node:fs/promises";import{dirname as Xe,join as Ze,relative as ce,resolve as et}from"node:path";var O=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return et(this.root,e)}async put(t,e){let s=this.path(t);await Ve(Xe(s),{recursive:!0}),await Qe(s,e)}async get(t){try{return await Je(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await ie(this.path(t)),!0}catch{return!1}}async delete(t){await qe(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),s=[];try{await ie(e)}catch{return s}let a=async p=>{let P=await Ye(p,{withFileTypes:!0});for(let b of P){let E=Ze(p,b.name);if(b.isDirectory()){await a(E);continue}let v=await ae(E);s.push({key:ce(this.root,E),size:v.size,updatedAt:v.mtime.toISOString()})}},c=await ae(e);return c.isDirectory()?await a(e):s.push({key:ce(this.root,e),size:c.size,updatedAt:c.mtime.toISOString()}),s}};import{DeleteObjectCommand as tt,GetObjectCommand as rt,HeadObjectCommand as st,ListObjectsV2Command as nt,PutObjectCommand as ot,S3Client as it}from"@aws-sdk/client-s3";import{getSignedUrl as at}from"@aws-sdk/s3-request-presigner";var _=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new it({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new ot({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await at(this.client,new rt({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new st({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new tt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new nt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};function q(o,t){return console.warn(t),new O(o)}function le(o){let t=o.localRoot??lt(ct(),".toolnet-memory","storage");if(o.provider==="r2"){let e=o.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(o.provider==="s3"){let e=o.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] S3 credentials missing. Using local fallback.")}if(o.provider==="huggingface"){let e=o.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new U({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new O(t)}function dt(o){return new Promise(t=>setTimeout(t,o))}async function de(o,t={}){let e=Math.max(1,t.attempts??3),s=t.baseDelayMs??150,a=t.maxDelayMs??2e3,c;for(let p=1;p<=e;p++)try{return await o()}catch(P){if(c=P,p>=e)break;let b=Math.min(a,s*2**(p-1)),E=Math.floor(Math.random()*Math.max(1,b*.2));await dt(b+E)}throw c}var pt=new Set(["put","get","getText","delete","list"]);function pe(o,t={}){return new Proxy(o,{get(e,s){let a=Reflect.get(e,s,e);return typeof a!="function"?a:pt.has(s)?(...c)=>de(()=>Promise.resolve(a.apply(e,c)),t):a.bind(e)}})}var fe=Number(process.env.TOOLNET_GRAPH_PORT??9749),me=process.env.TOOLNET_GRAPH_HOST??"127.0.0.1";function gt(o){if(typeof o=="string")return o;if(!o||typeof o!="object")return null;let t=o;for(let e of["key","Key","path","name"])if(typeof t[e]=="string")return t[e];return null}async function ht(o,t){return(await o.list(t)).map(gt).filter(s=>!!s)}async function $(o,t){let e=await o.getText(t);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function yt(o){let t=await ht(o,"projects/"),e=[...new Set(t.filter(c=>/^projects\/[^/]+\/project\.json$/.test(c)).map(c=>c.split("/")[1]))],s=new Set(t),a=[];for(let c of e){let p=await $(o,`projects/${c}/project.json`);!p||typeof p.id!="string"||a.push({id:p.id,name:typeof p.name=="string"?p.name:c,remote:c,hasGraph:s.has(`projects/${c}/code/graph/current.json`),hasArchitecture:s.has(`projects/${c}/code/architecture/current.json`),hasAnalysis:s.has(`projects/${c}/code/analysis/current.json`),hasVisualization:s.has(`projects/${c}/code/visualization/graph.json`)})}return a.sort((c,p)=>c.name.localeCompare(p.name))}async function St(){let o=te(),t=new z().detect(),e=pe(le({provider:o.storage.provider,huggingface:o.storage.huggingface,localRoot:o.storage.localRoot}),{attempts:3});e.name!=="huggingface"&&console.warn("[graph-ui] Remote Hugging Face storage is not active.");let s=ft(mt(import.meta.url)),a=Q(s,"public"),c=Q(a,"vendor","3d-force-graph.min.js");async function p(){let n=await yt(e),r=t.remote??t.name,l=n.find(m=>m.remote===r&&m.hasGraph),S=n.find(m=>m.hasGraph);return{projects:n,defaultProject:l?.remote??S?.remote??null}}let P=new Map;function b(n){return String(typeof n=="object"&&n!==null?n.id:n)}function E(n){return n.clusterLabel||n.cluster||n.layer||"Unclustered"}function v(n,r={}){return{id:n.id,name:n.name,remote:n.remote,hasArchitecture:n.hasArchitecture,hasAnalysis:n.hasAnalysis,...r}}async function R(n){let r=`projects/${n.remote}`,l=await $(e,`${r}/code/graph/current.json`);if(!l||!Array.isArray(l.symbols)||!Array.isArray(l.edges))throw new Error(`Code graph missing for project ${n.name}`);let S=await $(e,`${r}/code/architecture/current.json`),m=await $(e,`${r}/code/analysis/current.json`),y=new A;y.import(l.symbols,l.edges);let C=new H(y).build(n.id,S,m);return await e.put(`${r}/code/visualization/graph.json`,JSON.stringify(C),"application/json"),{...C,dashboard:v(n)}}async function N(n,r=!1){r&&P.delete(n.remote);let l=P.get(n.remote);if(l)return l;let S=`projects/${n.remote}`,m=await $(e,`${S}/code/visualization/graph.json`);if(m&&Array.isArray(m.nodes)&&Array.isArray(m.links)){let C={...m,dashboard:v(n)};return P.set(n.remote,C),C}let y=await R(n);return P.set(n.remote,y),y}function j(n,r){let l=new Map,S=new Map;for(let d of r.nodes){let u=E(d),g=`cluster:${u}`;S.set(d.id,g);let i=l.get(g);i||(i={id:g,name:u,qualifiedName:u,type:"subsystem",filePath:"",clusterLabel:u,incoming:0,outgoing:0,memberCount:0,files:new Set,viewKind:"cluster"},l.set(g,i)),i.memberCount+=1,d.filePath&&i.files.add(d.filePath)}let m=new Map;for(let d of r.links){let u=S.get(b(d.source)),g=S.get(b(d.target));if(!u||!g||u===g)continue;let i=`${u}\0${g}`,h=m.get(i);h||(h={source:u,target:g,type:"SUBSYSTEM_DEPENDENCY",count:0},m.set(i,h)),h.count+=1}for(let d of m.values()){let u=l.get(d.source),g=l.get(d.target);u&&(u.outgoing+=d.count),g&&(g.incoming+=d.count)}let y=[...l.values()].map(d=>({...d,filePath:`${d.files.size} files \xB7 ${d.memberCount} symbols`,files:void 0})),C=[...m.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:y.length,links:C.length,files:r.summary?.files??0,clusters:y.length},nodes:y,links:C,dashboard:v(n,{viewMode:"overview",totalNodes:r.nodes.length,totalLinks:r.links.length})}}function F(n,r,l){let S=r.nodes.filter(i=>!l||E(i)===l),m=new Set(S.map(i=>i.id)),y=new Map,C=new Map;for(let i of S){let h=i.filePath||"(unknown)";y.set(i.id,h);let x=C.get(h);x||(x={id:`file:${h}`,name:h.split("/").pop()||h,qualifiedName:h,type:"file",filePath:h,clusterLabel:E(i),incoming:0,outgoing:0,memberCount:0,viewKind:"file"},C.set(h,x)),x.memberCount+=1}let d=new Map;for(let i of r.links){let h=b(i.source),x=b(i.target);if(!m.has(h)||!m.has(x))continue;let W=y.get(h),V=y.get(x);if(!W||!V||W===V)continue;let X=`file:${W}`,Z=`file:${V}`,ee=`${X}\0${Z}`,G=d.get(ee);G||(G={source:X,target:Z,type:"FILE_DEPENDENCY",count:0},d.set(ee,G)),G.count+=1}for(let i of d.values()){let h=C.get(String(i.source).slice(5)),x=C.get(String(i.target).slice(5));h&&(h.outgoing+=i.count),x&&(x.incoming+=i.count)}let u=[...C.values()],g=[...d.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:u.length,clusters:l?1:0},nodes:u,links:g,dashboard:v(n,{viewMode:"files",selection:l,totalNodes:r.nodes.length,totalLinks:r.links.length})}}function T(n,r,l){let y=r.nodes.filter(i=>i.filePath===l),C=new Set(y.map(i=>i.id)),d=new Set;for(let i of y.slice(0,1800))d.add(i.id);for(let i of r.links){if(d.size>=1800)break;let h=b(i.source),x=b(i.target);(C.has(h)||C.has(x))&&(d.add(h),d.size<1800&&d.add(x))}let u=r.nodes.filter(i=>d.has(i.id)).map(i=>({...i,viewKind:"symbol"})),g=[];for(let i of r.links){if(g.length>=12e3)break;let h=b(i.source),x=b(i.target);d.has(h)&&d.has(x)&&g.push({...i,source:h,target:x})}return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:new Set(u.map(i=>i.filePath)).size,clusters:new Set(u.map(i=>E(i))).size},nodes:u,links:g,dashboard:v(n,{viewMode:"symbols",selection:l,focused:!0,truncated:u.length>=1800||g.length>=12e3,totalNodes:r.nodes.length,totalLinks:r.links.length})}}ut(async(n,r)=>{try{let l=new URL(n.url??"/",`http://${n.headers.host??"localhost"}`);if(l.pathname==="/api/health"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify({ok:!0,mode:"multi-project",projects:S.projects.length,indexedProjects:S.projects.filter(m=>m.hasGraph).length,defaultProject:S.defaultProject}));return}if(l.pathname==="/api/projects"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(S));return}if(l.pathname==="/api/graph"){let S=await p(),m=l.searchParams.get("project")??S.defaultProject;if(!m){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"No indexed ToolNet project found."}));return}let y=S.projects.find(i=>i.remote===m);if(!y){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project not found.",project:m}));return}if(!y.hasGraph){r.statusCode=409,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project is registered but has not been indexed yet.",project:y}));return}let C=l.searchParams.get("refresh")==="1",d=await N(y,C),u=l.searchParams.get("mode")??"full",g=d;if(u==="overview")g=j(y,d);else if(u==="files")g=F(y,d,l.searchParams.get("cluster")??"");else if(u==="symbols"){let i=l.searchParams.get("file");if(!i){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"file is required for symbols mode."}));return}g=T(y,d,i)}else if(u!=="full"){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:`Unknown graph mode: ${u}`}));return}r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(g));return}if(l.pathname==="/vendor/3d-force-graph.min.js"){r.setHeader("content-type","application/javascript; charset=utf-8"),ue(c).pipe(r);return}if(l.pathname==="/"||l.pathname==="/index.html"){r.setHeader("content-type","text/html; charset=utf-8"),r.setHeader("cache-control","no-store"),ue(Q(a,"index.html")).pipe(r);return}r.statusCode=404,r.end("Not found")}catch(l){console.error(l),r.statusCode=500,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:l instanceof Error?l.message:String(l)}))}}).listen(fe,me,()=>{console.log("ToolNet Multi-Project 3D Graph"),console.log(`Listening: http://${me}:${fe}`)})}St().catch(o=>{console.error(o),process.exit(1)});
|