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.
Files changed (45) hide show
  1. package/.env.example +74 -0
  2. package/.release-target +1 -1
  3. package/README.md +157 -7
  4. package/bin/toolnet-memory +12 -0
  5. package/bundle/agy-hook.js +59 -59
  6. package/bundle/agy.js +58 -58
  7. package/bundle/api.js +15 -15
  8. package/bundle/audit.js +2 -0
  9. package/bundle/background-refresh.js +7 -7
  10. package/bundle/code-capabilities.js +2 -0
  11. package/bundle/codex.js +63 -63
  12. package/bundle/context-runtime.js +41 -41
  13. package/bundle/context.js +15 -15
  14. package/bundle/copilot-hook.js +73 -73
  15. package/bundle/cursor-hook.js +73 -73
  16. package/bundle/docker-healthcheck.js +3 -0
  17. package/bundle/doctor.js +4 -4
  18. package/bundle/full-index.js +20 -19
  19. package/bundle/gc.js +5 -3
  20. package/bundle/graph-cli.js +1 -1
  21. package/bundle/graph-index.js +5 -4
  22. package/bundle/graph-ui.js +4 -3
  23. package/bundle/grok-hook.js +52 -52
  24. package/bundle/handoff-refresh.js +2 -2
  25. package/bundle/help-cli.js +4 -4
  26. package/bundle/impact.js +5 -4
  27. package/bundle/incremental.js +5 -5
  28. package/bundle/init.js +35 -35
  29. package/bundle/kiro-hook.js +74 -74
  30. package/bundle/learner.js +8 -6
  31. package/bundle/mcp.js +19 -17
  32. package/bundle/memory-agent.js +4 -4
  33. package/bundle/memory-query.js +3 -3
  34. package/bundle/opencode.js +73 -73
  35. package/bundle/project-manual.js +7 -7
  36. package/bundle/public/index.html +1089 -2306
  37. package/bundle/runtime.js +16 -13
  38. package/bundle/semantic.js +6 -5
  39. package/bundle/service.js +11 -6
  40. package/bundle/snapshot.js +6 -4
  41. package/bundle/status-cli.js +4 -4
  42. package/bundle/toolnet-cli.js +49 -49
  43. package/bundle/work-continuity.js +7 -7
  44. package/package.json +26 -2
  45. package/release-manifest.json +57 -1
