toolnet-memory 0.3.16 → 0.3.18
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 +74 -0
- package/.release-target +1 -1
- package/README.md +157 -7
- package/bin/toolnet-memory +12 -0
- package/bundle/agy-hook.js +59 -59
- package/bundle/agy.js +58 -58
- package/bundle/api.js +15 -15
- package/bundle/audit.js +2 -0
- package/bundle/background-refresh.js +7 -7
- package/bundle/code-capabilities.js +2 -0
- package/bundle/codex.js +63 -63
- package/bundle/context-runtime.js +41 -41
- package/bundle/context.js +15 -15
- package/bundle/copilot-hook.js +73 -73
- package/bundle/cursor-hook.js +73 -73
- package/bundle/docker-healthcheck.js +3 -0
- package/bundle/doctor.js +4 -4
- package/bundle/full-index.js +20 -19
- package/bundle/gc.js +5 -3
- package/bundle/graph-cli.js +1 -1
- package/bundle/graph-index.js +5 -4
- package/bundle/graph-ui.js +4 -3
- package/bundle/grok-hook.js +52 -52
- package/bundle/handoff-refresh.js +2 -2
- package/bundle/help-cli.js +4 -4
- package/bundle/impact.js +5 -4
- package/bundle/incremental.js +5 -5
- package/bundle/init.js +35 -35
- package/bundle/kiro-hook.js +74 -74
- package/bundle/learner.js +8 -6
- package/bundle/mcp.js +19 -17
- package/bundle/memory-agent.js +4 -4
- package/bundle/memory-query.js +3 -3
- package/bundle/opencode.js +73 -73
- package/bundle/project-manual.js +7 -7
- package/bundle/public/index.html +1089 -2306
- package/bundle/runtime.js +16 -13
- package/bundle/semantic.js +6 -5
- package/bundle/service.js +11 -6
- package/bundle/snapshot.js +6 -4
- package/bundle/status-cli.js +4 -4
- package/bundle/toolnet-cli.js +49 -49
- package/bundle/work-continuity.js +7 -7
- package/package.json +26 -2
- package/release-manifest.json +57 -1
package/bundle/graph-ui.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import"dotenv/config";import{createReadStream 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}),Je(i,r)}function j(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}}function oe(n){return{version:1,scheme:D,canonicalRemote:n.canonicalRemote,fingerprint:n.fingerprint,repositoryName:n.repositoryName}}function et(n){let t=n.metadata?.toolnetIdentity;if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.fingerprint=="string"?e.fingerprint:null}var Q=class{adopt(t,e){let r=se(I(t));if(!e.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!e.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!e.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(Ce(r)){let u=V(L(r));if(u.id!==e.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${u.id}`,`requested=${e.id}`].join(" "));return j(u,r)}let i=new Date().toISOString(),s={...e.metadata};e.gitIdentity&&(s.toolnetIdentity=oe(e.gitIdentity));let c={version:1,id:e.id.trim(),name:e.name.trim(),remote:e.remote.trim(),rootPath:r,createdAt:e.createdAt??i,updatedAt:i,graphVersion:e.graphVersion??0,memoryVersion:e.memoryVersion??0,metadata:Object.keys(s).length?s:void 0};return Y(r,c),j(c,r)}recordGitIdentity(t,e,r={}){let i=this.requireExisting(t),s=L(i.rootPath),c=V(s),u=et(c);if(u&&u!==e.fingerprint&&!r.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${u}`,`current=${e.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let g=c.metadata?.toolnetIdentity;return g&&typeof g=="object"&&!Array.isArray(g)&&g.fingerprint===e.fingerprint||(c.metadata={...c.metadata,toolnetIdentity:oe(e)},c.updatedAt=new Date().toISOString(),Y(i.rootPath,c)),j(c,i.rootPath)}findExisting(t=process.cwd()){let e=I(t),r=se(e),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(g=>J(F(r,g))),c=Se(e,s?r:void 0);if(!c)return null;let u=V(L(c));return j(u,c)}requireExisting(t=process.cwd()){let e=this.findExisting(t);if(!e)throw new Error("PROJECT_NOT_INITIALIZED");return e}detect(t=process.cwd()){let e=I(t),r=se(e),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(A=>J(F(r,A))),c=Se(e,s?r:void 0);if(c){let A=L(c),R=V(A);return R.rootPath!==c&&(R.rootPath=c,R.updatedAt=new Date().toISOString(),Y(c,R)),j(R,c)}let u=new Date().toISOString(),g=Qe(r),E=ye(r),P={version:1,id:E?he(E.canonicalRemote):Xe(r),name:g,remote:E?.repositoryName??g,rootPath:r,createdAt:u,updatedAt:u,graphVersion:0,memoryVersion:0,metadata:E?{toolnetIdentity:oe(E)}:void 0};return Y(r,P),j(P,r)}};var tt=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_-]{20,}\b/g,confidence:"exact"},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g,confidence:"exact"},{type:"aws_access_key",regex:/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,confidence:"exact"},{type:"github_token",regex:/\b(?:gh[pousr]_[A-Za-z0-9]{30,255}|github_pat_[A-Za-z0-9_]{40,255})\b/g,confidence:"exact"},{type:"stripe_secret_key",regex:/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g,confidence:"exact"},{type:"google_api_key",regex:/\bAIza[A-Za-z0-9_-]{30,}\b/g,confidence:"exact"},{type:"slack_token",regex:/\bxox[baprs]-[A-Za-z0-9-]{16,}\b/g,confidence:"exact"},{type:"npm_token",regex:/\bnpm_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/gi,confidence:"high"},{type:"jwt",regex:/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,confidence:"exact"},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,confidence:"exact"},{type:"password_assignment",regex:/\b(?:password|passwd|pwd)\s*[:=]\s*["']?[^"' \t\r\n]{6,}["']?/gi,confidence:"high"},{type:"secret_assignment",regex:/\b(?:secret|api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key)\s*[:=]\s*["']?[^"' \t\r\n]{8,}["']?/gi,confidence:"high"},{type:"cookie",regex:/\b(?:cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi,confidence:"high"},{type:"url_credentials",regex:/\bhttps?:\/\/[^:/@\s]+:[^/@\s]{4,}@[^/\s]+/gi,confidence:"high"}],rt=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function Ee(n){return n.normalize("NFKC").trim().toLowerCase()}function nt(n){if(n.length===0)return 0;let t=new Map;for(let r of n)t.set(r,(t.get(r)??0)+1);let e=0;for(let r of t.values()){let i=r/n.length;e-=i*Math.log2(i)}return e}function st(n){return/^[a-f0-9]{32}$/iu.test(n)||/^[a-f0-9]{40}$/iu.test(n)||/^[a-f0-9]{64}$/iu.test(n)}function ot(n,t,e){let r=n.slice(Math.max(0,t-48),t),i=n.slice(e,Math.min(n.length,e+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${i}`)}function it(n,t){return n.start<t.end&&t.start<n.end}function we(n){return n.sort((t,e)=>t.start!==e.start?t.start-e.start:e.end-e.start-(t.end-t.start))}var Z=class{allowValues=new Set;enableEntropyHeuristic;constructor(t={}){for(let e of t.allowValues??[]){let r=Ee(e);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=t.enableEntropyHeuristic??!0}scan(t){let e=[];for(let s of tt){let c=new RegExp(s.regex.source,s.regex.flags);for(let u of t.matchAll(c))u.index===void 0||!u[0]||this.allowed(u[0])||e.push({type:s.type,value:u[0],start:u.index,end:u.index+u[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&e.push(...this.entropyMatches(t));let r=we(e),i=[];for(let s of r)i.some(c=>it(c,s))||i.push(s);return we(i)}hasSecrets(t){return this.scan(t).length>0}allowed(t){let e=Ee(t);return rt.has(e)?!0:this.allowValues.has(e)}entropyMatches(t){let e=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let i of t.matchAll(r)){if(i.index===void 0||!i[0])continue;let s=i[0];this.allowed(s)||st(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||ot(t,i.index,i.index+s.length)&&(nt(s)<3.7||e.push({type:"high_entropy_secret",value:s,start:i.index,end:i.index+s.length,confidence:"heuristic"}))}return e}};var $=class{scanner;constructor(t={}){this.scanner=new Z(t)}sanitize(t){let e=this.scanner.scan(t);if(e.length===0)return{text:t,redacted:0,secretTypes:[]};let r=t,i=[...e].sort((c,u)=>u.start-c.start),s=new Set;for(let c of i)s.add(c.type),r=r.slice(0,c.start)+`[REDACTED:${c.type}]`+r.slice(c.end);return{text:r,redacted:e.length,secretTypes:[...s].sort()}}sanitizeValue(t){if(typeof t=="string")return this.sanitize(t).text;if(Array.isArray(t))return t.map(e=>this.sanitizeValue(e));if(t&&typeof t=="object"){let e={};for(let[r,i]of Object.entries(t)){let s=r.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");if(s.includes("password")||s.includes("passwd")||s==="pwd"||s.includes("secret")||s.includes("token")||s.includes("cookie")||s.includes("authorization")||s.includes("apikey")||s.includes("accesskey")||s.includes("privatekey")||s.includes("clientsecret")||s.includes("credential")){e[r]="[REDACTED]";continue}e[r]=this.sanitizeValue(i)}return e}return t}};var ur=new $;var Jr=2*1024*1024;var G=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(r=>r.projectId===t&&(r.name===e||r.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 r of t)this.addSymbol(r);for(let r of e)this.addEdge(r)}clearProject(t){for(let[e,r]of this.symbols)r.projectId===t&&this.symbols.delete(e);for(let[e,r]of this.edges)r.projectId===t&&this.edges.delete(e)}};import*as v from"typescript";import{execFile as dt}from"node:child_process";import{promisify as pt}from"node:util";var bn=pt(dt);import*as T from"typescript";import{DatabaseSync as gs}from"node:sqlite";import*as k from"typescript";import*as m from"typescript";var Bs=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 X=class{constructor(t){this.graph=t}graph;build(t,e,r,i){let s=this.graph.allSymbols(t),c=this.graph.allEdges(t),u=new Map(e?.layers.map(a=>[a.filePath,a.layer])??[]),g=new Map;for(let a of e?.clusters??[])for(let o of a.files)g.set(o,{id:a.id,label:a.label});let E=new Map(e?.hotspots.map(a=>[a.filePath,a.score])??[]),P=new Map(r?.deadCode.map(a=>[a.symbolId,{confidence:a.confidence,score:a.score}])??[]),A=new Map,R=new Map;for(let a of c)R.set(a.from,(R.get(a.from)??0)+1),A.set(a.to,(A.get(a.to)??0)+1);let M=[];for(let a=0;a<s.length;a++){let o=s[a],d=g.get(o.filePath);M.push({id:o.id,name:o.name,qualifiedName:o.qualifiedName,type:o.type,filePath:o.filePath,layer:u.get(o.filePath),cluster:d?.id,clusterLabel:d?.label,hotspotScore:E.get(o.filePath),deadCode:P.get(o.id),incoming:A.get(o.id)??0,outgoing:R.get(o.id)??0}),((a+1)%100===0||a===s.length-1)&&i?.({current:a+1,total:s.length,phase:"nodes"})}let H=new Set(M.map(a=>a.id)),N=c.filter(a=>H.has(a.from)&&H.has(a.to)),W=[];for(let a=0;a<N.length;a++){let o=N[a];W.push({source:o.from,target:o.to,type:o.type,confidence:typeof o.metadata?.confidence=="string"?o.metadata.confidence:void 0,resolver:typeof o.metadata?.resolver=="string"?o.metadata.resolver:void 0}),((a+1)%100===0||a===N.length-1)&&i?.({current:a+1,total:N.length,phase:"links"})}return{version:1,projectId:t,generatedAt:new Date().toISOString(),summary:{nodes:M.length,links:W.length,files:new Set(M.map(a=>a.filePath)).size,clusters:new Set(M.map(a=>a.cluster).filter(Boolean)).size},nodes:M,links:W}}};import{homedir as _t}from"node:os";import{join as Dt}from"node:path";import{DeleteObjectCommand as mt,GetObjectCommand as gt,HeadObjectCommand as ht,ListObjectsV2Command as yt,PutObjectCommand as St,S3Client as bt}from"@aws-sdk/client-s3";import{getSignedUrl as xt}from"@aws-sdk/s3-request-presigner";var ee=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new bt({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,r="application/octet-stream"){let i=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new St({Bucket:this.bucket,Key:t,Body:i,ContentType:r}))}async get(t){let e=await xt(this.client,new gt({Bucket:this.bucket,Key:t}),{expiresIn:60}),r=await fetch(e,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.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 ht({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 mt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],r;do{let i=await this.client.send(new yt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:r}));for(let s of i.Contents??[])s.Key&&e.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});r=i.IsTruncated?i.NextContinuationToken:void 0}while(r);return e}};import{access as ke,mkdir as Ct,readFile as Et,readdir as wt,rm as vt,stat as Pe,writeFile as kt}from"node:fs/promises";import{dirname as Pt,join as Rt,relative as Re,resolve as At}from"node:path";var K=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return At(this.root,e)}async put(t,e){let r=this.path(t);await Ct(Pt(r),{recursive:!0}),await kt(r,e)}async get(t){try{return await Et(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 ke(this.path(t)),!0}catch{return!1}}async delete(t){await vt(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),r=[];try{await ke(e)}catch{return r}let i=async c=>{let u=await wt(c,{withFileTypes:!0});for(let g of u){let E=Rt(c,g.name);if(g.isDirectory()){await i(E);continue}let P=await Pe(E);r.push({key:Re(this.root,E),size:P.size,updatedAt:P.mtime.toISOString()})}},s=await Pe(e);return s.isDirectory()?await i(e):r.push({key:Re(this.root,e),size:s.size,updatedAt:s.mtime.toISOString()}),r}};import{DeleteObjectCommand as Mt,GetObjectCommand as Tt,HeadObjectCommand as jt,ListObjectsV2Command as It,PutObjectCommand as Nt,S3Client as Lt}from"@aws-sdk/client-s3";import{getSignedUrl as Ot}from"@aws-sdk/s3-request-presigner";var z=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new Lt({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,r="application/octet-stream"){let i=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Nt({Bucket:this.bucket,Key:t,Body:i,ContentType:r}))}async get(t){let e=await Ot(this.client,new Tt({Bucket:this.bucket,Key:t}),{expiresIn:60}),r=await fetch(e,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.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 jt({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 Mt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],r;do{let i=await this.client.send(new It({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:r}));for(let s of i.Contents??[])s.Key&&e.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});r=i.IsTruncated?i.NextContinuationToken:void 0}while(r);return e}};function ie(n,t){return console.warn(t),new K(n)}function Ae(n){let t=n.localRoot??Dt(_t(),".toolnet-memory","storage");if(n.provider==="r2"){let e=n.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new z({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):ie(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 z({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):ie(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 ee({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):ie(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new K(t)}function Ft(n){return new Promise(t=>setTimeout(t,n))}async function Me(n,t={}){let e=Math.max(1,t.attempts??3),r=t.baseDelayMs??150,i=t.maxDelayMs??2e3,s;for(let c=1;c<=e;c++)try{return await n()}catch(u){if(s=u,c>=e)break;let g=Math.min(i,r*2**(c-1)),E=Math.floor(Math.random()*Math.max(1,g*.2));await Ft(g+E)}throw s}var $t=new Set(["put","get","getText","delete","list"]);function Te(n,t={}){return new Proxy(n,{get(e,r){let i=Reflect.get(e,r,e);return typeof i!="function"?i:$t.has(r)?(...s)=>Me(()=>Promise.resolve(i.apply(e,s)),t):i.bind(e)}})}var Ie=Number(process.env.TOOLNET_GRAPH_PORT??9749),Ne=process.env.TOOLNET_GRAPH_HOST??"127.0.0.1";function Bt(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(Bt).filter(r=>!!r)}async function B(n,t){let e=await n.getText(t);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function Wt(n){let t=await Ht(n,"projects/"),e=[...new Set(t.filter(s=>/^projects\/[^/]+\/project\.json$/.test(s)).map(s=>s.split("/")[1]))],r=new Set(t),i=[];for(let s of e){let c=await B(n,`projects/${s}/project.json`);!c||typeof c.id!="string"||i.push({id:c.id,name:typeof c.name=="string"?c.name:s,remote:s,hasGraph:r.has(`projects/${s}/code/graph/current.json`),hasArchitecture:r.has(`projects/${s}/code/architecture/current.json`),hasAnalysis:r.has(`projects/${s}/code/analysis/current.json`),hasVisualization:r.has(`projects/${s}/code/visualization/graph.json`)})}return i.sort((s,c)=>s.name.localeCompare(c.name))}async function Ut(){let n=pe(),t=new Q().detect(),e=Te(Ae({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 r=Kt(zt(import.meta.url)),i=ae(r,"public"),s=ae(i,"vendor","3d-force-graph.min.js");async function c(){let a=await Wt(e),o=t.remote??t.name,d=a.find(h=>h.remote===o&&h.hasGraph),x=a.find(h=>h.hasGraph);return{projects:a,defaultProject:d?.remote??x?.remote??null}}let u=new Map;function g(a){return String(typeof a=="object"&&a!==null?a.id:a)}function E(a){return a.clusterLabel||a.cluster||a.layer||"Unclustered"}function P(a,o={}){return{id:a.id,name:a.name,remote:a.remote,hasArchitecture:a.hasArchitecture,hasAnalysis:a.hasAnalysis,...o}}async function A(a){let o=`projects/${a.remote}`,d=await B(e,`${o}/code/graph/current.json`);if(!d||!Array.isArray(d.symbols)||!Array.isArray(d.edges))throw new Error(`Code graph missing for project ${a.name}`);let x=await B(e,`${o}/code/architecture/current.json`),h=await B(e,`${o}/code/analysis/current.json`),b=new G;b.import(d.symbols,d.edges);let C=new X(b).build(a.id,x,h);return await e.put(`${o}/code/visualization/graph.json`,JSON.stringify(C),"application/json"),{...C,dashboard:P(a)}}async function R(a,o=!1){o&&u.delete(a.remote);let d=u.get(a.remote);if(d)return d;let x=`projects/${a.remote}`,h=await B(e,`${x}/code/visualization/graph.json`);if(h&&Array.isArray(h.nodes)&&Array.isArray(h.links)){let C={...h,dashboard:P(a)};return u.set(a.remote,C),C}let b=await A(a);return u.set(a.remote,b),b}function M(a,o){let d=new Map,x=new Map;for(let p of o.nodes){let f=E(p),y=`cluster:${f}`;x.set(p.id,y);let l=d.get(y);l||(l={id:y,name:f,qualifiedName:f,type:"subsystem",filePath:"",clusterLabel:f,incoming:0,outgoing:0,memberCount:0,files:new Set,viewKind:"cluster"},d.set(y,l)),l.memberCount+=1,p.filePath&&l.files.add(p.filePath)}let h=new Map;for(let p of o.links){let f=x.get(g(p.source)),y=x.get(g(p.target));if(!f||!y||f===y)continue;let l=`${f}\0${y}`,S=h.get(l);S||(S={source:f,target:y,type:"SUBSYSTEM_DEPENDENCY",count:0},h.set(l,S)),S.count+=1}for(let p of h.values()){let f=d.get(p.source),y=d.get(p.target);f&&(f.outgoing+=p.count),y&&(y.incoming+=p.count)}let b=[...d.values()].map(p=>({...p,filePath:`${p.files.size} files \xB7 ${p.memberCount} symbols`,files:void 0})),C=[...h.values()];return{version:2,projectId:o.projectId,generatedAt:o.generatedAt,summary:{nodes:b.length,links:C.length,files:o.summary?.files??0,clusters:b.length},nodes:b,links:C,dashboard:P(a,{viewMode:"overview",totalNodes:o.nodes.length,totalLinks:o.links.length})}}function H(a,o,d){let x=o.nodes.filter(l=>!d||E(l)===d),h=new Set(x.map(l=>l.id)),b=new Map,C=new Map;for(let l of x){let S=l.filePath||"(unknown)";b.set(l.id,S);let w=C.get(S);w||(w={id:`file:${S}`,name:S.split("/").pop()||S,qualifiedName:S,type:"file",filePath:S,clusterLabel:E(l),incoming:0,outgoing:0,memberCount:0,viewKind:"file"},C.set(S,w)),w.memberCount+=1}let p=new Map;for(let l of o.links){let S=g(l.source),w=g(l.target);if(!h.has(S)||!h.has(w))continue;let te=b.get(S),re=b.get(w);if(!te||!re||te===re)continue;let ce=`file:${te}`,le=`file:${re}`,de=`${ce}\0${le}`,U=p.get(de);U||(U={source:ce,target:le,type:"FILE_DEPENDENCY",count:0},p.set(de,U)),U.count+=1}for(let l of p.values()){let S=C.get(String(l.source).slice(5)),w=C.get(String(l.target).slice(5));S&&(S.outgoing+=l.count),w&&(w.incoming+=l.count)}let f=[...C.values()],y=[...p.values()];return{version:2,projectId:o.projectId,generatedAt:o.generatedAt,summary:{nodes:f.length,links:y.length,files:f.length,clusters:d?1:0},nodes:f,links:y,dashboard:P(a,{viewMode:"files",selection:d,totalNodes:o.nodes.length,totalLinks:o.links.length})}}function N(a,o,d){let b=o.nodes.filter(l=>l.filePath===d),C=new Set(b.map(l=>l.id)),p=new Set;for(let l of b.slice(0,1800))p.add(l.id);for(let l of o.links){if(p.size>=1800)break;let S=g(l.source),w=g(l.target);(C.has(S)||C.has(w))&&(p.add(S),p.size<1800&&p.add(w))}let f=o.nodes.filter(l=>p.has(l.id)).map(l=>({...l,viewKind:"symbol"})),y=[];for(let l of o.links){if(y.length>=12e3)break;let S=g(l.source),w=g(l.target);p.has(S)&&p.has(w)&&y.push({...l,source:S,target:w})}return{version:2,projectId:o.projectId,generatedAt:o.generatedAt,summary:{nodes:f.length,links:y.length,files:new Set(f.map(l=>l.filePath)).size,clusters:new Set(f.map(l=>E(l))).size},nodes:f,links:y,dashboard:P(a,{viewMode:"symbols",selection:d,focused:!0,truncated:f.length>=1800||y.length>=12e3,totalNodes:o.nodes.length,totalLinks:o.links.length})}}Gt(async(a,o)=>{try{let d=new URL(a.url??"/",`http://${a.headers.host??"localhost"}`);if(d.pathname==="/api/health"){let x=await c();o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify({ok:!0,mode:"multi-project",projects:x.projects.length,indexedProjects:x.projects.filter(h=>h.hasGraph).length,defaultProject:x.defaultProject}));return}if(d.pathname==="/api/projects"){let x=await c();o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify(x));return}if(d.pathname==="/api/graph"){let x=await c(),h=d.searchParams.get("project")??x.defaultProject;if(!h){o.statusCode=404,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:"No indexed ToolNet project found."}));return}let b=x.projects.find(l=>l.remote===h);if(!b){o.statusCode=404,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:"Project not found.",project:h}));return}if(!b.hasGraph){o.statusCode=409,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:"Project is registered but has not been indexed yet.",project:b}));return}let C=d.searchParams.get("refresh")==="1",p=await R(b,C),f=d.searchParams.get("mode")??"full",y=p;if(f==="overview")y=M(b,p);else if(f==="files")y=H(b,p,d.searchParams.get("cluster")??"");else if(f==="symbols"){let l=d.searchParams.get("file");if(!l){o.statusCode=400,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:"file is required for symbols mode."}));return}y=N(b,p,l)}else if(f!=="full"){o.statusCode=400,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:`Unknown graph mode: ${f}`}));return}o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify(y));return}if(d.pathname==="/vendor/3d-force-graph.min.js"){o.setHeader("content-type","application/javascript; charset=utf-8"),je(s).pipe(o);return}if(d.pathname==="/"||d.pathname==="/index.html"){o.setHeader("content-type","text/html; charset=utf-8"),o.setHeader("cache-control","no-store"),je(ae(i,"index.html")).pipe(o);return}o.statusCode=404,o.end("Not found")}catch(d){console.error(d),o.statusCode=500,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:d instanceof Error?d.message:String(d)}))}}).listen(Ie,Ne,()=>{console.log("ToolNet Multi-Project 3D Graph"),console.log(`Listening: http://${Ne}:${Ie}`)})}Ut().catch(n=>{console.error(n),process.exit(1)});
|
|
1
|
+
import"dotenv/config";import{createReadStream as gt}from"node:fs";import{createServer as Kr}from"node:http";import{dirname as Hr,resolve as Re}from"node:path";import{fileURLToPath as Br}from"node:url";import{existsSync as yt,readFileSync as St}from"node:fs";import{homedir as bt}from"node:os";import{join as xt}from"node:path";function Et(r){let t=r.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 Ct(){let r=process.env.TOOLNET_GLOBAL_ENV??xt(bt(),".config","toolnet-memory",".env");if(!yt(r))return;let t=St(r,"utf8");for(let e of t.split(/\r?\n/)){let n=e.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let s=n.indexOf("=");if(s<=0)continue;let i=n.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=Et(n.slice(s+1)))}}Ct();function $(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function G(r,t){if(!r)return t;let e=Number(r);return Number.isFinite(e)?e:t}function Ae(){return{memory:{autoCapture:$(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:$(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:$(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:$(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:G(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:G(process.env.MEMORY_RERANK_TOP,10),finalContext:G(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:G(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:$(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:G(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as At}from"node:crypto";import{existsSync as oe,mkdirSync as Nt,readFileSync as Mt,renameSync as It,writeFileSync as Lt}from"node:fs";import{basename as _t,dirname as ie,join as H,parse as De,resolve as _}from"node:path";import{createHash as Ie}from"node:crypto";import{spawnSync as wt}from"node:child_process";var K="git-remote-v1",Rt=new Set(["github.com","gitlab.com","bitbucket.org"]);function Le(r,t){let e=t.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!e||e==="."||e===".."||e.split("/").some(n=>!n||n==="."||n==="..")?null:(Rt.has(r)&&(e=e.toLowerCase()),e)}function vt(r){let t;try{t=new URL(r)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(t.protocol))return null;let e=t.hostname.trim().toLowerCase();if(!e)return null;let n=t.protocol==="https:"&&t.port==="443"||t.protocol==="http:"&&t.port==="80"||t.protocol==="ssh:"&&t.port==="22",s=t.port&&!n?`${e}:${t.port}`:e,i=Le(e,t.pathname);return i?`${s}/${i}`:null}function Pt(r){let t=r.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!t)return null;let e=t[1]?.trim().toLowerCase();if(!e||e.length===1)return null;let n=Le(e,t[2]??"");return n?`${e}/${n}`:null}function Ne(r){let t=r.trim();return t?t.includes("://")?vt(t):Pt(t):null}function kt(r){return Ie("sha256").update(`${K}:${r}`).digest("hex")}function _e(r){return Ie("sha256").update(`toolnet-project:${K}:${r}`).digest("hex").slice(0,16)}function Tt(r){return r.split("/").filter(Boolean).at(-1)?.trim()||null}function ge(r,t){let e=wt("git",["-C",r,...t],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return e.error||e.status!==0?null:e.stdout?.trim()||null}function Me(r,t){let e=Tt(r);return e?{scheme:K,canonicalRemote:r,fingerprint:kt(r),repositoryName:e,source:t}:null}function Oe(r){let t=ge(r,["remote","get-url","origin"]);if(t){let s=Ne(t);if(s)return Me(s,"origin")}let e=ge(r,["remote"]);if(!e)return null;let n=new Set;for(let s of e.split(/\r?\n/u).map(i=>i.trim()).filter(Boolean)){let i=ge(r,["remote","get-url",s]);if(!i)continue;let c=Ne(i);c&&n.add(c)}return n.size!==1?null:Me([...n][0],"unique-remote")}var Fe=".toolnet",Ot="project.json";function jt(r){return At("sha256").update(r).digest("hex").slice(0,16)}function D(r){return H(r,Fe,Ot)}function $e(r){return oe(D(r))}function je(r,t){let e=_(r),n=De(e).root;for(;;){if($e(e))return e;if(e===n||t&&e===_(t))break;let s=ie(e);if(s===e)break;e=s}return null}function he(r){let t=_(r),e=De(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(i=>oe(H(t,i))))return t;if(t===e)break;let s=ie(t);if(s===t)break;t=s}return _(r)}function ne(r){let t;try{t=JSON.parse(Mt(r,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${r}: ${s instanceof Error?s.message:String(s)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let n=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:ie(ie(r)),createdAt:typeof e.createdAt=="string"?e.createdAt:n,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:n,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 se(r,t){let e=H(r,Fe);Nt(e,{recursive:!0});let n=D(r),s=`${n}.tmp-${process.pid}`;Lt(s,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),It(s,n)}function L(r,t){return{id:r.id,name:r.name,remote:r.remote,rootPath:t,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}function ye(r){return{version:1,scheme:K,canonicalRemote:r.canonicalRemote,fingerprint:r.fingerprint,repositoryName:r.repositoryName}}function Dt(r){let t=r.metadata?.toolnetIdentity;if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.fingerprint=="string"?e.fingerprint:null}var ae=class{adopt(t,e){let n=he(_(t));if(!e.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!e.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!e.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if($e(n)){let u=ne(D(n));if(u.id!==e.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${u.id}`,`requested=${e.id}`].join(" "));return L(u,n)}let s=new Date().toISOString(),i={...e.metadata};e.gitIdentity&&(i.toolnetIdentity=ye(e.gitIdentity));let c={version:1,id:e.id.trim(),name:e.name.trim(),remote:e.remote.trim(),rootPath:n,createdAt:e.createdAt??s,updatedAt:s,graphVersion:e.graphVersion??0,memoryVersion:e.memoryVersion??0,metadata:Object.keys(i).length?i:void 0};return se(n,c),L(c,n)}recordGitIdentity(t,e,n={}){let s=this.requireExisting(t),i=D(s.rootPath),c=ne(i),u=Dt(c);if(u&&u!==e.fingerprint&&!n.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${u}`,`current=${e.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let p=c.metadata?.toolnetIdentity;return p&&typeof p=="object"&&!Array.isArray(p)&&p.fingerprint===e.fingerprint||(c.metadata={...c.metadata,toolnetIdentity:ye(e)},c.updatedAt=new Date().toISOString(),se(s.rootPath,c)),L(c,s.rootPath)}findExisting(t=process.cwd()){let e=_(t),n=he(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(p=>oe(H(n,p))),c=je(e,i?n:void 0);if(!c)return null;let u=ne(D(c));return L(u,c)}requireExisting(t=process.cwd()){let e=this.findExisting(t);if(!e)throw new Error("PROJECT_NOT_INITIALIZED");return e}detect(t=process.cwd()){let e=_(t),n=he(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(k=>oe(H(n,k))),c=je(e,i?n:void 0);if(c){let k=D(c),A=ne(k);return A.rootPath!==c&&(A.rootPath=c,A.updatedAt=new Date().toISOString(),se(c,A)),L(A,c)}let u=new Date().toISOString(),p=_t(n),m=Oe(n),P={version:1,id:m?_e(m.canonicalRemote):jt(n),name:p,remote:m?.repositoryName??p,rootPath:n,createdAt:u,updatedAt:u,graphVersion:0,memoryVersion:0,metadata:m?{toolnetIdentity:ye(m)}:void 0};return se(n,P),L(P,n)}};var Ft=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_-]{20,}\b/g,confidence:"exact"},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g,confidence:"exact"},{type:"aws_access_key",regex:/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,confidence:"exact"},{type:"github_token",regex:/\b(?:gh[pousr]_[A-Za-z0-9]{30,255}|github_pat_[A-Za-z0-9_]{40,255})\b/g,confidence:"exact"},{type:"stripe_secret_key",regex:/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g,confidence:"exact"},{type:"google_api_key",regex:/\bAIza[A-Za-z0-9_-]{30,}\b/g,confidence:"exact"},{type:"slack_token",regex:/\bxox[baprs]-[A-Za-z0-9-]{16,}\b/g,confidence:"exact"},{type:"npm_token",regex:/\bnpm_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/gi,confidence:"high"},{type:"jwt",regex:/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,confidence:"exact"},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,confidence:"exact"},{type:"password_assignment",regex:/\b(?:password|passwd|pwd)\s*[:=]\s*["']?[^"' \t\r\n]{6,}["']?/gi,confidence:"high"},{type:"secret_assignment",regex:/\b(?:secret|api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key)\s*[:=]\s*["']?[^"' \t\r\n]{8,}["']?/gi,confidence:"high"},{type:"cookie",regex:/\b(?:cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi,confidence:"high"},{type:"url_credentials",regex:/\bhttps?:\/\/[^:/@\s]+:[^/@\s]{4,}@[^/\s]+/gi,confidence:"high"}],$t=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function Ge(r){return r.normalize("NFKC").trim().toLowerCase()}function Gt(r){if(r.length===0)return 0;let t=new Map;for(let n of r)t.set(n,(t.get(n)??0)+1);let e=0;for(let n of t.values()){let s=n/r.length;e-=s*Math.log2(s)}return e}function Kt(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function Ht(r,t,e){let n=r.slice(Math.max(0,t-48),t),s=r.slice(e,Math.min(r.length,e+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${n} ${s}`)}function Bt(r,t){return r.start<t.end&&t.start<r.end}function Ke(r){return r.sort((t,e)=>t.start!==e.start?t.start-e.start:e.end-e.start-(t.end-t.start))}var ce=class{allowValues=new Set;enableEntropyHeuristic;constructor(t={}){for(let e of t.allowValues??[]){let n=Ge(e);n&&this.allowValues.add(n)}this.enableEntropyHeuristic=t.enableEntropyHeuristic??!0}scan(t){let e=[];for(let i of Ft){let c=new RegExp(i.regex.source,i.regex.flags);for(let u of t.matchAll(c))u.index===void 0||!u[0]||this.allowed(u[0])||e.push({type:i.type,value:u[0],start:u.index,end:u.index+u[0].length,confidence:i.confidence})}this.enableEntropyHeuristic&&e.push(...this.entropyMatches(t));let n=Ke(e),s=[];for(let i of n)s.some(c=>Bt(c,i))||s.push(i);return Ke(s)}hasSecrets(t){return this.scan(t).length>0}allowed(t){let e=Ge(t);return $t.has(e)?!0:this.allowValues.has(e)}entropyMatches(t){let e=[],n=/[A-Za-z0-9_+/=-]{32,160}/g;for(let s of t.matchAll(n)){if(s.index===void 0||!s[0])continue;let i=s[0];this.allowed(i)||Kt(i)||!/[A-Za-z]/u.test(i)||!/[0-9]/u.test(i)||Ht(t,s.index,s.index+i.length)&&(Gt(i)<3.7||e.push({type:"high_entropy_secret",value:i,start:s.index,end:s.index+i.length,confidence:"heuristic"}))}return e}};var B=class{scanner;constructor(t={}){this.scanner=new ce(t)}sanitize(t){let e=this.scanner.scan(t);if(e.length===0)return{text:t,redacted:0,secretTypes:[]};let n=t,s=[...e].sort((c,u)=>u.start-c.start),i=new Set;for(let c of s)i.add(c.type),n=n.slice(0,c.start)+`[REDACTED:${c.type}]`+n.slice(c.end);return{text:n,redacted:e.length,secretTypes:[...i].sort()}}sanitizeValue(t){if(typeof t=="string")return this.sanitize(t).text;if(Array.isArray(t))return t.map(e=>this.sanitizeValue(e));if(t&&typeof t=="object"){let e={};for(let[n,s]of Object.entries(t)){let i=n.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");if(i.includes("password")||i.includes("passwd")||i==="pwd"||i.includes("secret")||i.includes("token")||i.includes("cookie")||i.includes("authorization")||i.includes("apikey")||i.includes("accesskey")||i.includes("privatekey")||i.includes("clientsecret")||i.includes("credential")){e[n]="[REDACTED]";continue}e[n]=this.sanitizeValue(s)}return e}return t}};var mn=new B;var Qn=2*1024*1024;var z=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(n=>n.projectId===t&&(n.name===e||n.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 n of t)this.addSymbol(n);for(let n of e)this.addEdge(n)}clearProject(t){for(let[e,n]of this.symbols)n.projectId===t&&this.symbols.delete(e);for(let[e,n]of this.edges)n.projectId===t&&this.edges.delete(e)}};var M={supported:!0,structural:!0,lexicalSearch:!0,engine:"typescript-compiler-api",lexicalEngine:"file-chunk-fts5-bm25"},U={supported:!1,structural:!1,lexicalSearch:!0,engine:"unsupported",lexicalEngine:"file-chunk-fts5-bm25"},le=[{language:"typescript",extensions:[".ts"],...M},{language:"tsx",extensions:[".tsx"],...M},{language:"javascript",extensions:[".js"],...M},{language:"jsx",extensions:[".jsx"],...M},{language:"mts",extensions:[".mts"],...M},{language:"cts",extensions:[".cts"],...M},{language:"mjs",extensions:[".mjs"],...M},{language:"cjs",extensions:[".cjs"],...M},{language:"python",extensions:[".py"],...U,lspServer:"pyright-langserver"},{language:"go",extensions:[".go"],...U,lspServer:"gopls"},{language:"rust",extensions:[".rs"],...U,lspServer:"rust-analyzer"},{language:"c",extensions:[".c",".h"],...U,lspServer:"clangd"},{language:"cpp",extensions:[".cc",".cpp",".cxx",".hpp",".hh"],...U,lspServer:"clangd"}];import*as v from"typescript";import{execFile as Yt}from"node:child_process";import{promisify as Vt}from"node:util";var ks=Vt(Yt);import*as I from"typescript";import{DatabaseSync as Ro}from"node:sqlite";import*as T from"typescript";import*as h from"typescript";var Zo=new Set([h.SyntaxKind.EqualsToken,h.SyntaxKind.PlusEqualsToken,h.SyntaxKind.MinusEqualsToken,h.SyntaxKind.AsteriskEqualsToken,h.SyntaxKind.SlashEqualsToken,h.SyntaxKind.PercentEqualsToken,h.SyntaxKind.QuestionQuestionEqualsToken,h.SyntaxKind.AmpersandAmpersandEqualsToken,h.SyntaxKind.BarBarEqualsToken]);var de=class{constructor(t){this.graph=t}graph;build(t,e,n,s){let i=this.graph.allSymbols(t),c=this.graph.allEdges(t),u=new Map(e?.layers.map(a=>[a.filePath,a.layer])??[]),p=new Map;for(let a of e?.clusters??[])for(let o of a.files)p.set(o,{id:a.id,label:a.label});let m=new Map(e?.hotspots.map(a=>[a.filePath,a.score])??[]),P=new Map(n?.deadCode.map(a=>[a.symbolId,{confidence:a.confidence,score:a.score}])??[]),k=new Map,A=new Map;for(let a of c)A.set(a.from,(A.get(a.from)??0)+1),k.set(a.to,(k.get(a.to)??0)+1);let N=[];for(let a=0;a<i.length;a++){let o=i[a],d=p.get(o.filePath);N.push({id:o.id,name:o.name,qualifiedName:o.qualifiedName,type:o.type,filePath:o.filePath,layer:u.get(o.filePath),cluster:d?.id,clusterLabel:d?.label,hotspotScore:m.get(o.filePath),deadCode:P.get(o.id),incoming:k.get(o.id)??0,outgoing:A.get(o.id)??0}),((a+1)%100===0||a===i.length-1)&&s?.({current:a+1,total:i.length,phase:"nodes"})}let ee=new Set(N.map(a=>a.id)),j=c.filter(a=>ee.has(a.from)&&ee.has(a.to)),te=[];for(let a=0;a<j.length;a++){let o=j[a];te.push({source:o.from,target:o.to,type:o.type,confidence:typeof o.metadata?.confidence=="string"?o.metadata.confidence:void 0,resolver:typeof o.metadata?.resolver=="string"?o.metadata.resolver:void 0}),((a+1)%100===0||a===j.length-1)&&s?.({current:a+1,total:j.length,phase:"links"})}return{version:1,projectId:t,generatedAt:new Date().toISOString(),summary:{nodes:N.length,links:te.length,files:new Set(N.map(a=>a.filePath)).size,clusters:new Set(N.map(a=>a.cluster).filter(Boolean)).size},nodes:N,links:te}}};import{spawnSync as Qt}from"node:child_process";var Zt=[{language:"python",command:"pyright-langserver"},{language:"go",command:"gopls"},{language:"rust",command:"rust-analyzer"},{language:"c",command:"clangd"},{language:"cpp",command:"clangd"}];function Xt(r){return r.split(/\r?\n/u).map(e=>e.trim()).find(Boolean)||void 0}function er(r,t){let e=Qt(t,["--version"],{encoding:"utf8",timeout:2e3,windowsHide:!0}),n=!e.error&&e.status===0,s=[e.stdout??"",e.stderr??""].join(`
|
|
4
|
+
`);return{language:r,command:t,installed:n,...n?{version:Xt(s)}:{},mode:"external-optional",activeInStructuralGraph:!1}}function Be(){return Zt.map(r=>er(r.language,r.command))}var tr=process.argv.includes("--json"),ze=Be(),rr={structuralParser:{engine:"typescript-compiler-api",languages:le.filter(r=>r.structural).map(r=>r.language)},lexicalSearch:{engine:"sqlite-fts5-bm25",languages:le.filter(r=>r.lexicalSearch).map(r=>r.language)},lspFoundation:{mode:"detection-only",automaticDownload:!1,structuralGraphIntegration:!1,servers:ze}};if(tr)console.log(JSON.stringify(rr,null,2));else{console.log("ToolNet Code Intelligence Capabilities"),console.log(""),console.log("Structural parser: TypeScript Compiler API");for(let r of le)console.log([r.language.padEnd(12),r.structural?"structural=yes":"structural=no",r.lexicalSearch?"lexical=yes":"lexical=no",r.lspServer?`lsp=${r.lspServer}`:""].filter(Boolean).join(" "));console.log(""),console.log("LSP detection:");for(let r of ze)console.log([r.language.padEnd(12),r.command.padEnd(22),r.installed?"available":"not-installed"].join(" "))}import{homedir as Lr}from"node:os";import{join as _r}from"node:path";import{DeleteObjectCommand as nr,GetObjectCommand as sr,HeadObjectCommand as or,ListObjectsV2Command as ir,PutObjectCommand as ar,S3Client as cr}from"@aws-sdk/client-s3";import{getSignedUrl as lr}from"@aws-sdk/s3-request-presigner";var ue=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new cr({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,n="application/octet-stream"){let s=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new ar({Bucket:this.bucket,Key:t,Body:s,ContentType:n}))}async get(t){let e=await lr(this.client,new sr({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`HF download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.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 or({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 nr({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let s=await this.client.send(new ir({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let i of s.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return e}};import{access as Ue,mkdir as dr,readFile as ur,readdir as pr,rm as fr,stat as We,writeFile as mr}from"node:fs/promises";import{dirname as gr,join as hr,relative as Ye,resolve as yr}from"node:path";var W=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return yr(this.root,e)}async put(t,e){let n=this.path(t);await dr(gr(n),{recursive:!0}),await mr(n,e)}async get(t){try{return await ur(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 Ue(this.path(t)),!0}catch{return!1}}async delete(t){await fr(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),n=[];try{await Ue(e)}catch{return n}let s=async c=>{let u=await pr(c,{withFileTypes:!0});for(let p of u){let m=hr(c,p.name);if(p.isDirectory()){await s(m);continue}let P=await We(m);n.push({key:Ye(this.root,m),size:P.size,updatedAt:P.mtime.toISOString()})}},i=await We(e);return i.isDirectory()?await s(e):n.push({key:Ye(this.root,e),size:i.size,updatedAt:i.mtime.toISOString()}),n}};import{DeleteObjectCommand as Sr,GetObjectCommand as br,HeadObjectCommand as xr,ListObjectsV2Command as Er,PutObjectCommand as Cr,S3Client as wr}from"@aws-sdk/client-s3";import{getSignedUrl as Rr}from"@aws-sdk/s3-request-presigner";var Y=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new wr({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,n="application/octet-stream"){let s=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Cr({Bucket:this.bucket,Key:t,Body:s,ContentType:n}))}async get(t){let e=await Rr(this.client,new br({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`${this.name} download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.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 xr({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 Sr({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let s=await this.client.send(new Er({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let i of s.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return e}};import{createCipheriv as vr,createDecipheriv as Pr,createHash as kr,randomBytes as Tr,timingSafeEqual as Ar}from"node:crypto";import{readFileSync as Nr}from"node:fs";var O=Buffer.from("TNMEME01","ascii"),Je=1,V=8,J=12,Se=16,qe=O.length+1+V+J+Se,Mr="toolnet-memory:remote-encryption:v1:",Qe="aes-256-gcm",be=32,C=class extends Error{constructor(e,n){super(n);this.code=e;this.name="RemoteEncryptionError"}code};function Ir(r){return r?["1","true","yes","on","enabled"].includes(r.trim().toLowerCase()):!1}function xe(r=process.env){return Ir(r.TOOLNET_REMOTE_ENCRYPTION)}function Ve(r){let t=r.trim();if(!t)throw new C("REMOTE_ENCRYPTION_KEY_EMPTY","Remote encryption key is empty.");let e;if(t.startsWith("hex:")){let n=t.slice(4);if(!/^[0-9a-f]{64}$/iu.test(n))throw new C("REMOTE_ENCRYPTION_KEY_INVALID","hex: remote encryption key must contain exactly 64 hexadecimal characters.");e=Buffer.from(n,"hex")}else if(/^[0-9a-f]{64}$/iu.test(t))e=Buffer.from(t,"hex");else{let n=t.startsWith("base64:")?t.slice(7):t;if(!/^[A-Za-z0-9+/_-]+={0,2}$/u.test(n))throw new C("REMOTE_ENCRYPTION_KEY_INVALID","Remote encryption key must be 32 raw bytes encoded as hexadecimal or base64.");e=Buffer.from(n,n.includes("-")||n.includes("_")?"base64url":"base64")}if(e.length!==be)throw new C("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${be} bytes.`);return e}function Ze(r=process.env){let t=r.TOOLNET_REMOTE_ENCRYPTION_KEY?.trim(),e=r.TOOLNET_REMOTE_ENCRYPTION_KEY_FILE?.trim();if(t&&e)throw new C("REMOTE_ENCRYPTION_KEY_AMBIGUOUS","Configure either TOOLNET_REMOTE_ENCRYPTION_KEY or TOOLNET_REMOTE_ENCRYPTION_KEY_FILE, not both.");if(t)return Ve(t);if(e){let n;try{n=Nr(e,"utf8")}catch(s){throw new C("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${e}.`,s instanceof Error?s.message:String(s)].join(" "))}return Ve(n)}}function Xe(r){return kr("sha256").update(r).digest().subarray(0,V)}function et(r){return Buffer.from(`${Mr}${r}`,"utf8")}function Ee(r){return r.byteLength<O.length?!1:Buffer.from(r).subarray(0,O.length).equals(O)}function tt(r,t,e){if(e.byteLength!==be)throw new C("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH","AES-256-GCM requires a 32-byte key.");let n=typeof t=="string"?Buffer.from(t,"utf8"):Buffer.from(t),s=Tr(J),i=vr(Qe,e,s);i.setAAD(et(r));let c=Buffer.concat([i.update(n),i.final()]),u=i.getAuthTag(),p=Buffer.alloc(qe),m=0;return O.copy(p,m),m+=O.length,p.writeUInt8(Je,m),m+=1,Xe(e).copy(p,m),m+=V,s.copy(p,m),m+=J,u.copy(p,m),Buffer.concat([p,c])}function rt(r,t,e){let n=Buffer.from(t);if(!Ee(n))throw new C("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(n.length<qe)throw new C("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let s=O.length,i=n.readUInt8(s);if(s+=1,i!==Je)throw new C("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${i}.`);let c=n.subarray(s,s+V);s+=V;let u=Xe(e);if(!Ar(c,u))throw new C("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let p=n.subarray(s,s+J);s+=J;let m=n.subarray(s,s+Se);s+=Se;let P=n.subarray(s),k=Pr(Qe,e,p);k.setAAD(et(r)),k.setAuthTag(m);try{return Buffer.concat([k.update(P),k.final()])}catch{throw new C("REMOTE_ENCRYPTION_AUTH_FAILED","Encrypted remote object failed AES-GCM authentication.")}}var Ce=class{constructor(t,e){this.inner=t;this.options=e;if(this.name=t.name,e.enabled&&!e.key)throw new C("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote client-side encryption is enabled but no encryption key is configured.")}inner;options;name;async put(t,e,n){if(!this.options.enabled){await this.inner.put(t,e,n);return}let s=this.options.key;if(!s)throw new C("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let i=tt(t,e,s);await this.inner.put(t,i,"application/octet-stream")}async get(t){let e=await this.inner.get(t);if(!e)return null;if(!Ee(e))return e;if(!this.options.enabled)throw new C("REMOTE_ENCRYPTION_REQUIRED",["Remote object is client-side encrypted.","Enable TOOLNET_REMOTE_ENCRYPTION and configure the matching key."].join(" "));let n=this.options.key;if(!n)throw new C("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return rt(t,e,n)}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){return this.inner.exists(t)}async delete(t){await this.inner.delete(t)}async list(t=""){return this.inner.list(t)}};function nt(r,t=process.env){if(r.name==="local")return xe(t)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),r;let e=xe(t),n=e?Ze(t):void 0;return new Ce(r,{enabled:e,key:n})}function q(r){return nt(r)}function we(r,t){return console.warn(t),q(new W(r))}function st(r){let t=r.localRoot??_r(Lr(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?q(new Y({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):we(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let e=r.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?q(new Y({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):we(t,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let e=r.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?q(new ue({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):we(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return q(new W(t))}function Or(r){return new Promise(t=>setTimeout(t,r))}async function ot(r,t={}){let e=Math.max(1,t.attempts??3),n=t.baseDelayMs??150,s=t.maxDelayMs??2e3,i;for(let c=1;c<=e;c++)try{return await r()}catch(u){if(i=u,c>=e)break;let p=Math.min(s,n*2**(c-1)),m=Math.floor(Math.random()*Math.max(1,p*.2));await Or(p+m)}throw i}var jr=new Set(["put","get","getText","delete","list"]);function it(r,t={}){return new Proxy(r,{get(e,n){let s=Reflect.get(e,n,e);return typeof s!="function"?s:jr.has(n)?(...i)=>ot(()=>Promise.resolve(s.apply(e,i)),t):s.bind(e)}})}import{timingSafeEqual as Dr}from"node:crypto";var Fr=24;function F(r){return r.trim().toLowerCase().replace(/^\[/u,"").replace(/\]$/u,"")}function $r(r){let t=F(r);return t==="0.0.0.0"||t==="::"||t==="0:0:0:0:0:0:0:0"}function Q(r){let t=F(r);return!!(t==="localhost"||t==="::1"||t==="0:0:0:0:0:0:0:1"||/^127(?:\.\d{1,3}){3}$/u.test(t)||t==="::ffff:127.0.0.1")}function ct(r){let t=r.trim();if(!t)return null;try{let e=new URL(t.includes("://")?t:`http://${t}`);return F(e.hostname)}catch{return null}}function at(r){let t=r.trim();if(!t)return null;try{let e=new URL(t.includes("://")?t:`http://${t}`),n=F(e.hostname);return n?e.port?`${n}:${e.port}`:n:null}catch{return null}}function lt(r){if(!r?.trim())return[];let t=new Set;for(let e of r.split(",")){let n=ct(e);n&&t.add(n)}return[...t].sort()}function dt(r,t,e=[]){if(!t)return!1;let n=ct(t);if(!n)return!1;let s=new Set(e.map(F));return s.has(n)?!0:Q(r)?Q(n):$r(r)?s.size>0?Q(n):!0:F(r)===n}function ut(r,t){if(!r)return!0;if(!t)return!1;let e=at(r),n=at(t);return!e||!n?!1:e===n}function Gr(r,t){let e=Buffer.from(r,"utf8"),n=Buffer.from(t,"utf8");return e.length!==n.length?!1:Dr(e,n)}function pt(r,t){if(!t)return!0;if(!r)return!1;let e=r.match(/^Bearer[ \t]+(.+)$/iu);return e?.[1]?Gr(e[1].trim(),t):!1}function ft(r){return r?Buffer.byteLength(r,"utf8")>=Fr:!1}function mt(r){r.setHeader("x-content-type-options","nosniff"),r.setHeader("x-frame-options","DENY"),r.setHeader("referrer-policy","no-referrer"),r.setHeader("cross-origin-resource-policy","same-origin"),r.setHeader("permissions-policy",["camera=()","microphone=()","geolocation=()"].join(", ")),r.setHeader("content-security-policy",["default-src 'self'","script-src 'self' 'unsafe-inline'","style-src 'self' 'unsafe-inline'","connect-src 'self'","img-src 'self' data:","object-src 'none'","base-uri 'none'","frame-ancestors 'none'","form-action 'none'"].join("; "))}var ve=Number(process.env.TOOLNET_GRAPH_PORT??9749),Z=process.env.TOOLNET_GRAPH_HOST??"127.0.0.1",pe=process.env.TOOLNET_GRAPH_TOKEN?.trim()||void 0,ht=lt(process.env.TOOLNET_GRAPH_ALLOWED_HOSTS);function zr(r){if(typeof r=="string")return r;if(!r||typeof r!="object")return null;let t=r;for(let e of["key","Key","path","name"])if(typeof t[e]=="string")return t[e];return null}async function Ur(r,t){return(await r.list(t)).map(zr).filter(n=>!!n)}async function X(r,t){let e=await r.getText(t);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function Wr(r){let t=await Ur(r,"projects/"),e=[...new Set(t.filter(i=>/^projects\/[^/]+\/project\.json$/.test(i)).map(i=>i.split("/")[1]))],n=new Set(t),s=[];for(let i of e){let c=await X(r,`projects/${i}/project.json`);!c||typeof c.id!="string"||s.push({id:c.id,name:typeof c.name=="string"?c.name:i,remote:i,hasGraph:n.has(`projects/${i}/code/graph/current.json`),hasArchitecture:n.has(`projects/${i}/code/architecture/current.json`),hasAnalysis:n.has(`projects/${i}/code/analysis/current.json`),hasVisualization:n.has(`projects/${i}/code/visualization/graph.json`)})}return s.sort((i,c)=>i.name.localeCompare(c.name))}async function Yr(){Q(Z)||(console.warn(`[graph-ui] WARNING: Graph UI is exposed on non-loopback host ${Z}:${ve}.`),console.warn(pe?"[graph-ui] Bearer authentication is enabled for Graph API endpoints.":"[graph-ui] WARNING: Graph API has no bearer token. Set TOOLNET_GRAPH_TOKEN before exposing this port to an untrusted network."),ht.length===0&&console.warn("[graph-ui] TIP: Set TOOLNET_GRAPH_ALLOWED_HOSTS to restrict accepted Host headers when using a public/reverse-proxy endpoint.")),pe&&!ft(pe)&&console.warn("[graph-ui] WARNING: TOOLNET_GRAPH_TOKEN is short. Use at least 24 random bytes.");let r=Ae(),t=new ae().detect(),e=it(st({provider:r.storage.provider,huggingface:r.storage.huggingface,localRoot:r.storage.localRoot}),{attempts:3});e.name!=="huggingface"&&console.warn("[graph-ui] Remote Hugging Face storage is not active.");let n=Hr(Br(import.meta.url)),s=Re(n,"public"),i=Re(s,"vendor","3d-force-graph.min.js");async function c(){let a=await Wr(e),o=t.remote??t.name,d=a.find(b=>b.remote===o&&b.hasGraph),w=a.find(b=>b.hasGraph);return{projects:a,defaultProject:d?.remote??w?.remote??null}}let u=new Map;function p(a){return String(typeof a=="object"&&a!==null?a.id:a)}function m(a){return a.clusterLabel||a.cluster||a.layer||"Unclustered"}function P(a,o={}){return{id:a.id,name:a.name,remote:a.remote,hasArchitecture:a.hasArchitecture,hasAnalysis:a.hasAnalysis,...o}}async function k(a){let o=`projects/${a.remote}`,d=await X(e,`${o}/code/graph/current.json`);if(!d||!Array.isArray(d.symbols)||!Array.isArray(d.edges))throw new Error(`Code graph missing for project ${a.name}`);let w=await X(e,`${o}/code/architecture/current.json`),b=await X(e,`${o}/code/analysis/current.json`),x=new z;x.import(d.symbols,d.edges);let E=new de(x).build(a.id,w,b);return await e.put(`${o}/code/visualization/graph.json`,JSON.stringify(E),"application/json"),{...E,dashboard:P(a)}}async function A(a,o=!1){o&&u.delete(a.remote);let d=u.get(a.remote);if(d)return d;let w=`projects/${a.remote}`,b=await X(e,`${w}/code/visualization/graph.json`);if(b&&Array.isArray(b.nodes)&&Array.isArray(b.links)){let E={...b,dashboard:P(a)};return u.set(a.remote,E),E}let x=await k(a);return u.set(a.remote,x),x}function N(a,o){let d=new Map,w=new Map;for(let f of o.nodes){let g=m(f),y=`cluster:${g}`;w.set(f.id,y);let l=d.get(y);l||(l={id:y,name:g,qualifiedName:g,type:"subsystem",filePath:"",clusterLabel:g,incoming:0,outgoing:0,memberCount:0,files:new Set,viewKind:"cluster"},d.set(y,l)),l.memberCount+=1,f.filePath&&l.files.add(f.filePath)}let b=new Map;for(let f of o.links){let g=w.get(p(f.source)),y=w.get(p(f.target));if(!g||!y||g===y)continue;let l=`${g}\0${y}`,S=b.get(l);S||(S={source:g,target:y,type:"SUBSYSTEM_DEPENDENCY",count:0},b.set(l,S)),S.count+=1}for(let f of b.values()){let g=d.get(f.source),y=d.get(f.target);g&&(g.outgoing+=f.count),y&&(y.incoming+=f.count)}let x=[...d.values()].map(f=>({...f,filePath:`${f.files.size} files \xB7 ${f.memberCount} symbols`,files:void 0})),E=[...b.values()];return{version:2,projectId:o.projectId,generatedAt:o.generatedAt,summary:{nodes:x.length,links:E.length,files:o.summary?.files??0,clusters:x.length},nodes:x,links:E,dashboard:P(a,{viewMode:"overview",totalNodes:o.nodes.length,totalLinks:o.links.length})}}function ee(a,o,d){let w=o.nodes.filter(l=>!d||m(l)===d),b=new Set(w.map(l=>l.id)),x=new Map,E=new Map;for(let l of w){let S=l.filePath||"(unknown)";x.set(l.id,S);let R=E.get(S);R||(R={id:`file:${S}`,name:S.split("/").pop()||S,qualifiedName:S,type:"file",filePath:S,clusterLabel:m(l),incoming:0,outgoing:0,memberCount:0,viewKind:"file"},E.set(S,R)),R.memberCount+=1}let f=new Map;for(let l of o.links){let S=p(l.source),R=p(l.target);if(!b.has(S)||!b.has(R))continue;let fe=x.get(S),me=x.get(R);if(!fe||!me||fe===me)continue;let Pe=`file:${fe}`,ke=`file:${me}`,Te=`${Pe}\0${ke}`,re=f.get(Te);re||(re={source:Pe,target:ke,type:"FILE_DEPENDENCY",count:0},f.set(Te,re)),re.count+=1}for(let l of f.values()){let S=E.get(String(l.source).slice(5)),R=E.get(String(l.target).slice(5));S&&(S.outgoing+=l.count),R&&(R.incoming+=l.count)}let g=[...E.values()],y=[...f.values()];return{version:2,projectId:o.projectId,generatedAt:o.generatedAt,summary:{nodes:g.length,links:y.length,files:g.length,clusters:d?1:0},nodes:g,links:y,dashboard:P(a,{viewMode:"files",selection:d,totalNodes:o.nodes.length,totalLinks:o.links.length})}}function j(a,o,d){let x=o.nodes.filter(l=>l.filePath===d),E=new Set(x.map(l=>l.id)),f=new Set;for(let l of x.slice(0,1800))f.add(l.id);for(let l of o.links){if(f.size>=1800)break;let S=p(l.source),R=p(l.target);(E.has(S)||E.has(R))&&(f.add(S),f.size<1800&&f.add(R))}let g=o.nodes.filter(l=>f.has(l.id)).map(l=>({...l,viewKind:"symbol"})),y=[];for(let l of o.links){if(y.length>=12e3)break;let S=p(l.source),R=p(l.target);f.has(S)&&f.has(R)&&y.push({...l,source:S,target:R})}return{version:2,projectId:o.projectId,generatedAt:o.generatedAt,summary:{nodes:g.length,links:y.length,files:new Set(g.map(l=>l.filePath)).size,clusters:new Set(g.map(l=>m(l))).size},nodes:g,links:y,dashboard:P(a,{viewMode:"symbols",selection:d,focused:!0,truncated:g.length>=1800||y.length>=12e3,totalNodes:o.nodes.length,totalLinks:o.links.length})}}Kr(async(a,o)=>{try{mt(o);let d=new URL(a.url??"/",`http://${a.headers.host??"localhost"}`);if(!dt(Z,a.headers.host,ht)){o.statusCode=421,o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify({error:"misdirected_request"}));return}if(d.pathname==="/api/health"){o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify({ok:!0,service:"toolnet-memory-graph"}));return}if(d.pathname.startsWith("/api/")){if(a.method!=="GET"&&a.method!=="HEAD"){o.statusCode=405,o.setHeader("allow","GET, HEAD"),o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify({error:"method_not_allowed"}));return}if(!ut(a.headers.origin,a.headers.host)){o.statusCode=403,o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify({error:"forbidden_origin"}));return}if(!pt(a.headers.authorization,pe)){o.statusCode=401,o.setHeader("www-authenticate",'Bearer realm="ToolNet Graph"'),o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify({error:"unauthorized"}));return}}if(d.pathname==="/api/projects"){let w=await c();o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify(w));return}if(d.pathname==="/api/graph"){let w=await c(),b=d.searchParams.get("project")??w.defaultProject;if(!b){o.statusCode=404,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:"No indexed ToolNet project found."}));return}let x=w.projects.find(l=>l.remote===b);if(!x){o.statusCode=404,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:"Project not found.",project:b}));return}if(!x.hasGraph){o.statusCode=409,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:"Project is registered but has not been indexed yet.",project:x}));return}let E=d.searchParams.get("refresh")==="1",f=await A(x,E),g=d.searchParams.get("mode")??"full",y=f;if(g==="overview")y=N(x,f);else if(g==="files")y=ee(x,f,d.searchParams.get("cluster")??"");else if(g==="symbols"){let l=d.searchParams.get("file");if(!l){o.statusCode=400,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:"file is required for symbols mode."}));return}y=j(x,f,l)}else if(g!=="full"){o.statusCode=400,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:`Unknown graph mode: ${g}`}));return}o.setHeader("content-type","application/json; charset=utf-8"),o.setHeader("cache-control","no-store"),o.end(JSON.stringify(y));return}if(d.pathname==="/vendor/3d-force-graph.min.js"){o.setHeader("content-type","application/javascript; charset=utf-8"),gt(i).pipe(o);return}if(d.pathname==="/"||d.pathname==="/index.html"){o.setHeader("content-type","text/html; charset=utf-8"),o.setHeader("cache-control","no-store"),gt(Re(s,"index.html")).pipe(o);return}o.statusCode=404,o.end("Not found")}catch(d){console.error(d),o.statusCode=500,o.setHeader("content-type","application/json; charset=utf-8"),o.end(JSON.stringify({error:d instanceof Error?d.message:String(d)}))}}).listen(ve,Z,()=>{console.log("ToolNet Multi-Project 3D Graph"),console.log(`Listening: http://${Z}:${ve}`)})}Yr().catch(r=>{console.error(r),process.exit(1)});
|