@@ -1,5 +1,6 @@
1
- import"dotenv/config";import{existsSync as Ce,readFileSync as Ee}from"node:fs";import{homedir as we}from"node:os";import{join as ve}from"node:path";function Re(n){let e=n.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function ke(){let n=process.env.TOOLNET_GLOBAL_ENV??ve(we(),".config","toolnet-memory",".env");if(!Ce(n))return;let e=Ee(n,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let s=r.indexOf("=");if(s<=0)continue;let o=r.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=Re(r.slice(s+1)))}}ke();function w(n,e){return n===void 0?e:["1","true","yes","on"].includes(n.toLowerCase())}function v(n,e){if(!n)return e;let t=Number(n);return Number.isFinite(t)?t:e}function Q(){return{memory:{autoCapture:w(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:w(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:w(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:w(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:v(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:v(process.env.MEMORY_RERANK_TOP,10),finalContext:v(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:v(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:w(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:v(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Le}from"node:crypto";import{existsSync as N,mkdirSync as Ne,readFileSync as _e,renameSync as Oe,writeFileSync as Fe}from"node:fs";import{basename as De,dirname as _,join as k,parse as oe,resolve as x}from"node:path";import{createHash as ee}from"node:crypto";import{spawnSync as Pe}from"node:child_process";var R="git-remote-v1",Ae=new Set(["github.com","gitlab.com","bitbucket.org"]);function te(n,e){let t=e.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!t||t==="."||t===".."||t.split("/").some(r=>!r||r==="."||r==="..")?null:(Ae.has(n)&&(t=t.toLowerCase()),t)}function Me(n){let e;try{e=new URL(n)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(e.protocol))return null;let t=e.hostname.trim().toLowerCase();if(!t)return null;let r=e.protocol==="https:"&&e.port==="443"||e.protocol==="http:"&&e.port==="80"||e.protocol==="ssh:"&&e.port==="22",s=e.port&&!r?`${t}:${e.port}`:t,o=te(t,e.pathname);return o?`${s}/${o}`:null}function Te(n){let e=n.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!e)return null;let t=e[1]?.trim().toLowerCase();if(!t||t.length===1)return null;let r=te(t,e[2]??"");return r?`${t}/${r}`:null}function Z(n){let e=n.trim();return e?e.includes("://")?Me(e):Te(e):null}function Ie(n){return ee("sha256").update(`${R}:${n}`).digest("hex")}function re(n){return ee("sha256").update(`toolnet-project:${R}:${n}`).digest("hex").slice(0,16)}function je(n){return n.split("/").filter(Boolean).at(-1)?.trim()||null}function W(n,e){let t=Pe("git",["-C",n,...e],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return t.error||t.status!==0?null:t.stdout?.trim()||null}function X(n,e){let t=je(n);return t?{scheme:R,canonicalRemote:n,fingerprint:Ie(n),repositoryName:t,source:e}:null}function ne(n){let e=W(n,["remote","get-url","origin"]);if(e){let s=Z(e);if(s)return X(s,"origin")}let t=W(n,["remote"]);if(!t)return null;let r=new Set;for(let s of t.split(/\r?\n/u).map(o=>o.trim()).filter(Boolean)){let o=W(n,["remote","get-url",s]);if(!o)continue;let i=Z(o);i&&r.add(i)}return r.size!==1?null:X([...r][0],"unique-remote")}var ie=".toolnet",$e="project.json";function Ge(n){return Le("sha256").update(n).digest("hex").slice(0,16)}function C(n){return k(n,ie,$e)}function ae(n){return N(C(n))}function se(n,e){let t=x(n),r=oe(t).root;for(;;){if(ae(t))return t;if(t===r||e&&t===x(e))break;let s=_(t);if(s===t)break;t=s}return null}function U(n){let e=x(n),t=oe(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(o=>N(k(e,o))))return e;if(e===t)break;let s=_(e);if(s===e)break;e=s}return x(n)}function j(n){let e;try{e=JSON.parse(_e(n,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${n}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${n}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${n}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${n}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:_(_(n)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function L(n,e){let t=k(n,ie);Ne(t,{recursive:!0});let r=C(n),s=`${r}.tmp-${process.pid}`;Fe(s,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),Oe(s,r)}function b(n,e){return{id:n.id,name:n.name,remote:n.remote,rootPath:e,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}function V(n){return{version:1,scheme:R,canonicalRemote:n.canonicalRemote,fingerprint:n.fingerprint,repositoryName:n.repositoryName}}function Ke(n){let e=n.metadata?.toolnetIdentity;if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;return typeof t.fingerprint=="string"?t.fingerprint:null}var O=class{adopt(e,t){let r=U(x(e));if(!t.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!t.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!t.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(ae(r)){let a=j(C(r));if(a.id!==t.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${a.id}`,`requested=${t.id}`].join(" "));return b(a,r)}let s=new Date().toISOString(),o={...t.metadata};t.gitIdentity&&(o.toolnetIdentity=V(t.gitIdentity));let i={version:1,id:t.id.trim(),name:t.name.trim(),remote:t.remote.trim(),rootPath:r,createdAt:t.createdAt??s,updatedAt:s,graphVersion:t.graphVersion??0,memoryVersion:t.memoryVersion??0,metadata:Object.keys(o).length?o:void 0};return L(r,i),b(i,r)}recordGitIdentity(e,t,r={}){let s=this.requireExisting(e),o=C(s.rootPath),i=j(o),a=Ke(i);if(a&&a!==t.fingerprint&&!r.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${a}`,`current=${t.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let c=i.metadata?.toolnetIdentity;return c&&typeof c=="object"&&!Array.isArray(c)&&c.fingerprint===t.fingerprint||(i.metadata={...i.metadata,toolnetIdentity:V(t)},i.updatedAt=new Date().toISOString(),L(s.rootPath,i)),b(i,s.rootPath)}findExisting(e=process.cwd()){let t=x(e),r=U(t),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>N(k(r,c))),i=se(t,o?r:void 0);if(!i)return null;let a=j(C(i));return b(a,i)}requireExisting(e=process.cwd()){let t=this.findExisting(e);if(!t)throw new Error("PROJECT_NOT_INITIALIZED");return t}detect(e=process.cwd()){let t=x(e),r=U(t),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>N(k(r,m))),i=se(t,o?r:void 0);if(i){let m=C(i),f=j(m);return f.rootPath!==i&&(f.rootPath=i,f.updatedAt=new Date().toISOString(),L(i,f)),b(f,i)}let a=new Date().toISOString(),c=De(r),l=ne(r),p={version:1,id:l?re(l.canonicalRemote):Ge(r),name:c,remote:l?.repositoryName??c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0,metadata:l?{toolnetIdentity:V(l)}:void 0};return L(r,p),b(p,r)}};var ze=[{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"}],Be=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function ce(n){return n.normalize("NFKC").trim().toLowerCase()}function He(n){if(n.length===0)return 0;let e=new Map;for(let r of n)e.set(r,(e.get(r)??0)+1);let t=0;for(let r of e.values()){let s=r/n.length;t-=s*Math.log2(s)}return t}function We(n){return/^[a-f0-9]{32}$/iu.test(n)||/^[a-f0-9]{40}$/iu.test(n)||/^[a-f0-9]{64}$/iu.test(n)}function Ue(n,e,t){let r=n.slice(Math.max(0,e-48),e),s=n.slice(t,Math.min(n.length,t+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${s}`)}function Ve(n,e){return n.start<e.end&&e.start<n.end}function le(n){return n.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var F=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let r=ce(t);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let o of ze){let i=new RegExp(o.regex.source,o.regex.flags);for(let a of e.matchAll(i))a.index===void 0||!a[0]||this.allowed(a[0])||t.push({type:o.type,value:a[0],start:a.index,end:a.index+a[0].length,confidence:o.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let r=le(t),s=[];for(let o of r)s.some(i=>Ve(i,o))||s.push(o);return le(s)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=ce(e);return Be.has(t)?!0:this.allowValues.has(t)}entropyMatches(e){let t=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let s of e.matchAll(r)){if(s.index===void 0||!s[0])continue;let o=s[0];this.allowed(o)||We(o)||!/[A-Za-z]/u.test(o)||!/[0-9]/u.test(o)||Ue(e,s.index,s.index+o.length)&&(He(o)<3.7||t.push({type:"high_entropy_secret",value:o,start:s.index,end:s.index+o.length,confidence:"heuristic"}))}return t}};var E=class{scanner;constructor(e={}){this.scanner=new F(e)}sanitize(e){let t=this.scanner.scan(e);if(t.length===0)return{text:e,redacted:0,secretTypes:[]};let r=e,s=[...t].sort((i,a)=>a.start-i.start),o=new Set;for(let i of s)o.add(i.type),r=r.slice(0,i.start)+`[REDACTED:${i.type}]`+r.slice(i.end);return{text:r,redacted:t.length,secretTypes:[...o].sort()}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(t=>this.sanitizeValue(t));if(e&&typeof e=="object"){let t={};for(let[r,s]of Object.entries(e)){let o=r.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");if(o.includes("password")||o.includes("passwd")||o==="pwd"||o.includes("secret")||o.includes("token")||o.includes("cookie")||o.includes("authorization")||o.includes("apikey")||o.includes("accesskey")||o.includes("privatekey")||o.includes("clientsecret")||o.includes("credential")){t[r]="[REDACTED]";continue}t[r]=this.sanitizeValue(s)}return t}return e}};var Jt=new E;import{homedir as ht}from"node:os";import{join as yt}from"node:path";import{DeleteObjectCommand as Ye,GetObjectCommand as qe,HeadObjectCommand as Je,ListObjectsV2Command as Qe,PutObjectCommand as Ze,S3Client as Xe}from"@aws-sdk/client-s3";import{getSignedUrl as et}from"@aws-sdk/s3-request-presigner";var D=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Xe({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ze({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await et(this.client,new qe({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Je({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Ye({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new Qe({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let o of s.Contents??[])o.Key&&t.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};import{access as de,mkdir as tt,readFile as rt,readdir as nt,rm as st,stat as pe,writeFile as ot}from"node:fs/promises";import{dirname as it,join as at,relative as ue,resolve as ct}from"node:path";var P=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return ct(this.root,t)}async put(e,t){let r=this.path(e);await tt(it(r),{recursive:!0}),await ot(r,t)}async get(e){try{return await rt(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await de(this.path(e)),!0}catch{return!1}}async delete(e){await st(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await de(t)}catch{return r}let s=async i=>{let a=await nt(i,{withFileTypes:!0});for(let c of a){let l=at(i,c.name);if(c.isDirectory()){await s(l);continue}let p=await pe(l);r.push({key:ue(this.root,l),size:p.size,updatedAt:p.mtime.toISOString()})}},o=await pe(t);return o.isDirectory()?await s(t):r.push({key:ue(this.root,t),size:o.size,updatedAt:o.mtime.toISOString()}),r}};import{DeleteObjectCommand as lt,GetObjectCommand as dt,HeadObjectCommand as pt,ListObjectsV2Command as ut,PutObjectCommand as mt,S3Client as ft}from"@aws-sdk/client-s3";import{getSignedUrl as gt}from"@aws-sdk/s3-request-presigner";var A=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new ft({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new mt({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await gt(this.client,new dt({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new pt({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new lt({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new ut({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let o of s.Contents??[])o.Key&&t.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};function Y(n,e){return console.warn(e),new P(n)}function me(n){let e=n.localRoot??yt(ht(),".toolnet-memory","storage");if(n.provider==="r2"){let t=n.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new A({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):Y(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let t=n.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new A({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):Y(e,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let t=n.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new D({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):Y(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new P(e)}var M=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var $=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","chunks","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function fe(n){let e=n.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function ge(n){let e=n.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var G=class{constructor(e,t,r,s){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=fe(s??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,s=await this.provider.getText(e);if(s){let i;try{i=JSON.parse(s)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(o,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=ge(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};var zn=2*1024*1024;var T=class{symbols=new Map;edges=new Map;addSymbol(e){this.symbols.set(e.id,e)}addEdge(e){this.edges.set(e.id,e)}getSymbol(e){return this.symbols.get(e)}findByName(e,t){return[...this.symbols.values()].filter(r=>r.projectId===e&&(r.name===t||r.qualifiedName===t))}allSymbols(e){return[...this.symbols.values()].filter(t=>t.projectId===e)}allEdges(e){return[...this.edges.values()].filter(t=>t.projectId===e)}import(e,t){for(let r of e)this.addSymbol(r);for(let r of t)this.addEdge(r)}clearProject(e){for(let[t,r]of this.symbols)r.projectId===e&&this.symbols.delete(t);for(let[t,r]of this.edges)r.projectId===e&&this.edges.delete(t)}};import*as u from"typescript";import{execFile as Ct}from"node:child_process";import{promisify as Et}from"node:util";var ps=Et(Ct);import*as y from"typescript";import{createHash as vt}from"node:crypto";import{readFile as Rt}from"node:fs/promises";import{join as kt}from"node:path";function ye(n){return vt("sha256").update(n).digest("hex")}var K=class{sanitizer=new E;async build(e,t,r,s={}){let o=s.maxLines??80,i=s.overlapLines??10,a=new Map;for(let l of r){let p=a.get(l.filePath)??[];p.push(l),a.set(l.filePath,p)}let c=[];for(let[l,p]of a){let m;try{m=await Rt(kt(t,l),"utf8")}catch{continue}let f=m.split(/\r?\n/),S=p.filter(h=>h.type!=="file"&&typeof h.startLine=="number"&&typeof h.endLine=="number");if(S.length>0)for(let h of S)c.push(...this.chunkRange({projectId:e,filePath:l,lines:f,symbol:h,startLine:h.startLine,endLine:h.endLine,maxLines:o,overlapLines:i}));else c.push(...this.chunkRange({projectId:e,filePath:l,lines:f,startLine:1,endLine:Math.max(1,f.length),maxLines:o,overlapLines:i}))}return c}chunkRange(e){let{projectId:t,filePath:r,lines:s,symbol:o,maxLines:i}=e,a=Math.min(e.overlapLines,Math.max(0,i-1)),c=Math.max(1,e.startLine),l=Math.min(s.length,Math.max(c,e.endLine)),p=[],m=c,f=0;for(;m<=l;){let S=Math.min(l,m+i-1),h=s.slice(m-1,S).join(`
5
- `).trim();if(h){let q=this.sanitizer.sanitize(h).text,J=ye(q),xe=[t,r,o?.id??"file",String(f),J].join(":");p.push({id:ye(xe).slice(0,32),projectId:t,filePath:r,symbolId:o?.id,symbolName:o?.qualifiedName??o?.name,symbolType:o?.type,startLine:m,endLine:S,content:q,contentHash:J})}if(S>=l)break;m=S-a+1,f++}return p}};import{DatabaseSync as Pt}from"node:sqlite";var z=class{constructor(e){this.projectId=e;this.db.exec("CREATE VIRTUAL TABLE code_fts USING fts5(id UNINDEXED, projectId UNINDEXED, filePath, symbol, content, tokenize='unicode61')")}projectId;db=new Pt(":memory:");chunks=new Map;get size(){return this.chunks.size}build(e){if(this.db.exec("DELETE FROM code_fts"),this.chunks.clear(),e.length===0)return 0;let t=this.db.prepare("INSERT INTO code_fts (id, projectId, filePath, symbol, content) VALUES (?, ?, ?, ?, ?)");this.db.exec("BEGIN");try{for(let r of e)this.chunks.set(r.id,r),t.run(r.id,this.projectId,r.filePath,r.symbolName??"",r.content);this.db.exec("COMMIT")}catch(r){throw this.db.exec("ROLLBACK"),r}return e.length}search(e,t=8){let r=this.toMatchQuery(e);if(!r)return[];let s=this.db.prepare("SELECT id, bm25(code_fts) AS rank FROM code_fts WHERE code_fts MATCH ? ORDER BY rank LIMIT ?").all(r,Math.max(t*4,20)),o=[];for(let i of s){let a=this.chunks.get(i.id);if(!a)continue;let c=1/(1+Math.abs(i.rank));o.push({chunk:a,score:c})}return o.slice(0,t)}toMatchQuery(e){let t=e.toLowerCase().normalize("NFKC").split(/[^\p{L}\p{N}]+/u).map(r=>r.trim()).filter(r=>r.length>0);return t.length===0?null:t.length===1?`"${t[0]}"`:`(${t.map(r=>`"${r}"`).join(" OR ")})`}close(){this.db.close()}};function Se(n,e){let t=n.toLowerCase().replaceAll("\\","/"),r=e.toLowerCase(),s=/\b(test|tests|spec|fixture|mock)\b/.test(r);return(t.startsWith("tests/")||t.includes("/tests/")||t.includes("__tests__")||/\.(test|spec)\.[^.]+$/.test(t))&&!s?.35:t.includes("/generated/")||t.includes(".generated.")?.25:1}var I="sqlite-fts5-bm25",B="lexical",At="Local Code Search \u2014 SQLite FTS5/BM25",be={engine:I,mode:B,displayName:At,semantic:!1,embedding:!1,vectorDatabase:!1,queryExpansion:!1,synonymExpansion:!1,networkRequired:!1,indexBackend:"sqlite-fts5",ranking:"bm25",compatibilityAliases:["SemanticCodeEngine","semantic_code_search","toolnet-memory semantic","semantic-index"]};var H=class{constructor(e){this.options=e;this.fts=new z(e.projectId)}options;fts;get searchContract(){return be}async initialize(e){let{projectId:t,rootPath:r,storage:s,graph:o}=this.options,i=new $(s),a=await new K().build(t,r,o.allSymbols(t)),c=await i.load(t),l=!c||c.chunks.length!==a.length||c.chunks.some(m=>!a.some(f=>f.id===m.id));l&&await i.save({version:1,projectId:t,updatedAt:new Date().toISOString(),chunks:a}),e?.({current:a.length,total:a.length,phase:"index",detail:I});let p=this.fts.build(a);return{chunks:a.length,documentsIndexed:p,indexSize:this.fts.size,engine:I,mode:B,embedding:!1,vectorDatabase:!1,savedChunks:l,vectorsLoaded:0,vectorsIndexed:p,vectorsRemoved:0,totalVectors:this.fts.size,savedVectors:l}}async search(e,t=8){let r=this.fts.search(e,t),s=[];for(let o of r){let i=o.chunk,a=Se(i.filePath,e),c=o.score,l=0,p=c*a;s.push({chunk:i,score:p,lexicalScore:c,vectorScore:l,engine:I,mode:B})}return s}size(){return this.fts.size}};import*as g from"typescript";import*as d from"typescript";var Po=new Set([d.SyntaxKind.EqualsToken,d.SyntaxKind.PlusEqualsToken,d.SyntaxKind.MinusEqualsToken,d.SyntaxKind.AsteriskEqualsToken,d.SyntaxKind.SlashEqualsToken,d.SyntaxKind.PercentEqualsToken,d.SyntaxKind.QuestionQuestionEqualsToken,d.SyntaxKind.AmpersandAmpersandEqualsToken,d.SyntaxKind.BarBarEqualsToken]);async function Tt(){let n=Q(),e=new O().detect(),t=me({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),r=new G(t,e.id,e.name,e.remote??e.name),s=new T,o=await new M(r).load(e.id);if(!o)throw new Error("Code graph missing. Run npm run code:index:test first.");s.import(o.symbols,o.edges);let i=new H({projectId:e.id,rootPath:e.rootPath,storage:r,graph:s}),a=await i.initialize(),c=process.argv.slice(2).join(" ")||"memory storage hugging face",l=await i.search(c,5);console.log({ok:!0,command:"semantic",legacyCommandName:!0,search:{engine:"sqlite-fts5-bm25",mode:"lexical",semantic:!1,embedding:!1,vectorDatabase:!1},query:c,...a,results:l.map(p=>({score:Number(p.score.toFixed(4)),file:p.chunk.filePath,symbol:p.chunk.symbolName,lines:`${p.chunk.startLine}-${p.chunk.endLine}`,preview:p.chunk.content.replace(/\s+/g," ").slice(0,140)}))})}Tt().catch(n=>{console.error(n),process.exit(1)});
1
+ import"dotenv/config";import{existsSync as We,readFileSync as Ye}from"node:fs";import{homedir as Ve}from"node:os";import{join as qe}from"node:path";function Je(n){let e=n.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Qe(){let n=process.env.TOOLNET_GLOBAL_ENV??qe(Ve(),".config","toolnet-memory",".env");if(!We(n))return;let e=Ye(n,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let s=r.indexOf("=");if(s<=0)continue;let o=r.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=Je(r.slice(s+1)))}}Qe();function P(n,e){return n===void 0?e:["1","true","yes","on"].includes(n.toLowerCase())}function k(n,e){if(!n)return e;let t=Number(n);return Number.isFinite(t)?t:e}function de(){return{memory:{autoCapture:P(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:P(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:P(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:P(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:k(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:k(process.env.MEMORY_RERANK_TOP,10),finalContext:k(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:k(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:P(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:k(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as st}from"node:crypto";import{existsSync as K,mkdirSync as ot,readFileSync as it,renameSync as at,writeFileSync as ct}from"node:fs";import{basename as lt,dirname as B,join as A,parse as Se,resolve as C}from"node:path";import{createHash as fe}from"node:crypto";import{spawnSync as Ze}from"node:child_process";var T="git-remote-v1",Xe=new Set(["github.com","gitlab.com","bitbucket.org"]);function me(n,e){let t=e.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!t||t==="."||t===".."||t.split("/").some(r=>!r||r==="."||r==="..")?null:(Xe.has(n)&&(t=t.toLowerCase()),t)}function et(n){let e;try{e=new URL(n)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(e.protocol))return null;let t=e.hostname.trim().toLowerCase();if(!t)return null;let r=e.protocol==="https:"&&e.port==="443"||e.protocol==="http:"&&e.port==="80"||e.protocol==="ssh:"&&e.port==="22",s=e.port&&!r?`${t}:${e.port}`:t,o=me(t,e.pathname);return o?`${s}/${o}`:null}function tt(n){let e=n.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!e)return null;let t=e[1]?.trim().toLowerCase();if(!t||t.length===1)return null;let r=me(t,e[2]??"");return r?`${t}/${r}`:null}function pe(n){let e=n.trim();return e?e.includes("://")?et(e):tt(e):null}function rt(n){return fe("sha256").update(`${T}:${n}`).digest("hex")}function ge(n){return fe("sha256").update(`toolnet-project:${T}:${n}`).digest("hex").slice(0,16)}function nt(n){return n.split("/").filter(Boolean).at(-1)?.trim()||null}function X(n,e){let t=Ze("git",["-C",n,...e],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return t.error||t.status!==0?null:t.stdout?.trim()||null}function ue(n,e){let t=nt(n);return t?{scheme:T,canonicalRemote:n,fingerprint:rt(n),repositoryName:t,source:e}:null}function he(n){let e=X(n,["remote","get-url","origin"]);if(e){let s=pe(e);if(s)return ue(s,"origin")}let t=X(n,["remote"]);if(!t)return null;let r=new Set;for(let s of t.split(/\r?\n/u).map(o=>o.trim()).filter(Boolean)){let o=X(n,["remote","get-url",s]);if(!o)continue;let i=pe(o);i&&r.add(i)}return r.size!==1?null:ue([...r][0],"unique-remote")}var be=".toolnet",dt="project.json";function pt(n){return st("sha256").update(n).digest("hex").slice(0,16)}function R(n){return A(n,be,dt)}function xe(n){return K(R(n))}function ye(n,e){let t=C(n),r=Se(t).root;for(;;){if(xe(t))return t;if(t===r||e&&t===C(e))break;let s=B(t);if(s===t)break;t=s}return null}function ee(n){let e=C(n),t=Se(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(o=>K(A(e,o))))return e;if(e===t)break;let s=B(e);if(s===e)break;e=s}return C(n)}function $(n){let e;try{e=JSON.parse(it(n,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${n}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${n}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${n}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${n}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:B(B(n)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function G(n,e){let t=A(n,be);ot(t,{recursive:!0});let r=R(n),s=`${r}.tmp-${process.pid}`;ct(s,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),at(s,r)}function E(n,e){return{id:n.id,name:n.name,remote:n.remote,rootPath:e,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}function te(n){return{version:1,scheme:T,canonicalRemote:n.canonicalRemote,fingerprint:n.fingerprint,repositoryName:n.repositoryName}}function ut(n){let e=n.metadata?.toolnetIdentity;if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;return typeof t.fingerprint=="string"?t.fingerprint:null}var z=class{adopt(e,t){let r=ee(C(e));if(!t.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!t.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!t.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(xe(r)){let a=$(R(r));if(a.id!==t.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${a.id}`,`requested=${t.id}`].join(" "));return E(a,r)}let s=new Date().toISOString(),o={...t.metadata};t.gitIdentity&&(o.toolnetIdentity=te(t.gitIdentity));let i={version:1,id:t.id.trim(),name:t.name.trim(),remote:t.remote.trim(),rootPath:r,createdAt:t.createdAt??s,updatedAt:s,graphVersion:t.graphVersion??0,memoryVersion:t.memoryVersion??0,metadata:Object.keys(o).length?o:void 0};return G(r,i),E(i,r)}recordGitIdentity(e,t,r={}){let s=this.requireExisting(e),o=R(s.rootPath),i=$(o),a=ut(i);if(a&&a!==t.fingerprint&&!r.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${a}`,`current=${t.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let l=i.metadata?.toolnetIdentity;return l&&typeof l=="object"&&!Array.isArray(l)&&l.fingerprint===t.fingerprint||(i.metadata={...i.metadata,toolnetIdentity:te(t)},i.updatedAt=new Date().toISOString(),G(s.rootPath,i)),E(i,s.rootPath)}findExisting(e=process.cwd()){let t=C(e),r=ee(t),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(l=>K(A(r,l))),i=ye(t,o?r:void 0);if(!i)return null;let a=$(R(i));return E(a,i)}requireExisting(e=process.cwd()){let t=this.findExisting(e);if(!t)throw new Error("PROJECT_NOT_INITIALIZED");return t}detect(e=process.cwd()){let t=C(e),r=ee(t),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>K(A(r,u))),i=ye(t,o?r:void 0);if(i){let u=R(i),g=$(u);return g.rootPath!==i&&(g.rootPath=i,g.updatedAt=new Date().toISOString(),G(i,g)),E(g,i)}let a=new Date().toISOString(),l=lt(r),c=he(r),p={version:1,id:c?ge(c.canonicalRemote):pt(r),name:l,remote:c?.repositoryName??l,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0,metadata:c?{toolnetIdentity:te(c)}:void 0};return G(r,p),E(p,r)}};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"}],mt=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 gt(n){if(n.length===0)return 0;let e=new Map;for(let r of n)e.set(r,(e.get(r)??0)+1);let t=0;for(let r of e.values()){let s=r/n.length;t-=s*Math.log2(s)}return t}function ht(n){return/^[a-f0-9]{32}$/iu.test(n)||/^[a-f0-9]{40}$/iu.test(n)||/^[a-f0-9]{64}$/iu.test(n)}function yt(n,e,t){let r=n.slice(Math.max(0,e-48),e),s=n.slice(t,Math.min(n.length,t+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${s}`)}function St(n,e){return n.start<e.end&&e.start<n.end}function Ce(n){return n.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var U=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let r=Ee(t);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let o of ft){let i=new RegExp(o.regex.source,o.regex.flags);for(let a of e.matchAll(i))a.index===void 0||!a[0]||this.allowed(a[0])||t.push({type:o.type,value:a[0],start:a.index,end:a.index+a[0].length,confidence:o.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let r=Ce(t),s=[];for(let o of r)s.some(i=>St(i,o))||s.push(o);return Ce(s)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=Ee(e);return mt.has(t)?!0:this.allowValues.has(t)}entropyMatches(e){let t=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let s of e.matchAll(r)){if(s.index===void 0||!s[0])continue;let o=s[0];this.allowed(o)||ht(o)||!/[A-Za-z]/u.test(o)||!/[0-9]/u.test(o)||yt(e,s.index,s.index+o.length)&&(gt(o)<3.7||t.push({type:"high_entropy_secret",value:o,start:s.index,end:s.index+o.length,confidence:"heuristic"}))}return t}};var v=class{scanner;constructor(e={}){this.scanner=new U(e)}sanitize(e){let t=this.scanner.scan(e);if(t.length===0)return{text:e,redacted:0,secretTypes:[]};let r=e,s=[...t].sort((i,a)=>a.start-i.start),o=new Set;for(let i of s)o.add(i.type),r=r.slice(0,i.start)+`[REDACTED:${i.type}]`+r.slice(i.end);return{text:r,redacted:t.length,secretTypes:[...o].sort()}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(t=>this.sanitizeValue(t));if(e&&typeof e=="object"){let t={};for(let[r,s]of Object.entries(e)){let o=r.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");if(o.includes("password")||o.includes("passwd")||o==="pwd"||o.includes("secret")||o.includes("token")||o.includes("cookie")||o.includes("authorization")||o.includes("apikey")||o.includes("accesskey")||o.includes("privatekey")||o.includes("clientsecret")||o.includes("credential")){t[r]="[REDACTED]";continue}t[r]=this.sanitizeValue(s)}return t}return e}};var Or=new v;import{homedir as qt}from"node:os";import{join as Jt}from"node:path";import{DeleteObjectCommand as bt,GetObjectCommand as xt,HeadObjectCommand as Et,ListObjectsV2Command as Ct,PutObjectCommand as wt,S3Client as Rt}from"@aws-sdk/client-s3";import{getSignedUrl as vt}from"@aws-sdk/s3-request-presigner";var H=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Rt({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new wt({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await vt(this.client,new xt({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Et({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new bt({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new Ct({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let o of s.Contents??[])o.Key&&t.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};import{access as we,mkdir as Pt,readFile as kt,readdir as Tt,rm as At,stat as Re,writeFile as Mt}from"node:fs/promises";import{dirname as It,join as Nt,relative as ve,resolve as Lt}from"node:path";var M=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Lt(this.root,t)}async put(e,t){let r=this.path(e);await Pt(It(r),{recursive:!0}),await Mt(r,t)}async get(e){try{return await kt(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await we(this.path(e)),!0}catch{return!1}}async delete(e){await At(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await we(t)}catch{return r}let s=async i=>{let a=await Tt(i,{withFileTypes:!0});for(let l of a){let c=Nt(i,l.name);if(l.isDirectory()){await s(c);continue}let p=await Re(c);r.push({key:ve(this.root,c),size:p.size,updatedAt:p.mtime.toISOString()})}},o=await Re(t);return o.isDirectory()?await s(t):r.push({key:ve(this.root,t),size:o.size,updatedAt:o.mtime.toISOString()}),r}};import{DeleteObjectCommand as _t,GetObjectCommand as Ot,HeadObjectCommand as jt,ListObjectsV2Command as Ft,PutObjectCommand as Dt,S3Client as $t}from"@aws-sdk/client-s3";import{getSignedUrl as Gt}from"@aws-sdk/s3-request-presigner";var I=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new $t({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Dt({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await Gt(this.client,new Ot({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new jt({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new _t({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new Ft({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let o of s.Contents??[])o.Key&&t.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};import{createCipheriv as Kt,createDecipheriv as Bt,createHash as zt,randomBytes as Ut,timingSafeEqual as Ht}from"node:crypto";import{readFileSync as Wt}from"node:fs";var w=Buffer.from("TNMEME01","ascii"),ke=1,N=8,L=12,re=16,Te=w.length+1+N+L+re,Yt="toolnet-memory:remote-encryption:v1:",Ae="aes-256-gcm",ne=32,f=class extends Error{constructor(t,r){super(r);this.code=t;this.name="RemoteEncryptionError"}code};function Vt(n){return n?["1","true","yes","on","enabled"].includes(n.trim().toLowerCase()):!1}function se(n=process.env){return Vt(n.TOOLNET_REMOTE_ENCRYPTION)}function Pe(n){let e=n.trim();if(!e)throw new f("REMOTE_ENCRYPTION_KEY_EMPTY","Remote encryption key is empty.");let t;if(e.startsWith("hex:")){let r=e.slice(4);if(!/^[0-9a-f]{64}$/iu.test(r))throw new f("REMOTE_ENCRYPTION_KEY_INVALID","hex: remote encryption key must contain exactly 64 hexadecimal characters.");t=Buffer.from(r,"hex")}else if(/^[0-9a-f]{64}$/iu.test(e))t=Buffer.from(e,"hex");else{let r=e.startsWith("base64:")?e.slice(7):e;if(!/^[A-Za-z0-9+/_-]+={0,2}$/u.test(r))throw new f("REMOTE_ENCRYPTION_KEY_INVALID","Remote encryption key must be 32 raw bytes encoded as hexadecimal or base64.");t=Buffer.from(r,r.includes("-")||r.includes("_")?"base64url":"base64")}if(t.length!==ne)throw new f("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${ne} bytes.`);return t}function Me(n=process.env){let e=n.TOOLNET_REMOTE_ENCRYPTION_KEY?.trim(),t=n.TOOLNET_REMOTE_ENCRYPTION_KEY_FILE?.trim();if(e&&t)throw new f("REMOTE_ENCRYPTION_KEY_AMBIGUOUS","Configure either TOOLNET_REMOTE_ENCRYPTION_KEY or TOOLNET_REMOTE_ENCRYPTION_KEY_FILE, not both.");if(e)return Pe(e);if(t){let r;try{r=Wt(t,"utf8")}catch(s){throw new f("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${t}.`,s instanceof Error?s.message:String(s)].join(" "))}return Pe(r)}}function Ie(n){return zt("sha256").update(n).digest().subarray(0,N)}function Ne(n){return Buffer.from(`${Yt}${n}`,"utf8")}function oe(n){return n.byteLength<w.length?!1:Buffer.from(n).subarray(0,w.length).equals(w)}function Le(n,e,t){if(t.byteLength!==ne)throw new f("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH","AES-256-GCM requires a 32-byte key.");let r=typeof e=="string"?Buffer.from(e,"utf8"):Buffer.from(e),s=Ut(L),o=Kt(Ae,t,s);o.setAAD(Ne(n));let i=Buffer.concat([o.update(r),o.final()]),a=o.getAuthTag(),l=Buffer.alloc(Te),c=0;return w.copy(l,c),c+=w.length,l.writeUInt8(ke,c),c+=1,Ie(t).copy(l,c),c+=N,s.copy(l,c),c+=L,a.copy(l,c),Buffer.concat([l,i])}function _e(n,e,t){let r=Buffer.from(e);if(!oe(r))throw new f("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(r.length<Te)throw new f("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let s=w.length,o=r.readUInt8(s);if(s+=1,o!==ke)throw new f("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${o}.`);let i=r.subarray(s,s+N);s+=N;let a=Ie(t);if(!Ht(i,a))throw new f("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let l=r.subarray(s,s+L);s+=L;let c=r.subarray(s,s+re);s+=re;let p=r.subarray(s),u=Bt(Ae,t,l);u.setAAD(Ne(n)),u.setAuthTag(c);try{return Buffer.concat([u.update(p),u.final()])}catch{throw new f("REMOTE_ENCRYPTION_AUTH_FAILED","Encrypted remote object failed AES-GCM authentication.")}}var ie=class{constructor(e,t){this.inner=e;this.options=t;if(this.name=e.name,t.enabled&&!t.key)throw new f("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote client-side encryption is enabled but no encryption key is configured.")}inner;options;name;async put(e,t,r){if(!this.options.enabled){await this.inner.put(e,t,r);return}let s=this.options.key;if(!s)throw new f("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let o=Le(e,t,s);await this.inner.put(e,o,"application/octet-stream")}async get(e){let t=await this.inner.get(e);if(!t)return null;if(!oe(t))return t;if(!this.options.enabled)throw new f("REMOTE_ENCRYPTION_REQUIRED",["Remote object is client-side encrypted.","Enable TOOLNET_REMOTE_ENCRYPTION and configure the matching key."].join(" "));let r=this.options.key;if(!r)throw new f("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return _e(e,t,r)}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){return this.inner.exists(e)}async delete(e){await this.inner.delete(e)}async list(e=""){return this.inner.list(e)}};function Oe(n,e=process.env){if(n.name==="local")return se(e)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),n;let t=se(e),r=t?Me(e):void 0;return new ie(n,{enabled:t,key:r})}function _(n){return Oe(n)}function ae(n,e){return console.warn(e),_(new M(n))}function je(n){let e=n.localRoot??Jt(qt(),".toolnet-memory","storage");if(n.provider==="r2"){let t=n.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?_(new I({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):ae(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let t=n.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?_(new I({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):ae(e,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let t=n.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?_(new H({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):ae(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return _(new M(e))}var O=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var W=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","chunks","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function Fe(n){let e=n.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function De(n){let e=n.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var Y=class{constructor(e,t,r,s){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=Fe(s??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,s=await this.provider.getText(e);if(s){let i;try{i=JSON.parse(s)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(o,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=De(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};var _s=2*1024*1024;var j=class{symbols=new Map;edges=new Map;addSymbol(e){this.symbols.set(e.id,e)}addEdge(e){this.edges.set(e.id,e)}getSymbol(e){return this.symbols.get(e)}findByName(e,t){return[...this.symbols.values()].filter(r=>r.projectId===e&&(r.name===t||r.qualifiedName===t))}allSymbols(e){return[...this.symbols.values()].filter(t=>t.projectId===e)}allEdges(e){return[...this.edges.values()].filter(t=>t.projectId===e)}import(e,t){for(let r of e)this.addSymbol(r);for(let r of t)this.addEdge(r)}clearProject(e){for(let[t,r]of this.symbols)r.projectId===e&&this.symbols.delete(t);for(let[t,r]of this.edges)r.projectId===e&&this.edges.delete(t)}};var S={supported:!0,structural:!0,lexicalSearch:!0,engine:"typescript-compiler-api",lexicalEngine:"file-chunk-fts5-bm25"},F={supported:!1,structural:!1,lexicalSearch:!0,engine:"unsupported",lexicalEngine:"file-chunk-fts5-bm25"},V=[{language:"typescript",extensions:[".ts"],...S},{language:"tsx",extensions:[".tsx"],...S},{language:"javascript",extensions:[".js"],...S},{language:"jsx",extensions:[".jsx"],...S},{language:"mts",extensions:[".mts"],...S},{language:"cts",extensions:[".cts"],...S},{language:"mjs",extensions:[".mjs"],...S},{language:"cjs",extensions:[".cjs"],...S},{language:"python",extensions:[".py"],...F,lspServer:"pyright-langserver"},{language:"go",extensions:[".go"],...F,lspServer:"gopls"},{language:"rust",extensions:[".rs"],...F,lspServer:"rust-analyzer"},{language:"c",extensions:[".c",".h"],...F,lspServer:"clangd"},{language:"cpp",extensions:[".cc",".cpp",".cxx",".hpp",".hh"],...F,lspServer:"clangd"}];import*as m from"typescript";import{execFile as er}from"node:child_process";import{promisify as tr}from"node:util";var po=tr(er);import*as b from"typescript";import{createHash as nr}from"node:crypto";import{readFile as sr}from"node:fs/promises";import{join as or}from"node:path";function Ge(n){return nr("sha256").update(n).digest("hex")}var q=class{sanitizer=new v;async build(e,t,r,s={}){let o=s.maxLines??80,i=s.overlapLines??10,a=new Map;for(let c of r){let p=a.get(c.filePath)??[];p.push(c),a.set(c.filePath,p)}let l=[];for(let[c,p]of a){let u;try{u=await sr(or(t,c),"utf8")}catch{continue}let g=u.split(/\r?\n/),x=p.filter(y=>y.type!=="file"&&typeof y.startLine=="number"&&typeof y.endLine=="number");if(x.length>0)for(let y of x)l.push(...this.chunkRange({projectId:e,filePath:c,lines:g,symbol:y,startLine:y.startLine,endLine:y.endLine,maxLines:o,overlapLines:i}));else l.push(...this.chunkRange({projectId:e,filePath:c,lines:g,startLine:1,endLine:Math.max(1,g.length),maxLines:o,overlapLines:i}))}return l}chunkRange(e){let{projectId:t,filePath:r,lines:s,symbol:o,maxLines:i}=e,a=Math.min(e.overlapLines,Math.max(0,i-1)),l=Math.max(1,e.startLine),c=Math.min(s.length,Math.max(l,e.endLine)),p=[],u=l,g=0;for(;u<=c;){let x=Math.min(c,u+i-1),y=s.slice(u-1,x).join(`
5
+ `).trim();if(y){let ce=this.sanitizer.sanitize(y).text,le=Ge(ce),He=[t,r,o?.id??"file",String(g),le].join(":");p.push({id:Ge(He).slice(0,32),projectId:t,filePath:r,symbolId:o?.id,symbolName:o?.qualifiedName??o?.name,symbolType:o?.type,startLine:u,endLine:x,content:ce,contentHash:le})}if(x>=c)break;u=x-a+1,g++}return p}};import{DatabaseSync as ir}from"node:sqlite";var J=class{constructor(e){this.projectId=e;this.db.exec("CREATE VIRTUAL TABLE code_fts USING fts5(id UNINDEXED, projectId UNINDEXED, filePath, symbol, content, tokenize='unicode61')")}projectId;db=new ir(":memory:");chunks=new Map;get size(){return this.chunks.size}build(e){if(this.db.exec("DELETE FROM code_fts"),this.chunks.clear(),e.length===0)return 0;let t=this.db.prepare("INSERT INTO code_fts (id, projectId, filePath, symbol, content) VALUES (?, ?, ?, ?, ?)");this.db.exec("BEGIN");try{for(let r of e)this.chunks.set(r.id,r),t.run(r.id,this.projectId,r.filePath,r.symbolName??"",r.content);this.db.exec("COMMIT")}catch(r){throw this.db.exec("ROLLBACK"),r}return e.length}search(e,t=8){let r=this.toMatchQuery(e);if(!r)return[];let s=this.db.prepare("SELECT id, bm25(code_fts) AS rank FROM code_fts WHERE code_fts MATCH ? ORDER BY rank LIMIT ?").all(r,Math.max(t*4,20)),o=[];for(let i of s){let a=this.chunks.get(i.id);if(!a)continue;let l=1/(1+Math.abs(i.rank));o.push({chunk:a,score:l})}return o.slice(0,t)}toMatchQuery(e){let t=e.toLowerCase().normalize("NFKC").split(/[^\p{L}\p{N}]+/u).map(r=>r.trim()).filter(r=>r.length>0);return t.length===0?null:t.length===1?`"${t[0]}"`:`(${t.map(r=>`"${r}"`).join(" OR ")})`}close(){this.db.close()}};function Ke(n,e){let t=n.toLowerCase().replaceAll("\\","/"),r=e.toLowerCase(),s=/\b(test|tests|spec|fixture|mock)\b/.test(r);return(t.startsWith("tests/")||t.includes("/tests/")||t.includes("__tests__")||/\.(test|spec)\.[^.]+$/.test(t))&&!s?.35:t.includes("/generated/")||t.includes(".generated.")?.25:1}var D="sqlite-fts5-bm25",Q="lexical",ar="Local Code Search \u2014 SQLite FTS5/BM25",Be={engine:D,mode:Q,displayName:ar,semantic:!1,embedding:!1,vectorDatabase:!1,queryExpansion:!1,synonymExpansion:!1,networkRequired:!1,indexBackend:"sqlite-fts5",ranking:"bm25",compatibilityAliases:["SemanticCodeEngine","semantic_code_search","toolnet-memory semantic","semantic-index"]};var Z=class{constructor(e){this.options=e;this.fts=new J(e.projectId)}options;fts;get searchContract(){return Be}async initialize(e){let{projectId:t,rootPath:r,storage:s,graph:o}=this.options,i=new W(s),a=await new q().build(t,r,o.allSymbols(t)),l=await i.load(t),c=!l||l.chunks.length!==a.length||l.chunks.some(u=>!a.some(g=>g.id===u.id));c&&await i.save({version:1,projectId:t,updatedAt:new Date().toISOString(),chunks:a}),e?.({current:a.length,total:a.length,phase:"index",detail:D});let p=this.fts.build(a);return{chunks:a.length,documentsIndexed:p,indexSize:this.fts.size,engine:D,mode:Q,embedding:!1,vectorDatabase:!1,savedChunks:c,vectorsLoaded:0,vectorsIndexed:p,vectorsRemoved:0,totalVectors:this.fts.size,savedVectors:c}}async search(e,t=8){let r=this.fts.search(e,t),s=[];for(let o of r){let i=o.chunk,a=Ke(i.filePath,e),l=o.score,c=0,p=l*a;s.push({chunk:i,score:p,lexicalScore:l,vectorScore:c,engine:D,mode:Q})}return s}size(){return this.fts.size}};import*as h from"typescript";import*as d from"typescript";var Pi=new Set([d.SyntaxKind.EqualsToken,d.SyntaxKind.PlusEqualsToken,d.SyntaxKind.MinusEqualsToken,d.SyntaxKind.AsteriskEqualsToken,d.SyntaxKind.SlashEqualsToken,d.SyntaxKind.PercentEqualsToken,d.SyntaxKind.QuestionQuestionEqualsToken,d.SyntaxKind.AmpersandAmpersandEqualsToken,d.SyntaxKind.BarBarEqualsToken]);import{spawnSync as lr}from"node:child_process";var dr=[{language:"python",command:"pyright-langserver"},{language:"go",command:"gopls"},{language:"rust",command:"rust-analyzer"},{language:"c",command:"clangd"},{language:"cpp",command:"clangd"}];function pr(n){return n.split(/\r?\n/u).map(t=>t.trim()).find(Boolean)||void 0}function ur(n,e){let t=lr(e,["--version"],{encoding:"utf8",timeout:2e3,windowsHide:!0}),r=!t.error&&t.status===0,s=[t.stdout??"",t.stderr??""].join(`
6
+ `);return{language:n,command:e,installed:r,...r?{version:pr(s)}:{},mode:"external-optional",activeInStructuralGraph:!1}}function ze(){return dr.map(n=>ur(n.language,n.command))}var fr=process.argv.includes("--json"),Ue=ze(),mr={structuralParser:{engine:"typescript-compiler-api",languages:V.filter(n=>n.structural).map(n=>n.language)},lexicalSearch:{engine:"sqlite-fts5-bm25",languages:V.filter(n=>n.lexicalSearch).map(n=>n.language)},lspFoundation:{mode:"detection-only",automaticDownload:!1,structuralGraphIntegration:!1,servers:Ue}};if(fr)console.log(JSON.stringify(mr,null,2));else{console.log("ToolNet Code Intelligence Capabilities"),console.log(""),console.log("Structural parser: TypeScript Compiler API");for(let n of V)console.log([n.language.padEnd(12),n.structural?"structural=yes":"structural=no",n.lexicalSearch?"lexical=yes":"lexical=no",n.lspServer?`lsp=${n.lspServer}`:""].filter(Boolean).join(" "));console.log(""),console.log("LSP detection:");for(let n of Ue)console.log([n.language.padEnd(12),n.command.padEnd(22),n.installed?"available":"not-installed"].join(" "))}async function gr(){let n=de(),e=new z().detect(),t=je({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),r=new Y(t,e.id,e.name,e.remote??e.name),s=new j,o=await new O(r).load(e.id);if(!o)throw new Error("Code graph missing. Run npm run code:index:test first.");s.import(o.symbols,o.edges);let i=new Z({projectId:e.id,rootPath:e.rootPath,storage:r,graph:s}),a=await i.initialize(),l=process.argv.slice(2).join(" ")||"memory storage hugging face",c=await i.search(l,5);console.log({ok:!0,command:"semantic",legacyCommandName:!0,search:{engine:"sqlite-fts5-bm25",mode:"lexical",semantic:!1,embedding:!1,vectorDatabase:!1},query:l,...a,results:c.map(p=>({score:Number(p.score.toFixed(4)),file:p.chunk.filePath,symbol:p.chunk.symbolName,lines:`${p.chunk.startLine}-${p.chunk.endLine}`,preview:p.chunk.content.replace(/\s+/g," ").slice(0,140)}))})}gr().catch(n=>{console.error(n),process.exit(1)});
package/bundle/service.js CHANGED
@@ -1,6 +1,11 @@
1
- import{chmodSync as je,existsSync as M,mkdirSync as Pe,unlinkSync as D}from"node:fs";import{createConnection as we,createServer as xe}from"node:net";import{dirname as ke,resolve as Te}from"node:path";import{pathToFileURL as be}from"node:url";import{existsSync as B,readFileSync as H}from"node:fs";import{homedir as J}from"node:os";import{join as V}from"node:path";function L(o){let e=o.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function z(){let o=process.env.TOOLNET_GLOBAL_ENV??V(J(),".config","toolnet-memory",".env");if(!B(o))return;let e=H(o,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let s=r.indexOf("=");if(s<=0)continue;let n=r.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=L(r.slice(s+1)))}}z();function m(o,e){return o===void 0?e:["1","true","yes","on"].includes(o.toLowerCase())}function y(o,e){if(!o)return e;let t=Number(o);return Number.isFinite(t)?t:e}function C(){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:y(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:y(process.env.MEMORY_RERANK_TOP,10),finalContext:y(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:y(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:y(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{homedir as me}from"node:os";import{join as ye}from"node:path";import{DeleteObjectCommand as F,GetObjectCommand as Y,HeadObjectCommand as G,ListObjectsV2Command as W,PutObjectCommand as q,S3Client as Q}from"@aws-sdk/client-s3";import{getSignedUrl as Z}from"@aws-sdk/s3-request-presigner";var v=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Q({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new q({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await Z(this.client,new Y({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new G({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new F({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new W({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};import{access as O,mkdir as X,readFile as ee,readdir as te,rm as re,stat as E,writeFile as oe}from"node:fs/promises";import{dirname as se,join as ne,relative as A,resolve as ie}from"node:path";var f=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return ie(this.root,t)}async put(e,t){let r=this.path(e);await X(se(r),{recursive:!0}),await oe(r,t)}async get(e){try{return await ee(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await O(this.path(e)),!0}catch{return!1}}async delete(e){await re(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await O(t)}catch{return r}let s=async i=>{let c=await te(i,{withFileTypes:!0});for(let g of c){let l=ne(i,g.name);if(g.isDirectory()){await s(l);continue}let a=await E(l);r.push({key:A(this.root,l),size:a.size,updatedAt:a.mtime.toISOString()})}},n=await E(t);return n.isDirectory()?await s(t):r.push({key:A(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),r}};import{DeleteObjectCommand as ae,GetObjectCommand as ce,HeadObjectCommand as pe,ListObjectsV2Command as ue,PutObjectCommand as le,S3Client as de}from"@aws-sdk/client-s3";import{getSignedUrl as ge}from"@aws-sdk/s3-request-presigner";var h=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new de({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new le({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await ge(this.client,new ce({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new pe({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new ae({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new ue({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};function T(o,e){return console.warn(e),new f(o)}function R(o){let e=o.localRoot??ye(me(),".toolnet-memory","storage");if(o.provider==="r2"){let t=o.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new h({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):T(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(o.provider==="s3"){let t=o.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new h({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):T(e,"[storage] S3 credentials missing. Using local fallback.")}if(o.provider==="huggingface"){let t=o.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new v({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):T(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new f(e)}var j=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"memories","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return[];let r=JSON.parse(t);if(!Array.isArray(r))throw new Error("Invalid memory snapshot");return r}async save(e,t){let r=JSON.stringify(t,null,2);await this.storage.put(this.key(e),r,"application/json")}};var P=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function fe(o){return new Promise(e=>setTimeout(e,o))}async function N(o,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,s=e.maxDelayMs??2e3,n;for(let i=1;i<=t;i++)try{return await o()}catch(c){if(n=c,i>=t)break;let g=Math.min(s,r*2**(i-1)),l=Math.floor(Math.random()*Math.max(1,g*.2));await fe(g+l)}throw n}var he=new Set(["put","get","getText","delete","list"]);function I(o,e={}){return new Proxy(o,{get(t,r){let s=Reflect.get(t,r,t);return typeof s!="function"?s:he.has(r)?(...n)=>N(()=>Promise.resolve(s.apply(t,n)),e):s.bind(t)}})}function _(o){let e=o.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function K(o){let e=o.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var w=class{constructor(e,t,r,s){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=_(s??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,s=await this.provider.getText(e);if(s){let i;try{i=JSON.parse(s)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
3
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=K(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{homedir as Se}from"node:os";import{join as ve}from"node:path";function $(){return process.env.TOOLNET_SERVICE_SOCKET??ve(Se(),".toolnet","run","service.sock")}function b(o){return o instanceof Error?o.message:String(o)}function Ce(o){return`${o.id}:${o.remote??o.name}`}async function Oe(o){let e=C(),t=R({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),r=I(t,{attempts:Number(process.env.TOOLNET_STORAGE_RETRIES??3)}),s=new w(r,o.id,o.name,o.remote??o.name),[n,i]=await Promise.all([new j(s).load(o.id),new P(s).load(o.id)]);return{memory:n,graph:i}}function Ee(o,e=200){return new Promise(t=>{let r=we({path:o}),s=!1,n=c=>{s||(s=!0,clearTimeout(i),r.destroy(),t(c))},i=setTimeout(()=>{n(!1)},e);r.once("connect",()=>{n(!0)}),r.once("error",()=>{n(!1)})})}async function Ae(o={}){let e=o.socketPath??$(),t=Math.max(1e3,o.cacheTtlMs??Number(process.env.TOOLNET_SERVICE_CACHE_TTL_MS??3e4)),r=o.loader??Oe,s=new Map,n=new Date().toISOString(),i={requests:0,cacheHits:0,cacheMisses:0},c=()=>({pid:process.pid,startedAt:n,requests:i.requests,cacheHits:i.cacheHits,cacheMisses:i.cacheMisses,cacheEntries:s.size}),g=async a=>{if(i.requests+=1,a.type==="ping")return{ok:!0,type:"ping",stats:c()};let p=Ce(a.project);if(a.type==="invalidate")return{ok:!0,type:"invalidate",removed:s.delete(p)};let d=s.get(p);if(d&&d.expiresAt>Date.now())return i.cacheHits+=1,{ok:!0,type:"hydrate",cacheHit:!0,loadedAt:d.loadedAt,memory:d.data.memory,graph:d.data.graph};i.cacheMisses+=1;try{let u=await r(a.project),S=new Date().toISOString();return s.set(p,{expiresAt:Date.now()+t,loadedAt:S,data:u}),{ok:!0,type:"hydrate",cacheHit:!1,loadedAt:S,memory:u.memory,graph:u.graph}}catch(u){return{ok:!1,error:b(u)}}};if(Pe(ke(e),{recursive:!0,mode:448}),M(e)){if(await Ee(e))throw new Error(`ToolNet service already running at ${e}`);D(e)}let l=xe(a=>{a.setEncoding("utf8");let p="";a.on("data",d=>{p+=d;let u=p.indexOf(`
4
- `);if(u===-1)return;let S=p.slice(0,u).trim();p=p.slice(u+1),(async()=>{try{let x=JSON.parse(S),k=await g(x);a.end(`${JSON.stringify(k)}
5
- `)}catch(x){let k={ok:!1,error:b(x)};a.end(`${JSON.stringify(k)}
6
- `)}})()})});return await new Promise((a,p)=>{let d=u=>{p(u)};l.once("error",d),l.listen(e,()=>{l.off("error",d),a()})}),je(e,384),{socketPath:e,stats:c,close:async()=>{await new Promise(a=>{l.close(()=>{a()})}),M(e)&&D(e)}}}async function Re(){let o=await Ae();console.log(`[toolnet-memory] service ready: ${o.socketPath}`);let e=()=>{o.close().finally(()=>process.exit(0))};process.once("SIGINT",e),process.once("SIGTERM",e)}var U=process.argv[1];U&&import.meta.url===be(Te(U)).href&&Re().catch(o=>{console.error(`[toolnet-memory] service failed: ${b(o)}`),process.exit(1)});export{Oe as loadProjectForService,Ae as startToolNetService};
1
+ import{chmodSync as Oo,existsSync as jt,mkdirSync as Io,unlinkSync as Rt}from"node:fs";import{createConnection as Mo,createServer as No}from"node:net";import{dirname as Co,resolve as Do}from"node:path";import{pathToFileURL as Go}from"node:url";import{existsSync as kt,readFileSync as _t}from"node:fs";import{homedir as Ot}from"node:os";import{join as It}from"node:path";function Mt(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Nt(){let r=process.env.TOOLNET_GLOBAL_ENV??It(Ot(),".config","toolnet-memory",".env");if(!kt(r))return;let e=_t(r,"utf8");for(let t of e.split(/\r?\n/)){let o=t.trim();if(!o||o.startsWith("#"))continue;o.startsWith("export ")&&(o=o.slice(7));let n=o.indexOf("=");if(n<=0)continue;let s=o.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=Mt(o.slice(n+1)))}}Nt();function x(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function b(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function N(){return{memory:{autoCapture:x(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:x(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:x(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:x(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:b(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:b(process.env.MEMORY_RERANK_TOP,10),finalContext:b(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:b(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:x(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:b(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{homedir as lr}from"node:os";import{join as pr}from"node:path";import{DeleteObjectCommand as Ct,GetObjectCommand as Dt,HeadObjectCommand as Gt,ListObjectsV2Command as Lt,PutObjectCommand as $t,S3Client as Kt}from"@aws-sdk/client-s3";import{getSignedUrl as Ut}from"@aws-sdk/s3-request-presigner";var C=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Kt({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,o="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new $t({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await Ut(this.client,new Dt({Bucket:this.bucket,Key:e}),{expiresIn:60}),o=await fetch(t,{redirect:"follow"});if(o.status===404)return null;if(!o.ok)throw new Error(`HF download failed: ${o.status} ${o.statusText}`);return new Uint8Array(await o.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Gt({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Ct({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new Lt({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return t}};import{access as me,mkdir as Ht,readFile as Vt,readdir as Yt,rm as Ft,stat as ge,writeFile as zt}from"node:fs/promises";import{dirname as Bt,join as Jt,relative as ye,resolve as qt}from"node:path";var j=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return qt(this.root,t)}async put(e,t){let o=this.path(e);await Ht(Bt(o),{recursive:!0}),await zt(o,t)}async get(e){try{return await Vt(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await me(this.path(e)),!0}catch{return!1}}async delete(e){await Ft(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await me(t)}catch{return o}let n=async i=>{let a=await Yt(i,{withFileTypes:!0});for(let u of a){let c=Jt(i,u.name);if(u.isDirectory()){await n(c);continue}let d=await ge(c);o.push({key:ye(this.root,c),size:d.size,updatedAt:d.mtime.toISOString()})}},s=await ge(t);return s.isDirectory()?await n(t):o.push({key:ye(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),o}};import{DeleteObjectCommand as Wt,GetObjectCommand as Zt,HeadObjectCommand as Qt,ListObjectsV2Command as Xt,PutObjectCommand as er,S3Client as tr}from"@aws-sdk/client-s3";import{getSignedUrl as rr}from"@aws-sdk/s3-request-presigner";var R=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new tr({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,o="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new er({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await rr(this.client,new Zt({Bucket:this.bucket,Key:e}),{expiresIn:60}),o=await fetch(t,{redirect:"follow"});if(o.status===404)return null;if(!o.ok)throw new Error(`${this.name} download failed: ${o.status} ${o.statusText}`);return new Uint8Array(await o.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Qt({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Wt({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new Xt({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return t}};import{createCipheriv as or,createDecipheriv as nr,createHash as sr,randomBytes as ir,timingSafeEqual as ar}from"node:crypto";import{readFileSync as cr}from"node:fs";var h=Buffer.from("TNMEME01","ascii"),Se=1,A=8,k=12,Q=16,Ee=h.length+1+A+k+Q,ur="toolnet-memory:remote-encryption:v1:",Pe="aes-256-gcm",X=32,f=class extends Error{constructor(t,o){super(o);this.code=t;this.name="RemoteEncryptionError"}code};function dr(r){return r?["1","true","yes","on","enabled"].includes(r.trim().toLowerCase()):!1}function ee(r=process.env){return dr(r.TOOLNET_REMOTE_ENCRYPTION)}function he(r){let e=r.trim();if(!e)throw new f("REMOTE_ENCRYPTION_KEY_EMPTY","Remote encryption key is empty.");let t;if(e.startsWith("hex:")){let o=e.slice(4);if(!/^[0-9a-f]{64}$/iu.test(o))throw new f("REMOTE_ENCRYPTION_KEY_INVALID","hex: remote encryption key must contain exactly 64 hexadecimal characters.");t=Buffer.from(o,"hex")}else if(/^[0-9a-f]{64}$/iu.test(e))t=Buffer.from(e,"hex");else{let o=e.startsWith("base64:")?e.slice(7):e;if(!/^[A-Za-z0-9+/_-]+={0,2}$/u.test(o))throw new f("REMOTE_ENCRYPTION_KEY_INVALID","Remote encryption key must be 32 raw bytes encoded as hexadecimal or base64.");t=Buffer.from(o,o.includes("-")||o.includes("_")?"base64url":"base64")}if(t.length!==X)throw new f("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${X} bytes.`);return t}function we(r=process.env){let e=r.TOOLNET_REMOTE_ENCRYPTION_KEY?.trim(),t=r.TOOLNET_REMOTE_ENCRYPTION_KEY_FILE?.trim();if(e&&t)throw new f("REMOTE_ENCRYPTION_KEY_AMBIGUOUS","Configure either TOOLNET_REMOTE_ENCRYPTION_KEY or TOOLNET_REMOTE_ENCRYPTION_KEY_FILE, not both.");if(e)return he(e);if(t){let o;try{o=cr(t,"utf8")}catch(n){throw new f("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${t}.`,n instanceof Error?n.message:String(n)].join(" "))}return he(o)}}function ve(r){return sr("sha256").update(r).digest().subarray(0,A)}function Te(r){return Buffer.from(`${ur}${r}`,"utf8")}function te(r){return r.byteLength<h.length?!1:Buffer.from(r).subarray(0,h.length).equals(h)}function xe(r,e,t){if(t.byteLength!==X)throw new f("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH","AES-256-GCM requires a 32-byte key.");let o=typeof e=="string"?Buffer.from(e,"utf8"):Buffer.from(e),n=ir(k),s=or(Pe,t,n);s.setAAD(Te(r));let i=Buffer.concat([s.update(o),s.final()]),a=s.getAuthTag(),u=Buffer.alloc(Ee),c=0;return h.copy(u,c),c+=h.length,u.writeUInt8(Se,c),c+=1,ve(t).copy(u,c),c+=A,n.copy(u,c),c+=k,a.copy(u,c),Buffer.concat([u,i])}function be(r,e,t){let o=Buffer.from(e);if(!te(o))throw new f("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(o.length<Ee)throw new f("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let n=h.length,s=o.readUInt8(n);if(n+=1,s!==Se)throw new f("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${s}.`);let i=o.subarray(n,n+A);n+=A;let a=ve(t);if(!ar(i,a))throw new f("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let u=o.subarray(n,n+k);n+=k;let c=o.subarray(n,n+Q);n+=Q;let d=o.subarray(n),l=nr(Pe,t,u);l.setAAD(Te(r)),l.setAuthTag(c);try{return Buffer.concat([l.update(d),l.final()])}catch{throw new f("REMOTE_ENCRYPTION_AUTH_FAILED","Encrypted remote object failed AES-GCM authentication.")}}var re=class{constructor(e,t){this.inner=e;this.options=t;if(this.name=e.name,t.enabled&&!t.key)throw new f("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote client-side encryption is enabled but no encryption key is configured.")}inner;options;name;async put(e,t,o){if(!this.options.enabled){await this.inner.put(e,t,o);return}let n=this.options.key;if(!n)throw new f("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let s=xe(e,t,n);await this.inner.put(e,s,"application/octet-stream")}async get(e){let t=await this.inner.get(e);if(!t)return null;if(!te(t))return t;if(!this.options.enabled)throw new f("REMOTE_ENCRYPTION_REQUIRED",["Remote object is client-side encrypted.","Enable TOOLNET_REMOTE_ENCRYPTION and configure the matching key."].join(" "));let o=this.options.key;if(!o)throw new f("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return be(e,t,o)}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){return this.inner.exists(e)}async delete(e){await this.inner.delete(e)}async list(e=""){return this.inner.list(e)}};function je(r,e=process.env){if(r.name==="local")return ee(e)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),r;let t=ee(e),o=t?we(e):void 0;return new re(r,{enabled:t,key:o})}function _(r){return je(r)}function oe(r,e){return console.warn(e),_(new j(r))}function D(r){let e=r.localRoot??pr(lr(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?_(new R({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):oe(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?_(new R({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):oe(e,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?_(new C({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):oe(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return _(new j(e))}var G=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"memories","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return[];let o=JSON.parse(t);if(!Array.isArray(o))throw new Error("Invalid memory snapshot");return o}async save(e,t){let o=JSON.stringify(t,null,2);await this.storage.put(this.key(e),o,"application/json")}};var L=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function fr(r){return new Promise(e=>setTimeout(e,r))}async function Re(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,n=e.maxDelayMs??2e3,s;for(let i=1;i<=t;i++)try{return await r()}catch(a){if(s=a,i>=t)break;let u=Math.min(n,o*2**(i-1)),c=Math.floor(Math.random()*Math.max(1,u*.2));await fr(u+c)}throw s}var mr=new Set(["put","get","getText","delete","list"]);function $(r,e={}){return new Proxy(r,{get(t,o){let n=Reflect.get(t,o,t);return typeof n!="function"?n:mr.has(o)?(...s)=>Re(()=>Promise.resolve(n.apply(t,s)),e):n.bind(t)}})}function Ae(r){let e=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function ke(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(o)return`${o[1]}memory/records/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),o)return`${o[1]}memory/vectors/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),o)return`${o[1]}code/graph/${o[2]}`;let n=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:e))}var P=class{constructor(e,t,o,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=o,this.folder=Ae(n??o),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),o=t,n=await this.provider.getText(e);if(n){let i;try{i=JSON.parse(n)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(o=i.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:o,updatedAt:t};await this.provider.put(e,JSON.stringify(s,null,2)+`
3
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=ke(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,o){return await this.ensureRegistered(),this.provider.put(this.key(e),t,o)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{homedir as gr}from"node:os";import{join as yr}from"node:path";function _e(){return process.env.TOOLNET_SERVICE_SOCKET??yr(gr(),".toolnet","run","service.sock")}import{closeSync as ho,existsSync as pe,mkdirSync as gt,openSync as So,readFileSync as yt,renameSync as Eo,statSync as Po,unlinkSync as ht,writeFileSync as St}from"node:fs";import{randomUUID as Et}from"node:crypto";import{dirname as Pt,join as wt}from"node:path";import{createHash as Tr}from"node:crypto";import{existsSync as H,mkdirSync as xr,readFileSync as br,renameSync as jr,writeFileSync as Rr}from"node:fs";import{basename as Ar,dirname as V,join as I,parse as Le,resolve as E}from"node:path";import{createHash as Me}from"node:crypto";import{spawnSync as hr}from"node:child_process";var O="git-remote-v1",Sr=new Set(["github.com","gitlab.com","bitbucket.org"]);function Ne(r,e){let t=e.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!t||t==="."||t===".."||t.split("/").some(o=>!o||o==="."||o==="..")?null:(Sr.has(r)&&(t=t.toLowerCase()),t)}function Er(r){let e;try{e=new URL(r)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(e.protocol))return null;let t=e.hostname.trim().toLowerCase();if(!t)return null;let o=e.protocol==="https:"&&e.port==="443"||e.protocol==="http:"&&e.port==="80"||e.protocol==="ssh:"&&e.port==="22",n=e.port&&!o?`${t}:${e.port}`:t,s=Ne(t,e.pathname);return s?`${n}/${s}`:null}function Pr(r){let e=r.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!e)return null;let t=e[1]?.trim().toLowerCase();if(!t||t.length===1)return null;let o=Ne(t,e[2]??"");return o?`${t}/${o}`:null}function Oe(r){let e=r.trim();return e?e.includes("://")?Er(e):Pr(e):null}function wr(r){return Me("sha256").update(`${O}:${r}`).digest("hex")}function Ce(r){return Me("sha256").update(`toolnet-project:${O}:${r}`).digest("hex").slice(0,16)}function vr(r){return r.split("/").filter(Boolean).at(-1)?.trim()||null}function ne(r,e){let t=hr("git",["-C",r,...e],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return t.error||t.status!==0?null:t.stdout?.trim()||null}function Ie(r,e){let t=vr(r);return t?{scheme:O,canonicalRemote:r,fingerprint:wr(r),repositoryName:t,source:e}:null}function De(r){let e=ne(r,["remote","get-url","origin"]);if(e){let n=Oe(e);if(n)return Ie(n,"origin")}let t=ne(r,["remote"]);if(!t)return null;let o=new Set;for(let n of t.split(/\r?\n/u).map(s=>s.trim()).filter(Boolean)){let s=ne(r,["remote","get-url",n]);if(!s)continue;let i=Oe(s);i&&o.add(i)}return o.size!==1?null:Ie([...o][0],"unique-remote")}var $e=".toolnet",kr="project.json";function _r(r){return Tr("sha256").update(r).digest("hex").slice(0,16)}function w(r){return I(r,$e,kr)}function Ke(r){return H(w(r))}function Ge(r,e){let t=E(r),o=Le(t).root;for(;;){if(Ke(t))return t;if(t===o||e&&t===E(e))break;let n=V(t);if(n===t)break;t=n}return null}function se(r){let e=E(r),t=Le(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(s=>H(I(e,s))))return e;if(e===t)break;let n=V(e);if(n===e)break;e=n}return E(r)}function K(r){let e;try{e=JSON.parse(br(r,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${r}: ${n instanceof Error?n.message:String(n)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let o=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:V(V(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:o,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:o,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function U(r,e){let t=I(r,$e);xr(t,{recursive:!0});let o=w(r),n=`${o}.tmp-${process.pid}`;Rr(n,JSON.stringify(e,null,2)+`
4
+ `,{encoding:"utf8",mode:384}),jr(n,o)}function S(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}function ie(r){return{version:1,scheme:O,canonicalRemote:r.canonicalRemote,fingerprint:r.fingerprint,repositoryName:r.repositoryName}}function Or(r){let e=r.metadata?.toolnetIdentity;if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;return typeof t.fingerprint=="string"?t.fingerprint:null}var v=class{adopt(e,t){let o=se(E(e));if(!t.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!t.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!t.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(Ke(o)){let a=K(w(o));if(a.id!==t.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${a.id}`,`requested=${t.id}`].join(" "));return S(a,o)}let n=new Date().toISOString(),s={...t.metadata};t.gitIdentity&&(s.toolnetIdentity=ie(t.gitIdentity));let i={version:1,id:t.id.trim(),name:t.name.trim(),remote:t.remote.trim(),rootPath:o,createdAt:t.createdAt??n,updatedAt:n,graphVersion:t.graphVersion??0,memoryVersion:t.memoryVersion??0,metadata:Object.keys(s).length?s:void 0};return U(o,i),S(i,o)}recordGitIdentity(e,t,o={}){let n=this.requireExisting(e),s=w(n.rootPath),i=K(s),a=Or(i);if(a&&a!==t.fingerprint&&!o.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${a}`,`current=${t.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let u=i.metadata?.toolnetIdentity;return u&&typeof u=="object"&&!Array.isArray(u)&&u.fingerprint===t.fingerprint||(i.metadata={...i.metadata,toolnetIdentity:ie(t)},i.updatedAt=new Date().toISOString(),U(n.rootPath,i)),S(i,n.rootPath)}findExisting(e=process.cwd()){let t=E(e),o=se(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>H(I(o,u))),i=Ge(t,s?o:void 0);if(!i)return null;let a=K(w(i));return S(a,i)}requireExisting(e=process.cwd()){let t=this.findExisting(e);if(!t)throw new Error("PROJECT_NOT_INITIALIZED");return t}detect(e=process.cwd()){let t=E(e),o=se(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(l=>H(I(o,l))),i=Ge(t,s?o:void 0);if(i){let l=w(i),p=K(l);return p.rootPath!==i&&(p.rootPath=i,p.updatedAt=new Date().toISOString(),U(i,p)),S(p,i)}let a=new Date().toISOString(),u=Ar(o),c=De(o),d={version:1,id:c?Ce(c.canonicalRemote):_r(o),name:u,remote:c?.repositoryName??u,rootPath:o,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0,metadata:c?{toolnetIdentity:ie(c)}:void 0};return U(o,d),S(d,o)}};import{appendFileSync as $r,closeSync as Kr,existsSync as Ve,mkdirSync as Ye,openSync as Ur,readFileSync as Fe,statSync as Hr,unlinkSync as ze,writeFileSync as Vr}from"node:fs";import{createHash as Yr,randomUUID as Be}from"node:crypto";import{dirname as Je,join as ue}from"node:path";var Ir=[{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"}],Mr=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function Ue(r){return r.normalize("NFKC").trim().toLowerCase()}function Nr(r){if(r.length===0)return 0;let e=new Map;for(let o of r)e.set(o,(e.get(o)??0)+1);let t=0;for(let o of e.values()){let n=o/r.length;t-=n*Math.log2(n)}return t}function Cr(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function Dr(r,e,t){let o=r.slice(Math.max(0,e-48),e),n=r.slice(t,Math.min(r.length,t+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${o} ${n}`)}function Gr(r,e){return r.start<e.end&&e.start<r.end}function He(r){return r.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var Y=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let o=Ue(t);o&&this.allowValues.add(o)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let s of Ir){let i=new RegExp(s.regex.source,s.regex.flags);for(let a of e.matchAll(i))a.index===void 0||!a[0]||this.allowed(a[0])||t.push({type:s.type,value:a[0],start:a.index,end:a.index+a[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let o=He(t),n=[];for(let s of o)n.some(i=>Gr(i,s))||n.push(s);return He(n)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=Ue(e);return Mr.has(t)?!0:this.allowValues.has(t)}entropyMatches(e){let t=[],o=/[A-Za-z0-9_+/=-]{32,160}/g;for(let n of e.matchAll(o)){if(n.index===void 0||!n[0])continue;let s=n[0];this.allowed(s)||Cr(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||Dr(e,n.index,n.index+s.length)&&(Nr(s)<3.7||t.push({type:"high_entropy_secret",value:s,start:n.index,end:n.index+s.length,confidence:"heuristic"}))}return t}};var F=class{scanner;constructor(e={}){this.scanner=new Y(e)}sanitize(e){let t=this.scanner.scan(e);if(t.length===0)return{text:e,redacted:0,secretTypes:[]};let o=e,n=[...t].sort((i,a)=>a.start-i.start),s=new Set;for(let i of n)s.add(i.type),o=o.slice(0,i.start)+`[REDACTED:${i.type}]`+o.slice(i.end);return{text:o,redacted:t.length,secretTypes:[...s].sort()}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(t=>this.sanitizeValue(t));if(e&&typeof e=="object"){let t={};for(let[o,n]of Object.entries(e)){let s=o.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")){t[o]="[REDACTED]";continue}t[o]=this.sanitizeValue(n)}return t}return e}};var Lr=new F;function z(r){return Lr.sanitizeValue(r)}var ae="0".repeat(64),Fr=3e4,zr=100,Br=20;function Jr(r){return new Promise(e=>{setTimeout(e,r)})}function qr(r){return ue(r.rootPath,".toolnet","audit")}function qe(r){return ue(qr(r),"events.jsonl")}function We(r){return ue(r.rootPath,".toolnet","runtime","locks","audit.lock")}function ce(r){if(Array.isArray(r))return r.map(ce);if(!r||typeof r!="object")return r;let e=r;return Object.fromEntries(Object.keys(e).sort().map(t=>[t,ce(e[t])]))}function Wr(r){return JSON.stringify(ce(r))}function Ze(r){return Yr("sha256").update(Wr(r),"utf8").digest("hex")}function Zr(){let r=process.env.TOOLNET_AGENT_ID?.trim()||process.env.TOOLNET_AGENT?.trim();return r?{kind:"agent",id:r}:{kind:"system",id:`pid:${process.pid}`}}function Qe(r){let e=qe(r);if(!Ve(e))return[];let t=Fe(e,"utf8"),o=[];for(let n of t.split(/\r?\n/u)){let s=n.trim();s&&o.push(JSON.parse(s))}return o}function Qr(r){try{return Date.now()-Hr(r).mtimeMs>Fr}catch{return!0}}async function Xr(r){let e=We(r);Ye(Je(e),{recursive:!0,mode:448});for(let t=0;t<zr;t+=1){let o=Be();try{let n=Ur(e,"wx",384);try{Vr(n,JSON.stringify({token:o,pid:process.pid,createdAt:new Date().toISOString()})+`
5
+ `)}finally{Kr(n)}return o}catch(n){if(n.code!=="EEXIST")throw n;if(Qr(e)){try{ze(e)}catch{}continue}await Jr(Br)}}throw new Error("AUDIT_LOCK_TIMEOUT")}function eo(r,e){let t=We(r);if(Ve(t))try{if(JSON.parse(Fe(t,"utf8")).token!==e)return;ze(t)}catch{}}function to(r){return Qe(r).at(-1)}async function ro(r,e){let t=await Xr(r);try{let o=to(r);if(o){let d=oo(r);if(!d.valid)throw new Error(["AUDIT_LOG_INTEGRITY_FAILED",d.error??""].filter(Boolean).join(" "))}let n=e.details?z(e.details):void 0,s=e.target?z(e.target):void 0,i=z(e.actor??Zr()),a={version:1,sequence:(o?.sequence??0)+1,id:Be(),at:e.at??new Date().toISOString(),projectId:r.id,action:e.action,outcome:e.outcome,actor:i,...s?{target:s}:{},...n?{details:n}:{},previousHash:o?.hash??ae},u={...a,hash:Ze(a)},c=qe(r);return Ye(Je(c),{recursive:!0,mode:448}),$r(c,JSON.stringify(u)+`
6
+ `,{encoding:"utf8",mode:384}),u}finally{eo(r,t)}}async function de(r,e){try{return await ro(r,e),!0}catch(t){return console.warn(`[audit] ${t instanceof Error?t.message:String(t)}`),!1}}function oo(r){let e;try{e=Qe(r)}catch(n){return{valid:!1,records:0,lastHash:ae,firstInvalidLine:1,error:n instanceof Error?n.message:String(n)}}let t=ae,o=1;for(let n=0;n<e.length;n+=1){let s=e[n];if(s.sequence!==o)return{valid:!1,records:e.length,lastHash:t,firstInvalidLine:n+1,error:"AUDIT_SEQUENCE_INVALID"};if(s.previousHash!==t)return{valid:!1,records:e.length,lastHash:t,firstInvalidLine:n+1,error:"AUDIT_PREVIOUS_HASH_INVALID"};let{hash:i,...a}=s,u=Ze(a);if(i!==u)return{valid:!1,records:e.length,lastHash:t,firstInvalidLine:n+1,error:"AUDIT_HASH_INVALID"};t=i,o+=1}return{valid:!0,records:e.length,lastHash:t}}var no={keepSnapshots:10,runtimeDays:30,staleLockMinutes:10};function B(r,e,t){if(!Number.isSafeInteger(r)||r<e)throw new Error(`${t} must be an integer >= ${e}`);return r}function so(r){let e=B(r.keepSnapshots,1,"keepSnapshots"),t=B(r.runtimeDays,1,"runtimeDays"),o=B(r.staleLockMinutes,1,"staleLockMinutes"),n;return r.snapshotMaxAgeDays!==void 0&&(n=B(r.snapshotMaxAgeDays,1,"snapshotMaxAgeDays")),{keepSnapshots:e,runtimeDays:t,staleLockMinutes:o,...n!==void 0?{snapshotMaxAgeDays:n}:{}}}function Xe(r={}){return so({...no,...r})}var io=["memories/current.json","vectors/current.json","graph/current.json","graph/manifest.json","graph/resolution/current.json","code/chunks/current.json","code/vectors/current.json","code/architecture/current.json","code/architecture/entry-points.json","code/architecture/hotspots.json","code/architecture/layers.json","code/architecture/clusters.json","code/analysis/current.json","code/analysis/dead-code.json","code/analysis/dependencies.json","code/visualization/graph.json"],J=class{constructor(e){this.storage=e}storage;projectPrefix(e){return`projects/${e}`}snapshotPrefix(e,t){return[this.projectPrefix(e),"snapshots",t].join("/")}async create(e,t="automatic",o){let n=new Date().toISOString(),s=n.replace(/[:.]/g,"-"),i=this.projectPrefix(e),a=this.snapshotPrefix(e,s),u=[];for(let d of io){let l=`${i}/${d}`,p=await this.storage.get(l);p&&(await this.storage.put(`${a}/${d}`,p,"application/json"),u.push(d))}if(u.length===0)return null;let c={version:1,id:s,projectId:e,createdAt:n,reason:t,objects:u,metadata:o};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(c,null,2),"application/json"),c}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),n=(await this.storage.list(t)).filter(i=>i.key.endsWith("/snapshot.json")),s=[];for(let i of n){let a=await this.storage.getText(i.key);if(a)try{s.push(JSON.parse(a))}catch{}}return s.sort((i,a)=>a.createdAt.localeCompare(i.createdAt))}async restore(e,t){let o=this.snapshotPrefix(e,t),n=await this.storage.getText(`${o}/snapshot.json`);if(!n)throw new Error(`Snapshot not found: ${t}`);let s=JSON.parse(n);if(s.projectId!==e)throw new Error("Snapshot project mismatch");let i=this.projectPrefix(e),a=[];for(let u of s.objects){let c=await this.storage.get(`${o}/${u}`);c&&(await this.storage.put(`${i}/${u}`,c,"application/json"),a.push(u))}return{restored:a}}async remove(e,t){let o=this.snapshotPrefix(e,t),n=await this.storage.list(o);for(let s of n)await this.storage.delete(s.key);return n.length}async prune(e,t=10){let n=(await this.list(e)).slice(t),s=0;for(let i of n)await this.remove(e,i.id),s++;return s}};import{existsSync as mo,rmSync as go}from"node:fs";import{existsSync as rt,lstatSync as ot,readFileSync as ao,readdirSync as co}from"node:fs";import{basename as nt,join as g,relative as uo,resolve as et}from"node:path";function le(r,e){return Math.max(0,e-r)}function lo(r){if(!Number.isInteger(r)||r<=0)return!1;try{return process.kill(r,0),!0}catch(e){return e?.code!=="ESRCH"}}function st(r){let e="";try{e=ao(r,"utf8").trim()}catch{return!1}if(!e)return!1;try{let t=JSON.parse(e);return typeof t.pid!="number"?!1:lo(t.pid)}catch{return!1}}function it(r){return r==="journal.lock"||r===".current.lock"||r.endsWith(".lock")}function at(r){return r.startsWith(".tmp-")||r.includes(".tmp-")||r.endsWith(".tmp")||r.endsWith(".partial")}function ct(r){if(!rt(r))return[];let e=[];for(let t of co(r,{withFileTypes:!0})){let o=g(r,t.name);if(!t.isSymbolicLink()){if(t.isDirectory()){e.push(...ct(o));continue}t.isFile()&&e.push(o)}}return e.sort()}function tt(r,e){return[r,e].join(":")}function po(r){return g(r.rootPath,".toolnet","runtime")}function fo(r,e){let t=et(po(r)),o=et(e),n=uo(t,o);return!!n&&n!==".."&&!n.startsWith("../")&&!n.startsWith("..\\")}function ut(r,e,t,o=Date.now()){if(e.scope!=="local")return{safe:!1,detail:"candidate is not local"};if(!fo(r,e.target))return{safe:!1,detail:"target escaped .toolnet/runtime"};if(!rt(e.target))return{safe:!1,detail:"target disappeared"};let n;try{n=ot(e.target)}catch{return{safe:!1,detail:"cannot stat target"}}if(!n.isFile()||n.isSymbolicLink())return{safe:!1,detail:"target is not a regular file"};if(e.expectedMtimeMs!==void 0&&n.mtimeMs!==e.expectedMtimeMs)return{safe:!1,detail:"target changed after planning"};let s=nt(e.target);if(e.category==="runtime-lock"){if(!it(s))return{safe:!1,detail:"lock name no longer matches policy"};let i=t.staleLockMinutes*6e4;return le(n.mtimeMs,o)<i?{safe:!1,detail:"lock is no longer stale"}:st(e.target)?{safe:!1,detail:"lock owner is alive"}:{safe:!0}}if(e.category==="runtime-temp"){if(!at(s))return{safe:!1,detail:"temporary file name no longer matches policy"};let i=t.runtimeDays*864e5;return le(n.mtimeMs,o)<i?{safe:!1,detail:"temporary file is too new"}:{safe:!0}}return{safe:!1,detail:"category is protected from local deletion"}}function dt(r,e,t=Date.now()){let o=g(r.rootPath,".toolnet"),n=g(o,"runtime"),s=[],i=[{scope:"local",category:"protected-memory",target:g(o,"memory"),reason:"durable semantic memory is never age-GCed"},{scope:"local",category:"protected-work",target:g(o,"work"),reason:"current work continuity is authoritative state"},{scope:"local",category:"protected-context",target:g(o,"context"),reason:"context is not deleted by retention policy"},{scope:"local",category:"protected-journal",target:g(o,"journal","events.jsonl"),reason:"shared project journal is durable continuity data"},{scope:"local",category:"protected-audit",target:g(o,"audit","events.jsonl"),reason:"tamper-evident audit history is durable security data"},{scope:"local",category:"protected-legacy-session",target:g(o,"sessions"),reason:"legacy sessions remain read/protected and are never automatically removed"},{scope:"local",category:"protected-wal",target:g(n,"sources"),reason:"events.jsonl and state.json remain authoritative crash-recovery WAL/state"}],a=e.staleLockMinutes*6e4,u=e.runtimeDays*864e5;for(let c of ct(n)){let d;try{d=ot(c)}catch{continue}if(!d.isFile()||d.isSymbolicLink())continue;let l=nt(c),p=le(d.mtimeMs,t);if(it(l)){if(p<a||st(c))continue;s.push({id:tt("runtime-lock",c),scope:"local",category:"runtime-lock",target:c,reason:`stale runtime lock older than ${e.staleLockMinutes} minutes with no live owner`,action:"delete-file",ageMs:p,size:d.size,expectedMtimeMs:d.mtimeMs});continue}l==="events.jsonl"||l==="state.json"||at(l)&&(p<u||s.push({id:tt("runtime-temp",c),scope:"local",category:"runtime-temp",target:c,reason:`temporary runtime artifact older than ${e.runtimeDays} days`,action:"delete-file",ageMs:p,size:d.size,expectedMtimeMs:d.mtimeMs}))}return s.sort((c,d)=>c.target.localeCompare(d.target)),{version:1,generatedAt:new Date(t).toISOString(),projectId:r.id,projectRoot:r.rootPath,policy:e,candidates:s,protected:i,estimatedBytes:s.reduce((c,d)=>c+(d.size??0),0)}}async function lt(r,e,t,o){let n=[],s=0,i=0,a=0,u=0;for(let c of t){if(o.dryRun){n.push({candidate:c,status:"skipped",detail:"dry-run"}),i+=1;continue}if(c.action==="delete-file"){let d=ut(r,c,e);if(!d.safe){n.push({candidate:c,status:"skipped",detail:d.detail??"safety check rejected target"}),i+=1;continue}try{if(!mo(c.target)){n.push({candidate:c,status:"skipped",detail:"target already absent"}),i+=1;continue}go(c.target,{force:!0}),n.push({candidate:c,status:"deleted"}),s+=1,u+=c.size??0}catch(l){n.push({candidate:c,status:"failed",detail:l instanceof Error?l.message:String(l)}),a+=1}continue}if(c.action==="delete-snapshot"){if(c.scope!=="remote"||!c.snapshotId){n.push({candidate:c,status:"skipped",detail:"invalid snapshot candidate"}),i+=1;continue}if(!o.snapshotManager){n.push({candidate:c,status:"skipped",detail:"remote snapshot manager unavailable"}),i+=1;continue}try{await o.snapshotManager.remove(r.id,c.snapshotId),n.push({candidate:c,status:"deleted"}),s+=1}catch(d){n.push({candidate:c,status:"failed",detail:d instanceof Error?d.message:String(d)}),a+=1}continue}n.push({candidate:c,status:"skipped",detail:"unsupported GC action"}),i+=1}return{dryRun:o.dryRun,deleted:s,skipped:i,failed:a,bytesFreed:u,items:n}}function yo(r,e){let t=Date.parse(r);return Number.isFinite(t)?Math.max(0,e-t):null}function pt(r,e,t=Date.now()){let o=[...r].sort((a,u)=>u.createdAt.localeCompare(a.createdAt)),n=[],s=[],i=e.snapshotMaxAgeDays===void 0?void 0:e.snapshotMaxAgeDays*864e5;for(let a=0;a<o.length;a+=1){let u=o[a],c=yo(u.createdAt,t);if(a<e.keepSnapshots){s.push({scope:"remote",category:"snapshot",target:u.id,reason:`one of newest ${e.keepSnapshots} snapshots`});continue}if(c===null){s.push({scope:"remote",category:"snapshot",target:u.id,reason:"snapshot timestamp is invalid; uncertain data is retained"});continue}if(i!==void 0&&c<i){s.push({scope:"remote",category:"snapshot",target:u.id,reason:`snapshot has not reached ${e.snapshotMaxAgeDays} day age guard`});continue}n.push({id:`snapshot:${u.id}`,scope:"remote",category:"snapshot",target:u.id,snapshotId:u.id,reason:e.snapshotMaxAgeDays===void 0?`snapshot exceeds keep limit ${e.keepSnapshots}`:`snapshot exceeds keep limit ${e.keepSnapshots} and age guard ${e.snapshotMaxAgeDays} days`,action:"delete-snapshot",ageMs:c})}return s.push({scope:"remote",category:"protected-remote-operation",target:"projects/<project>/operations/**",reason:"append-only multi-host operations are outside GC authority"}),{candidates:n,protected:s}}var q=class{constructor(e,t,o={}){this.project=e;this.policy=t;o.remoteStorage&&(this.snapshotManager=new J(o.remoteStorage))}project;policy;snapshotManager;async plan(e=!1){let t=dt(this.project,this.policy);if(!e)return t;if(!this.snapshotManager)throw new Error("REMOTE_GC_STORAGE_NOT_CONFIGURED");let o=await this.snapshotManager.list(this.project.id),n=pt(o,this.policy);return{...t,candidates:[...t.candidates,...n.candidates],protected:[...t.protected,...n.protected]}}async execute(e,t){if(e.projectId!==this.project.id)throw new Error("GC_PROJECT_MISMATCH");if(e.projectRoot!==this.project.rootPath)throw new Error("GC_PROJECT_ROOT_MISMATCH");if(!t&&e.candidates.some(o=>o.scope==="remote")){if(!this.snapshotManager)throw new Error("REMOTE_GC_STORAGE_NOT_CONFIGURED");e=await this.plan(!0)}return lt(this.project,this.policy,e.candidates,{dryRun:t,snapshotManager:this.snapshotManager})}};var wo=168,vo=15,To=30*6e4;function ft(r){return r?["1","true","yes","on","enabled"].includes(r.trim().toLowerCase()):!1}function T(r,e){if(!r)return e;let t=Number(r);return!Number.isSafeInteger(t)||t<1?e:t}function vt(r=process.env){let e=T(r.TOOLNET_AUTO_GC_INTERVAL_HOURS,wo),t=T(r.TOOLNET_AUTO_GC_TICK_MINUTES,vo);return{enabled:ft(r.TOOLNET_AUTO_GC),intervalMs:e*60*6e4,tickMs:t*6e4,includeRemote:ft(r.TOOLNET_AUTO_GC_REMOTE),policy:Xe({keepSnapshots:T(r.TOOLNET_AUTO_GC_KEEP_SNAPSHOTS,10),runtimeDays:T(r.TOOLNET_AUTO_GC_RUNTIME_DAYS,30),staleLockMinutes:T(r.TOOLNET_AUTO_GC_STALE_LOCK_MINUTES,10),...r.TOOLNET_AUTO_GC_SNAPSHOT_DAYS?{snapshotMaxAgeDays:T(r.TOOLNET_AUTO_GC_SNAPSHOT_DAYS,30)}:{}})}}function Tt(r){return wt(r.rootPath,".toolnet","runtime","gc","auto-gc-state.json")}function xt(r){return wt(r.rootPath,".toolnet","runtime","gc","auto-gc.lock")}function xo(r){let e=Tt(r);if(pe(e))try{return JSON.parse(yt(e,"utf8"))}catch{return}}function mt(r,e){let t=Tt(r);gt(Pt(t),{recursive:!0,mode:448});let o=`${t}.tmp-${process.pid}-${Et()}`;St(o,JSON.stringify(e,null,2)+`
7
+ `,{encoding:"utf8",mode:384}),Eo(o,t)}function bo(r){try{return Date.now()-Po(r).mtimeMs>To}catch{return!0}}function jo(r){let e=xt(r);if(gt(Pt(e),{recursive:!0,mode:448}),pe(e)&&bo(e))try{ht(e)}catch{}let t=Et();try{let o=So(e,"wx",384);try{St(o,JSON.stringify({token:t,pid:process.pid,createdAt:new Date().toISOString()})+`
8
+ `)}finally{ho(o)}return t}catch(o){if(o.code==="EEXIST")return null;throw o}}function Ro(r,e){let t=xt(r);if(pe(t))try{JSON.parse(yt(t,"utf8")).token===e&&ht(t)}catch{}}function Ao(r,e,t=Date.now()){if(!e.enabled)return!1;let o=xo(r);if(!o?.lastSuccessAt)return!0;let n=Date.parse(o.lastSuccessAt);return Number.isFinite(n)?t-n>=e.intervalMs:!0}async function ko(r){let e=N(),t=$(D({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:Number(process.env.TOOLNET_STORAGE_RETRIES??3)});return new P(t,r.id,r.name,r.remote??r.name)}async function _o(r,e=vt(),t={}){let o=t.now??Date.now();if(!e.enabled)return{status:"disabled"};if(!t.force&&!Ao(r,e,o))return{status:"not-due"};let n=jo(r);if(!n)return{status:"locked"};let s=new Date(o).toISOString();try{let i=e.includeRemote?await ko(r):void 0,a=new q(r,e.policy,{...i?{remoteStorage:i}:{}}),u=await a.plan(e.includeRemote),c=await a.execute(u,!1),d=c.failed===0,l={version:1,projectId:r.id,lastAttemptAt:s,...d?{lastSuccessAt:s,nextDueAt:new Date(o+e.intervalMs).toISOString()}:{},lastResult:{deleted:c.deleted,skipped:c.skipped,failed:c.failed,bytesFreed:c.bytesFreed},...d?{}:{lastError:"GC_EXECUTION_ITEMS_FAILED"}};return mt(r,l),await de(r,{action:"gc.auto",outcome:d?"success":"failed",actor:{kind:"service",id:`pid:${process.pid}`},details:{includeRemote:e.includeRemote,candidates:u.candidates.length,deleted:c.deleted,skipped:c.skipped,failed:c.failed,bytesFreed:c.bytesFreed}}),{status:d?"success":"failed",result:c}}catch(i){let a=i instanceof Error?i.message:String(i);return mt(r,{version:1,projectId:r.id,lastAttemptAt:s,lastError:a}),await de(r,{action:"gc.auto",outcome:"failed",actor:{kind:"service",id:`pid:${process.pid}`},details:{includeRemote:e.includeRemote,error:a}}),{status:"failed",error:a}}finally{Ro(r,n)}}function bt(r=vt()){let e=new Map,t=new Set,o=new v;async function n(a){if(!t.has(a.id)){t.add(a.id);try{await _o(a,r)}finally{t.delete(a.id)}}}function s(a){if(!r.enabled)return;let u;try{u=o.requireExisting(a)}catch{return}e.set(u.id,u),n(u)}let i=r.enabled?setInterval(()=>{for(let a of e.values())n(a)},r.tickMs):void 0;return i?.unref(),{enabled:r.enabled,observeRoot:s,close(){i&&clearInterval(i)}}}function fe(r){return r instanceof Error?r.message:String(r)}function Lo(r){return`${r.id}:${r.remote??r.name}`}async function $o(r){let e=N(),t=D({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),o=$(t,{attempts:Number(process.env.TOOLNET_STORAGE_RETRIES??3)}),n=new P(o,r.id,r.name,r.remote??r.name),[s,i]=await Promise.all([new G(n).load(r.id),new L(n).load(r.id)]);return{memory:s,graph:i}}function Ko(r,e=200){return new Promise(t=>{let o=Mo({path:r}),n=!1,s=a=>{n||(n=!0,clearTimeout(i),o.destroy(),t(a))},i=setTimeout(()=>{s(!1)},e);o.once("connect",()=>{s(!0)}),o.once("error",()=>{s(!1)})})}async function Uo(r={}){let e=r.socketPath??_e(),t=Math.max(1e3,r.cacheTtlMs??Number(process.env.TOOLNET_SERVICE_CACHE_TTL_MS??3e4)),o=r.loader??$o,n=bt();if(n.enabled){let l=new v().findExisting(process.cwd());l&&n.observeRoot(l.rootPath),console.log("[toolnet-memory] auto-GC scheduler enabled")}let s=new Map,i=new Date().toISOString(),a={requests:0,cacheHits:0,cacheMisses:0},u=()=>({pid:process.pid,startedAt:i,requests:a.requests,cacheHits:a.cacheHits,cacheMisses:a.cacheMisses,cacheEntries:s.size}),c=async l=>{if(a.requests+=1,l.type==="ping")return{ok:!0,type:"ping",stats:u()};let p=Lo(l.project);if(n.observeRoot(l.project.rootPath),l.type==="invalidate")return{ok:!0,type:"invalidate",removed:s.delete(p)};let y=s.get(p);if(y&&y.expiresAt>Date.now())return a.cacheHits+=1,{ok:!0,type:"hydrate",cacheHit:!0,loadedAt:y.loadedAt,memory:y.data.memory,graph:y.data.graph};a.cacheMisses+=1;try{let m=await o(l.project),M=new Date().toISOString();return s.set(p,{expiresAt:Date.now()+t,loadedAt:M,data:m}),{ok:!0,type:"hydrate",cacheHit:!1,loadedAt:M,memory:m.memory,graph:m.graph}}catch(m){return{ok:!1,error:fe(m)}}};if(Io(Co(e),{recursive:!0,mode:448}),jt(e)){if(await Ko(e))throw new Error(`ToolNet service already running at ${e}`);Rt(e)}let d=No(l=>{l.setEncoding("utf8");let p="";l.on("data",y=>{p+=y;let m=p.indexOf(`
9
+ `);if(m===-1)return;let M=p.slice(0,m).trim();p=p.slice(m+1),(async()=>{try{let W=JSON.parse(M),Z=await c(W);l.end(`${JSON.stringify(Z)}
10
+ `)}catch(W){let Z={ok:!1,error:fe(W)};l.end(`${JSON.stringify(Z)}
11
+ `)}})()})});return await new Promise((l,p)=>{let y=m=>{p(m)};d.once("error",y),d.listen(e,()=>{d.off("error",y),l()})}),Oo(e,384),{socketPath:e,stats:u,close:async()=>{n.close(),await new Promise(l=>{d.close(()=>{l()})}),jt(e)&&Rt(e)}}}async function Ho(){let r=await Uo();console.log(`[toolnet-memory] service ready: ${r.socketPath}`);let e=()=>{r.close().finally(()=>process.exit(0))};process.once("SIGINT",e),process.once("SIGTERM",e)}var At=process.argv[1];At&&import.meta.url===Go(Do(At)).href&&Ho().catch(r=>{console.error(`[toolnet-memory] service failed: ${fe(r)}`),process.exit(1)});export{$o as loadProjectForService,Uo as startToolNetService};
@@ -1,4 +1,6 @@
1
- import"dotenv/config";import{existsSync as oe,readFileSync as se}from"node:fs";import{homedir as ie}from"node:os";import{join as ae}from"node:path";function ce(n){let e=n.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function ue(){let n=process.env.TOOLNET_GLOBAL_ENV??ae(ie(),".config","toolnet-memory",".env");if(!oe(n))return;let e=se(n,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=ce(r.slice(o+1)))}}ue();function h(n,e){return n===void 0?e:["1","true","yes","on"].includes(n.toLowerCase())}function S(n,e){if(!n)return e;let t=Number(n);return Number.isFinite(t)?t:e}function K(){return{memory:{autoCapture:h(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:h(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:h(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:h(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:S(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:S(process.env.MEMORY_RERANK_TOP,10),finalContext:S(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:S(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:h(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:S(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as ye}from"node:crypto";import{existsSync as M,mkdirSync as he,readFileSync as Se,renameSync as ve,writeFileSync as xe}from"node:fs";import{basename as be,dirname as R,join as x,parse as G,resolve as m}from"node:path";import{createHash as L}from"node:crypto";import{spawnSync as de}from"node:child_process";var v="git-remote-v1",pe=new Set(["github.com","gitlab.com","bitbucket.org"]);function z(n,e){let t=e.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!t||t==="."||t===".."||t.split("/").some(r=>!r||r==="."||r==="..")?null:(pe.has(n)&&(t=t.toLowerCase()),t)}function le(n){let e;try{e=new URL(n)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(e.protocol))return null;let t=e.hostname.trim().toLowerCase();if(!t)return null;let r=e.protocol==="https:"&&e.port==="443"||e.protocol==="http:"&&e.port==="80"||e.protocol==="ssh:"&&e.port==="22",o=e.port&&!r?`${t}:${e.port}`:t,s=z(t,e.pathname);return s?`${o}/${s}`:null}function me(n){let e=n.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!e)return null;let t=e[1]?.trim().toLowerCase();if(!t||t.length===1)return null;let r=z(t,e[2]??"");return r?`${t}/${r}`:null}function $(n){let e=n.trim();return e?e.includes("://")?le(e):me(e):null}function fe(n){return L("sha256").update(`${v}:${n}`).digest("hex")}function V(n){return L("sha256").update(`toolnet-project:${v}:${n}`).digest("hex").slice(0,16)}function ge(n){return n.split("/").filter(Boolean).at(-1)?.trim()||null}function T(n,e){let t=de("git",["-C",n,...e],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return t.error||t.status!==0?null:t.stdout?.trim()||null}function D(n,e){let t=ge(n);return t?{scheme:v,canonicalRemote:n,fingerprint:fe(n),repositoryName:t,source:e}:null}function B(n){let e=T(n,["remote","get-url","origin"]);if(e){let o=$(e);if(o)return D(o,"origin")}let t=T(n,["remote"]);if(!t)return null;let r=new Set;for(let o of t.split(/\r?\n/u).map(s=>s.trim()).filter(Boolean)){let s=T(n,["remote","get-url",o]);if(!s)continue;let i=$(s);i&&r.add(i)}return r.size!==1?null:D([...r][0],"unique-remote")}var H=".toolnet",we="project.json";function je(n){return ye("sha256").update(n).digest("hex").slice(0,16)}function f(n){return x(n,H,we)}function J(n){return M(f(n))}function F(n,e){let t=m(n),r=G(t).root;for(;;){if(J(t))return t;if(t===r||e&&t===m(e))break;let o=R(t);if(o===t)break;t=o}return null}function _(n){let e=m(n),t=G(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(s=>M(x(e,s))))return e;if(e===t)break;let o=R(e);if(o===e)break;e=o}return m(n)}function j(n){let e;try{e=JSON.parse(Se(n,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${n}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${n}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${n}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${n}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:R(R(n)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function k(n,e){let t=x(n,H);he(t,{recursive:!0});let r=f(n),o=`${r}.tmp-${process.pid}`;xe(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),ve(o,r)}function l(n,e){return{id:n.id,name:n.name,remote:n.remote,rootPath:e,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}function O(n){return{version:1,scheme:v,canonicalRemote:n.canonicalRemote,fingerprint:n.fingerprint,repositoryName:n.repositoryName}}function ke(n){let e=n.metadata?.toolnetIdentity;if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;return typeof t.fingerprint=="string"?t.fingerprint:null}var A=class{adopt(e,t){let r=_(m(e));if(!t.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!t.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!t.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(J(r)){let a=j(f(r));if(a.id!==t.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${a.id}`,`requested=${t.id}`].join(" "));return l(a,r)}let o=new Date().toISOString(),s={...t.metadata};t.gitIdentity&&(s.toolnetIdentity=O(t.gitIdentity));let i={version:1,id:t.id.trim(),name:t.name.trim(),remote:t.remote.trim(),rootPath:r,createdAt:t.createdAt??o,updatedAt:o,graphVersion:t.graphVersion??0,memoryVersion:t.memoryVersion??0,metadata:Object.keys(s).length?s:void 0};return k(r,i),l(i,r)}recordGitIdentity(e,t,r={}){let o=this.requireExisting(e),s=f(o.rootPath),i=j(s),a=ke(i);if(a&&a!==t.fingerprint&&!r.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${a}`,`current=${t.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let c=i.metadata?.toolnetIdentity;return c&&typeof c=="object"&&!Array.isArray(c)&&c.fingerprint===t.fingerprint||(i.metadata={...i.metadata,toolnetIdentity:O(t)},i.updatedAt=new Date().toISOString(),k(o.rootPath,i)),l(i,o.rootPath)}findExisting(e=process.cwd()){let t=m(e),r=_(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>M(x(r,c))),i=F(t,s?r:void 0);if(!i)return null;let a=j(f(i));return l(a,i)}requireExisting(e=process.cwd()){let t=this.findExisting(e);if(!t)throw new Error("PROJECT_NOT_INITIALIZED");return t}detect(e=process.cwd()){let t=m(e),r=_(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(y=>M(x(r,y))),i=F(t,s?r:void 0);if(i){let y=f(i),p=j(y);return p.rootPath!==i&&(p.rootPath=i,p.updatedAt=new Date().toISOString(),k(i,p)),l(p,i)}let a=new Date().toISOString(),c=be(r),u=B(r),d={version:1,id:u?V(u.canonicalRemote):je(r),name:c,remote:u?.repositoryName??c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0,metadata:u?{toolnetIdentity:O(u)}:void 0};return k(r,d),l(d,r)}};var Me=[{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"}],Re=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function U(n){return n.normalize("NFKC").trim().toLowerCase()}function Ae(n){if(n.length===0)return 0;let e=new Map;for(let r of n)e.set(r,(e.get(r)??0)+1);let t=0;for(let r of e.values()){let o=r/n.length;t-=o*Math.log2(o)}return t}function Pe(n){return/^[a-f0-9]{32}$/iu.test(n)||/^[a-f0-9]{40}$/iu.test(n)||/^[a-f0-9]{64}$/iu.test(n)}function Ce(n,e,t){let r=n.slice(Math.max(0,e-48),e),o=n.slice(t,Math.min(n.length,t+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${o}`)}function Ie(n,e){return n.start<e.end&&e.start<n.end}function Y(n){return n.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var P=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let r=U(t);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let s of Me){let i=new RegExp(s.regex.source,s.regex.flags);for(let a of e.matchAll(i))a.index===void 0||!a[0]||this.allowed(a[0])||t.push({type:s.type,value:a[0],start:a.index,end:a.index+a[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let r=Y(t),o=[];for(let s of r)o.some(i=>Ie(i,s))||o.push(s);return Y(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=U(e);return Re.has(t)?!0:this.allowValues.has(t)}entropyMatches(e){let t=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of e.matchAll(r)){if(o.index===void 0||!o[0])continue;let s=o[0];this.allowed(s)||Pe(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||Ce(e,o.index,o.index+s.length)&&(Ae(s)<3.7||t.push({type:"high_entropy_secret",value:s,start:o.index,end:o.index+s.length,confidence:"heuristic"}))}return t}};var C=class{scanner;constructor(e={}){this.scanner=new P(e)}sanitize(e){let t=this.scanner.scan(e);if(t.length===0)return{text:e,redacted:0,secretTypes:[]};let r=e,o=[...t].sort((i,a)=>a.start-i.start),s=new Set;for(let i of o)s.add(i.type),r=r.slice(0,i.start)+`[REDACTED:${i.type}]`+r.slice(i.end);return{text:r,redacted:t.length,secretTypes:[...s].sort()}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(t=>this.sanitizeValue(t));if(e&&typeof e=="object"){let t={};for(let[r,o]of Object.entries(e)){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")){t[r]="[REDACTED]";continue}t[r]=this.sanitizeValue(o)}return t}return e}};var kt=new C;import{homedir as Xe}from"node:os";import{join as et}from"node:path";import{DeleteObjectCommand as Ee,GetObjectCommand as Te,HeadObjectCommand as _e,ListObjectsV2Command as Oe,PutObjectCommand as Ne,S3Client as Ke}from"@aws-sdk/client-s3";import{getSignedUrl as $e}from"@aws-sdk/s3-request-presigner";var I=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ke({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ne({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await $e(this.client,new Te({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new _e({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Ee({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Oe({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let s of o.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as W,mkdir as De,readFile as Le,readdir as ze,rm as Ve,stat as Z,writeFile as Be}from"node:fs/promises";import{dirname as Fe,join as Ge,relative as q,resolve as He}from"node:path";var b=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return He(this.root,t)}async put(e,t){let r=this.path(e);await De(Fe(r),{recursive:!0}),await Be(r,t)}async get(e){try{return await Le(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await W(this.path(e)),!0}catch{return!1}}async delete(e){await Ve(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await W(t)}catch{return r}let o=async i=>{let a=await ze(i,{withFileTypes:!0});for(let c of a){let u=Ge(i,c.name);if(c.isDirectory()){await o(u);continue}let d=await Z(u);r.push({key:q(this.root,u),size:d.size,updatedAt:d.mtime.toISOString()})}},s=await Z(t);return s.isDirectory()?await o(t):r.push({key:q(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),r}};import{DeleteObjectCommand as Je,GetObjectCommand as Ue,HeadObjectCommand as Ye,ListObjectsV2Command as We,PutObjectCommand as Ze,S3Client as qe}from"@aws-sdk/client-s3";import{getSignedUrl as Qe}from"@aws-sdk/s3-request-presigner";var w=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new qe({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ze({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Qe(this.client,new Ue({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ye({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Je({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new We({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let s of o.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};function N(n,e){return console.warn(e),new b(n)}function Q(n){let e=n.localRoot??et(Xe(),".toolnet-memory","storage");if(n.provider==="r2"){let t=n.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new w({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):N(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let t=n.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new w({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):N(e,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let t=n.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new I({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):N(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new b(e)}function tt(n){return new Promise(e=>setTimeout(e,n))}async function X(n,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,o=e.maxDelayMs??2e3,s;for(let i=1;i<=t;i++)try{return await n()}catch(a){if(s=a,i>=t)break;let c=Math.min(o,r*2**(i-1)),u=Math.floor(Math.random()*Math.max(1,c*.2));await tt(c+u)}throw s}var rt=new Set(["put","get","getText","delete","list"]);function ee(n,e={}){return new Proxy(n,{get(t,r){let o=Reflect.get(t,r,t);return typeof o!="function"?o:rt.has(r)?(...s)=>X(()=>Promise.resolve(o.apply(t,s)),e):o.bind(t)}})}function te(n){let e=n.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function re(n){let e=n.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var E=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=te(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let i;try{i=JSON.parse(o)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(s,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=re(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};var nt=["memories/current.json","vectors/current.json","graph/current.json","graph/manifest.json","graph/resolution/current.json","code/chunks/current.json","code/vectors/current.json","code/architecture/current.json","code/architecture/entry-points.json","code/architecture/hotspots.json","code/architecture/layers.json","code/architecture/clusters.json","code/analysis/current.json","code/analysis/dead-code.json","code/analysis/dependencies.json","code/visualization/graph.json"],g=class{constructor(e){this.storage=e}storage;projectPrefix(e){return`projects/${e}`}snapshotPrefix(e,t){return[this.projectPrefix(e),"snapshots",t].join("/")}async create(e,t="automatic",r){let o=new Date().toISOString(),s=o.replace(/[:.]/g,"-"),i=this.projectPrefix(e),a=this.snapshotPrefix(e,s),c=[];for(let d of nt){let y=`${i}/${d}`,p=await this.storage.get(y);p&&(await this.storage.put(`${a}/${d}`,p,"application/json"),c.push(d))}if(c.length===0)return null;let u={version:1,id:s,projectId:e,createdAt:o,reason:t,objects:c,metadata:r};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(u,null,2),"application/json"),u}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),o=(await this.storage.list(t)).filter(i=>i.key.endsWith("/snapshot.json")),s=[];for(let i of o){let a=await this.storage.getText(i.key);if(a)try{s.push(JSON.parse(a))}catch{}}return s.sort((i,a)=>a.createdAt.localeCompare(i.createdAt))}async restore(e,t){let r=this.snapshotPrefix(e,t),o=await this.storage.getText(`${r}/snapshot.json`);if(!o)throw new Error(`Snapshot not found: ${t}`);let s=JSON.parse(o);if(s.projectId!==e)throw new Error("Snapshot project mismatch");let i=this.projectPrefix(e),a=[];for(let c of s.objects){let u=await this.storage.get(`${r}/${c}`);u&&(await this.storage.put(`${i}/${c}`,u,"application/json"),a.push(c))}return{restored:a}}async remove(e,t){let r=this.snapshotPrefix(e,t),o=await this.storage.list(r);for(let s of o)await this.storage.delete(s.key);return o.length}async prune(e,t=10){let o=(await this.list(e)).slice(t),s=0;for(let i of o)await this.remove(e,i.id),s++;return s}};async function ne(n,e){let t=new g(n),o=(await t.list(e))[0];if(!o)throw new Error("No snapshot available");let s=await t.restore(e,o.id);return{snapshot:o.id,createdAt:o.createdAt,...s}}async function ot(){let n=process.argv[2],e=K(),t=new A().detect(),r=ee(Q({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),o=new E(r,t.id,t.name,t.remote??t.name),s=new g(o);if(n==="list"){let i=await s.list(t.id);console.log(i.map(a=>({id:a.id,createdAt:a.createdAt,reason:a.reason})));return}if(n==="create"){let i=process.argv.slice(3).join(" ")||"manual-cli";console.log(await s.create(t.id,i));return}if(n==="restore"){let i=process.argv[3];if(!i)throw new Error("snapshot id required");await s.create(t.id,"before-cli-restore"),console.log(await s.restore(t.id,i));return}if(n==="recover-latest"){console.log(await ne(o,t.id));return}throw new Error("Usage: snapshot-cli list|create|restore|recover-latest")}ot().catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1)});
1
+ import"dotenv/config";import{existsSync as $e,readFileSync as Ve}from"node:fs";import{homedir as ze}from"node:os";import{join as Ye}from"node:path";function Ue(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Be(){let r=process.env.TOOLNET_GLOBAL_ENV??Ye(ze(),".config","toolnet-memory",".env");if(!$e(r))return;let e=Ve(r,"utf8");for(let t of e.split(/\r?\n/)){let n=t.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let s=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=Ue(n.slice(o+1)))}}Be();function w(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function v(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function Z(){return{memory:{autoCapture:w(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:w(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:w(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:w(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:v(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:v(process.env.MEMORY_RERANK_TOP,10),finalContext:v(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:v(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:w(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:v(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Ze}from"node:crypto";import{existsSync as _,mkdirSync as Qe,readFileSync as Xe,renameSync as et,writeFileSync as tt}from"node:fs";import{basename as rt,dirname as O,join as x,parse as se,resolve as g}from"node:path";import{createHash as ee}from"node:crypto";import{spawnSync as He}from"node:child_process";var b="git-remote-v1",Ge=new Set(["github.com","gitlab.com","bitbucket.org"]);function te(r,e){let t=e.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!t||t==="."||t===".."||t.split("/").some(n=>!n||n==="."||n==="..")?null:(Ge.has(r)&&(t=t.toLowerCase()),t)}function Fe(r){let e;try{e=new URL(r)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(e.protocol))return null;let t=e.hostname.trim().toLowerCase();if(!t)return null;let n=e.protocol==="https:"&&e.port==="443"||e.protocol==="http:"&&e.port==="80"||e.protocol==="ssh:"&&e.port==="22",o=e.port&&!n?`${t}:${e.port}`:t,s=te(t,e.pathname);return s?`${o}/${s}`:null}function Je(r){let e=r.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!e)return null;let t=e[1]?.trim().toLowerCase();if(!t||t.length===1)return null;let n=te(t,e[2]??"");return n?`${t}/${n}`:null}function Q(r){let e=r.trim();return e?e.includes("://")?Fe(e):Je(e):null}function We(r){return ee("sha256").update(`${b}:${r}`).digest("hex")}function re(r){return ee("sha256").update(`toolnet-project:${b}:${r}`).digest("hex").slice(0,16)}function qe(r){return r.split("/").filter(Boolean).at(-1)?.trim()||null}function $(r,e){let t=He("git",["-C",r,...e],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return t.error||t.status!==0?null:t.stdout?.trim()||null}function X(r,e){let t=qe(r);return t?{scheme:b,canonicalRemote:r,fingerprint:We(r),repositoryName:t,source:e}:null}function ne(r){let e=$(r,["remote","get-url","origin"]);if(e){let o=Q(e);if(o)return X(o,"origin")}let t=$(r,["remote"]);if(!t)return null;let n=new Set;for(let o of t.split(/\r?\n/u).map(s=>s.trim()).filter(Boolean)){let s=$(r,["remote","get-url",o]);if(!s)continue;let i=Q(s);i&&n.add(i)}return n.size!==1?null:X([...n][0],"unique-remote")}var ie=".toolnet",nt="project.json";function ot(r){return Ze("sha256").update(r).digest("hex").slice(0,16)}function h(r){return x(r,ie,nt)}function ae(r){return _(h(r))}function oe(r,e){let t=g(r),n=se(t).root;for(;;){if(ae(t))return t;if(t===n||e&&t===g(e))break;let o=O(t);if(o===t)break;t=o}return null}function V(r){let e=g(r),t=se(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>_(x(e,s))))return e;if(e===t)break;let o=O(e);if(o===e)break;e=o}return g(r)}function I(r){let e;try{e=JSON.parse(Xe(r,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${r}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let n=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:O(O(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:n,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:n,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function T(r,e){let t=x(r,ie);Qe(t,{recursive:!0});let n=h(r),o=`${n}.tmp-${process.pid}`;tt(o,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),et(o,n)}function f(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}function z(r){return{version:1,scheme:b,canonicalRemote:r.canonicalRemote,fingerprint:r.fingerprint,repositoryName:r.repositoryName}}function st(r){let e=r.metadata?.toolnetIdentity;if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;return typeof t.fingerprint=="string"?t.fingerprint:null}var C=class{adopt(e,t){let n=V(g(e));if(!t.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!t.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!t.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(ae(n)){let a=I(h(n));if(a.id!==t.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${a.id}`,`requested=${t.id}`].join(" "));return f(a,n)}let o=new Date().toISOString(),s={...t.metadata};t.gitIdentity&&(s.toolnetIdentity=z(t.gitIdentity));let i={version:1,id:t.id.trim(),name:t.name.trim(),remote:t.remote.trim(),rootPath:n,createdAt:t.createdAt??o,updatedAt:o,graphVersion:t.graphVersion??0,memoryVersion:t.memoryVersion??0,metadata:Object.keys(s).length?s:void 0};return T(n,i),f(i,n)}recordGitIdentity(e,t,n={}){let o=this.requireExisting(e),s=h(o.rootPath),i=I(s),a=st(i);if(a&&a!==t.fingerprint&&!n.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${a}`,`current=${t.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let c=i.metadata?.toolnetIdentity;return c&&typeof c=="object"&&!Array.isArray(c)&&c.fingerprint===t.fingerprint||(i.metadata={...i.metadata,toolnetIdentity:z(t)},i.updatedAt=new Date().toISOString(),T(o.rootPath,i)),f(i,o.rootPath)}findExisting(e=process.cwd()){let t=g(e),n=V(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>_(x(n,c))),i=oe(t,s?n:void 0);if(!i)return null;let a=I(h(i));return f(a,i)}requireExisting(e=process.cwd()){let t=this.findExisting(e);if(!t)throw new Error("PROJECT_NOT_INITIALIZED");return t}detect(e=process.cwd()){let t=g(e),n=V(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(p=>_(x(n,p))),i=oe(t,s?n:void 0);if(i){let p=h(i),m=I(p);return m.rootPath!==i&&(m.rootPath=i,m.updatedAt=new Date().toISOString(),T(i,m)),f(m,i)}let a=new Date().toISOString(),c=rt(n),u=ne(n),l={version:1,id:u?re(u.canonicalRemote):ot(n),name:c,remote:u?.repositoryName??c,rootPath:n,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0,metadata:u?{toolnetIdentity:z(u)}:void 0};return T(n,l),f(l,n)}};var it=[{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"}],at=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function ce(r){return r.normalize("NFKC").trim().toLowerCase()}function ct(r){if(r.length===0)return 0;let e=new Map;for(let n of r)e.set(n,(e.get(n)??0)+1);let t=0;for(let n of e.values()){let o=n/r.length;t-=o*Math.log2(o)}return t}function ut(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function dt(r,e,t){let n=r.slice(Math.max(0,e-48),e),o=r.slice(t,Math.min(r.length,t+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${n} ${o}`)}function lt(r,e){return r.start<e.end&&e.start<r.end}function ue(r){return r.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var N=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let n=ce(t);n&&this.allowValues.add(n)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let s of it){let i=new RegExp(s.regex.source,s.regex.flags);for(let a of e.matchAll(i))a.index===void 0||!a[0]||this.allowed(a[0])||t.push({type:s.type,value:a[0],start:a.index,end:a.index+a[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let n=ue(t),o=[];for(let s of n)o.some(i=>lt(i,s))||o.push(s);return ue(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=ce(e);return at.has(t)?!0:this.allowValues.has(t)}entropyMatches(e){let t=[],n=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of e.matchAll(n)){if(o.index===void 0||!o[0])continue;let s=o[0];this.allowed(s)||ut(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||dt(e,o.index,o.index+s.length)&&(ct(s)<3.7||t.push({type:"high_entropy_secret",value:s,start:o.index,end:o.index+s.length,confidence:"heuristic"}))}return t}};var K=class{scanner;constructor(e={}){this.scanner=new N(e)}sanitize(e){let t=this.scanner.scan(e);if(t.length===0)return{text:e,redacted:0,secretTypes:[]};let n=e,o=[...t].sort((i,a)=>a.start-i.start),s=new Set;for(let i of o)s.add(i.type),n=n.slice(0,i.start)+`[REDACTED:${i.type}]`+n.slice(i.end);return{text:n,redacted:t.length,secretTypes:[...s].sort()}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(t=>this.sanitizeValue(t));if(e&&typeof e=="object"){let t={};for(let[n,o]of Object.entries(e)){let s=n.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")){t[n]="[REDACTED]";continue}t[n]=this.sanitizeValue(o)}return t}return e}};var pt=new K;function R(r){return pt.sanitizeValue(r)}import{homedir as Ut}from"node:os";import{join as Bt}from"node:path";import{DeleteObjectCommand as mt,GetObjectCommand as ft,HeadObjectCommand as gt,ListObjectsV2Command as yt,PutObjectCommand as ht,S3Client as St}from"@aws-sdk/client-s3";import{getSignedUrl as Et}from"@aws-sdk/s3-request-presigner";var D=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new St({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,n="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ht({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Et(this.client,new ft({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new gt({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new mt({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new yt({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let s of o.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return t}};import{access as de,mkdir as wt,readFile as vt,readdir as bt,rm as xt,stat as le,writeFile as Rt}from"node:fs/promises";import{dirname as At,join as Pt,relative as pe,resolve as jt}from"node:path";var A=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return jt(this.root,t)}async put(e,t){let n=this.path(e);await wt(At(n),{recursive:!0}),await Rt(n,t)}async get(e){try{return await vt(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await de(this.path(e)),!0}catch{return!1}}async delete(e){await xt(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),n=[];try{await de(t)}catch{return n}let o=async i=>{let a=await bt(i,{withFileTypes:!0});for(let c of a){let u=Pt(i,c.name);if(c.isDirectory()){await o(u);continue}let l=await le(u);n.push({key:pe(this.root,u),size:l.size,updatedAt:l.mtime.toISOString()})}},s=await le(t);return s.isDirectory()?await o(t):n.push({key:pe(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),n}};import{DeleteObjectCommand as Mt,GetObjectCommand as kt,HeadObjectCommand as It,ListObjectsV2Command as Tt,PutObjectCommand as _t,S3Client as Ot}from"@aws-sdk/client-s3";import{getSignedUrl as Ct}from"@aws-sdk/s3-request-presigner";var P=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Ot({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,n="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new _t({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Ct(this.client,new kt({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(t,{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(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new It({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Mt({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Tt({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let s of o.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return t}};import{createCipheriv as Nt,createDecipheriv as Kt,createHash as Dt,randomBytes as Lt,timingSafeEqual as $t}from"node:crypto";import{readFileSync as Vt}from"node:fs";var y=Buffer.from("TNMEME01","ascii"),fe=1,j=8,M=12,Y=16,ge=y.length+1+j+M+Y,zt="toolnet-memory:remote-encryption:v1:",ye="aes-256-gcm",U=32,d=class extends Error{constructor(t,n){super(n);this.code=t;this.name="RemoteEncryptionError"}code};function Yt(r){return r?["1","true","yes","on","enabled"].includes(r.trim().toLowerCase()):!1}function B(r=process.env){return Yt(r.TOOLNET_REMOTE_ENCRYPTION)}function me(r){let e=r.trim();if(!e)throw new d("REMOTE_ENCRYPTION_KEY_EMPTY","Remote encryption key is empty.");let t;if(e.startsWith("hex:")){let n=e.slice(4);if(!/^[0-9a-f]{64}$/iu.test(n))throw new d("REMOTE_ENCRYPTION_KEY_INVALID","hex: remote encryption key must contain exactly 64 hexadecimal characters.");t=Buffer.from(n,"hex")}else if(/^[0-9a-f]{64}$/iu.test(e))t=Buffer.from(e,"hex");else{let n=e.startsWith("base64:")?e.slice(7):e;if(!/^[A-Za-z0-9+/_-]+={0,2}$/u.test(n))throw new d("REMOTE_ENCRYPTION_KEY_INVALID","Remote encryption key must be 32 raw bytes encoded as hexadecimal or base64.");t=Buffer.from(n,n.includes("-")||n.includes("_")?"base64url":"base64")}if(t.length!==U)throw new d("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${U} bytes.`);return t}function he(r=process.env){let e=r.TOOLNET_REMOTE_ENCRYPTION_KEY?.trim(),t=r.TOOLNET_REMOTE_ENCRYPTION_KEY_FILE?.trim();if(e&&t)throw new d("REMOTE_ENCRYPTION_KEY_AMBIGUOUS","Configure either TOOLNET_REMOTE_ENCRYPTION_KEY or TOOLNET_REMOTE_ENCRYPTION_KEY_FILE, not both.");if(e)return me(e);if(t){let n;try{n=Vt(t,"utf8")}catch(o){throw new d("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${t}.`,o instanceof Error?o.message:String(o)].join(" "))}return me(n)}}function Se(r){return Dt("sha256").update(r).digest().subarray(0,j)}function Ee(r){return Buffer.from(`${zt}${r}`,"utf8")}function H(r){return r.byteLength<y.length?!1:Buffer.from(r).subarray(0,y.length).equals(y)}function we(r,e,t){if(t.byteLength!==U)throw new d("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH","AES-256-GCM requires a 32-byte key.");let n=typeof e=="string"?Buffer.from(e,"utf8"):Buffer.from(e),o=Lt(M),s=Nt(ye,t,o);s.setAAD(Ee(r));let i=Buffer.concat([s.update(n),s.final()]),a=s.getAuthTag(),c=Buffer.alloc(ge),u=0;return y.copy(c,u),u+=y.length,c.writeUInt8(fe,u),u+=1,Se(t).copy(c,u),u+=j,o.copy(c,u),u+=M,a.copy(c,u),Buffer.concat([c,i])}function ve(r,e,t){let n=Buffer.from(e);if(!H(n))throw new d("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(n.length<ge)throw new d("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let o=y.length,s=n.readUInt8(o);if(o+=1,s!==fe)throw new d("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${s}.`);let i=n.subarray(o,o+j);o+=j;let a=Se(t);if(!$t(i,a))throw new d("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let c=n.subarray(o,o+M);o+=M;let u=n.subarray(o,o+Y);o+=Y;let l=n.subarray(o),p=Kt(ye,t,c);p.setAAD(Ee(r)),p.setAuthTag(u);try{return Buffer.concat([p.update(l),p.final()])}catch{throw new d("REMOTE_ENCRYPTION_AUTH_FAILED","Encrypted remote object failed AES-GCM authentication.")}}var G=class{constructor(e,t){this.inner=e;this.options=t;if(this.name=e.name,t.enabled&&!t.key)throw new d("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote client-side encryption is enabled but no encryption key is configured.")}inner;options;name;async put(e,t,n){if(!this.options.enabled){await this.inner.put(e,t,n);return}let o=this.options.key;if(!o)throw new d("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let s=we(e,t,o);await this.inner.put(e,s,"application/octet-stream")}async get(e){let t=await this.inner.get(e);if(!t)return null;if(!H(t))return t;if(!this.options.enabled)throw new d("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 d("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return ve(e,t,n)}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){return this.inner.exists(e)}async delete(e){await this.inner.delete(e)}async list(e=""){return this.inner.list(e)}};function be(r,e=process.env){if(r.name==="local")return B(e)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),r;let t=B(e),n=t?he(e):void 0;return new G(r,{enabled:t,key:n})}function k(r){return be(r)}function F(r,e){return console.warn(e),k(new A(r))}function xe(r){let e=r.localRoot??Bt(Ut(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?k(new P({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):F(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?k(new P({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):F(e,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?k(new D({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):F(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return k(new A(e))}function Ht(r){return new Promise(e=>setTimeout(e,r))}async function Re(r,e={}){let t=Math.max(1,e.attempts??3),n=e.baseDelayMs??150,o=e.maxDelayMs??2e3,s;for(let i=1;i<=t;i++)try{return await r()}catch(a){if(s=a,i>=t)break;let c=Math.min(o,n*2**(i-1)),u=Math.floor(Math.random()*Math.max(1,c*.2));await Ht(c+u)}throw s}var Gt=new Set(["put","get","getText","delete","list"]);function Ae(r,e={}){return new Proxy(r,{get(t,n){let o=Reflect.get(t,n,t);return typeof o!="function"?o:Gt.has(n)?(...s)=>Re(()=>Promise.resolve(o.apply(t,s)),e):o.bind(t)}})}function Pe(r){let e=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function je(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var L=class{constructor(e,t,n,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=n,this.folder=Pe(o??n),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),n=t,o=await this.provider.getText(e);if(o){let i;try{i=JSON.parse(o)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(n=i.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:t};await this.provider.put(e,JSON.stringify(s,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=je(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,n){return await this.ensureRegistered(),this.provider.put(this.key(e),t,n)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};var Ft=["memories/current.json","vectors/current.json","graph/current.json","graph/manifest.json","graph/resolution/current.json","code/chunks/current.json","code/vectors/current.json","code/architecture/current.json","code/architecture/entry-points.json","code/architecture/hotspots.json","code/architecture/layers.json","code/architecture/clusters.json","code/analysis/current.json","code/analysis/dead-code.json","code/analysis/dependencies.json","code/visualization/graph.json"],S=class{constructor(e){this.storage=e}storage;projectPrefix(e){return`projects/${e}`}snapshotPrefix(e,t){return[this.projectPrefix(e),"snapshots",t].join("/")}async create(e,t="automatic",n){let o=new Date().toISOString(),s=o.replace(/[:.]/g,"-"),i=this.projectPrefix(e),a=this.snapshotPrefix(e,s),c=[];for(let l of Ft){let p=`${i}/${l}`,m=await this.storage.get(p);m&&(await this.storage.put(`${a}/${l}`,m,"application/json"),c.push(l))}if(c.length===0)return null;let u={version:1,id:s,projectId:e,createdAt:o,reason:t,objects:c,metadata:n};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(u,null,2),"application/json"),u}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),o=(await this.storage.list(t)).filter(i=>i.key.endsWith("/snapshot.json")),s=[];for(let i of o){let a=await this.storage.getText(i.key);if(a)try{s.push(JSON.parse(a))}catch{}}return s.sort((i,a)=>a.createdAt.localeCompare(i.createdAt))}async restore(e,t){let n=this.snapshotPrefix(e,t),o=await this.storage.getText(`${n}/snapshot.json`);if(!o)throw new Error(`Snapshot not found: ${t}`);let s=JSON.parse(o);if(s.projectId!==e)throw new Error("Snapshot project mismatch");let i=this.projectPrefix(e),a=[];for(let c of s.objects){let u=await this.storage.get(`${n}/${c}`);u&&(await this.storage.put(`${i}/${c}`,u,"application/json"),a.push(c))}return{restored:a}}async remove(e,t){let n=this.snapshotPrefix(e,t),o=await this.storage.list(n);for(let s of o)await this.storage.delete(s.key);return o.length}async prune(e,t=10){let o=(await this.list(e)).slice(t),s=0;for(let i of o)await this.remove(e,i.id),s++;return s}};async function Me(r,e){let t=new S(r),o=(await t.list(e))[0];if(!o)throw new Error("No snapshot available");let s=await t.restore(e,o.id);return{snapshot:o.id,createdAt:o.createdAt,...s}}import{appendFileSync as Jt,closeSync as Wt,existsSync as ke,mkdirSync as Ie,openSync as qt,readFileSync as Te,statSync as Zt,unlinkSync as _e,writeFileSync as Qt}from"node:fs";import{createHash as Xt,randomUUID as Oe}from"node:crypto";import{dirname as Ce,join as q}from"node:path";var J="0".repeat(64),er=3e4,tr=100,rr=20;function nr(r){return new Promise(e=>{setTimeout(e,r)})}function or(r){return q(r.rootPath,".toolnet","audit")}function Ne(r){return q(or(r),"events.jsonl")}function Ke(r){return q(r.rootPath,".toolnet","runtime","locks","audit.lock")}function W(r){if(Array.isArray(r))return r.map(W);if(!r||typeof r!="object")return r;let e=r;return Object.fromEntries(Object.keys(e).sort().map(t=>[t,W(e[t])]))}function sr(r){return JSON.stringify(W(r))}function De(r){return Xt("sha256").update(sr(r),"utf8").digest("hex")}function ir(){let r=process.env.TOOLNET_AGENT_ID?.trim()||process.env.TOOLNET_AGENT?.trim();return r?{kind:"agent",id:r}:{kind:"system",id:`pid:${process.pid}`}}function Le(r){let e=Ne(r);if(!ke(e))return[];let t=Te(e,"utf8"),n=[];for(let o of t.split(/\r?\n/u)){let s=o.trim();s&&n.push(JSON.parse(s))}return n}function ar(r){try{return Date.now()-Zt(r).mtimeMs>er}catch{return!0}}async function cr(r){let e=Ke(r);Ie(Ce(e),{recursive:!0,mode:448});for(let t=0;t<tr;t+=1){let n=Oe();try{let o=qt(e,"wx",384);try{Qt(o,JSON.stringify({token:n,pid:process.pid,createdAt:new Date().toISOString()})+`
5
+ `)}finally{Wt(o)}return n}catch(o){if(o.code!=="EEXIST")throw o;if(ar(e)){try{_e(e)}catch{}continue}await nr(rr)}}throw new Error("AUDIT_LOCK_TIMEOUT")}function ur(r,e){let t=Ke(r);if(ke(t))try{if(JSON.parse(Te(t,"utf8")).token!==e)return;_e(t)}catch{}}function dr(r){return Le(r).at(-1)}async function lr(r,e){let t=await cr(r);try{let n=dr(r);if(n){let l=pr(r);if(!l.valid)throw new Error(["AUDIT_LOG_INTEGRITY_FAILED",l.error??""].filter(Boolean).join(" "))}let o=e.details?R(e.details):void 0,s=e.target?R(e.target):void 0,i=R(e.actor??ir()),a={version:1,sequence:(n?.sequence??0)+1,id:Oe(),at:e.at??new Date().toISOString(),projectId:r.id,action:e.action,outcome:e.outcome,actor:i,...s?{target:s}:{},...o?{details:o}:{},previousHash:n?.hash??J},c={...a,hash:De(a)},u=Ne(r);return Ie(Ce(u),{recursive:!0,mode:448}),Jt(u,JSON.stringify(c)+`
6
+ `,{encoding:"utf8",mode:384}),c}finally{ur(r,t)}}async function E(r,e){try{return await lr(r,e),!0}catch(t){return console.warn(`[audit] ${t instanceof Error?t.message:String(t)}`),!1}}function pr(r){let e;try{e=Le(r)}catch(o){return{valid:!1,records:0,lastHash:J,firstInvalidLine:1,error:o instanceof Error?o.message:String(o)}}let t=J,n=1;for(let o=0;o<e.length;o+=1){let s=e[o];if(s.sequence!==n)return{valid:!1,records:e.length,lastHash:t,firstInvalidLine:o+1,error:"AUDIT_SEQUENCE_INVALID"};if(s.previousHash!==t)return{valid:!1,records:e.length,lastHash:t,firstInvalidLine:o+1,error:"AUDIT_PREVIOUS_HASH_INVALID"};let{hash:i,...a}=s,c=De(a);if(i!==c)return{valid:!1,records:e.length,lastHash:t,firstInvalidLine:o+1,error:"AUDIT_HASH_INVALID"};t=i,n+=1}return{valid:!0,records:e.length,lastHash:t}}async function mr(){let r=process.argv[2],e=Z(),t=new C().detect(),n=Ae(xe({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),o=new L(n,t.id,t.name,t.remote??t.name),s=new S(o);if(r==="list"){let i=await s.list(t.id);console.log(i.map(a=>({id:a.id,createdAt:a.createdAt,reason:a.reason})));return}if(r==="create"){let i=process.argv.slice(3).join(" ")||"manual-cli",a=await s.create(t.id,i);if(!a){console.log("No data to snapshot.");return}await E(t,{action:"snapshot.create",outcome:"success",actor:{kind:"user",id:process.env.TOOLNET_AGENT_ID?.trim()||"cli"},details:{snapshotId:a.id,reason:i}}),console.log(a);return}if(r==="restore"){let i=process.argv[3];if(!i)throw new Error("snapshot id required");let a=await s.create(t.id,"before-cli-restore");try{let c=await s.restore(t.id,i);await E(t,{action:"snapshot.restore",outcome:"success",actor:{kind:"user",id:process.env.TOOLNET_AGENT_ID?.trim()||"cli"},target:i,details:{...a?{backupSnapshotId:a.id}:{}}}),console.log(c)}catch(c){throw await E(t,{action:"snapshot.restore",outcome:"failed",actor:{kind:"user",id:process.env.TOOLNET_AGENT_ID?.trim()||"cli"},target:i,details:{...a?{backupSnapshotId:a.id}:{},error:c instanceof Error?c.message:String(c)}}),c}return}if(r==="recover-latest"){try{let i=await Me(o,t.id);await E(t,{action:"snapshot.recover",outcome:"success",actor:{kind:"user",id:process.env.TOOLNET_AGENT_ID?.trim()||"cli"}}),console.log(i)}catch(i){throw await E(t,{action:"snapshot.recover",outcome:"failed",actor:{kind:"user",id:process.env.TOOLNET_AGENT_ID?.trim()||"cli"},details:{error:i instanceof Error?i.message:String(i)}}),i}return}throw new Error("Usage: snapshot-cli list|create|restore|recover-latest")}mr().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});