toolnet-memory 0.3.15 → 0.3.17

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 (48) hide show
  1. package/.env.example +74 -0
  2. package/.release-target +1 -1
  3. package/README.md +316 -6
  4. package/bin/toolnet-memory +12 -0
  5. package/bundle/agy-hook.js +66 -66
  6. package/bundle/agy.js +56 -56
  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/claude-hook.js +49 -49
  11. package/bundle/code-capabilities.js +2 -0
  12. package/bundle/codex-context.js +29 -29
  13. package/bundle/codex.js +71 -71
  14. package/bundle/context-runtime.js +39 -39
  15. package/bundle/context.js +15 -15
  16. package/bundle/copilot-hook.js +78 -78
  17. package/bundle/cursor-hook.js +78 -78
  18. package/bundle/docker-healthcheck.js +3 -0
  19. package/bundle/doctor.js +4 -4
  20. package/bundle/full-index.js +22 -21
  21. package/bundle/gc.js +5 -3
  22. package/bundle/graph-cli.js +1 -1
  23. package/bundle/graph-index.js +5 -4
  24. package/bundle/graph-ui.js +4 -3
  25. package/bundle/grok-hook.js +56 -56
  26. package/bundle/handoff-refresh.js +10 -10
  27. package/bundle/help-cli.js +4 -4
  28. package/bundle/impact.js +5 -4
  29. package/bundle/incremental.js +5 -5
  30. package/bundle/init.js +36 -35
  31. package/bundle/kiro-hook.js +85 -85
  32. package/bundle/learner.js +8 -6
  33. package/bundle/mcp.js +19 -17
  34. package/bundle/memory-agent.js +6 -6
  35. package/bundle/memory-query.js +7 -7
  36. package/bundle/opencode.js +77 -77
  37. package/bundle/project-manual.js +7 -7
  38. package/bundle/public/index.html +51 -2
  39. package/bundle/recovery-certify.js +9 -9
  40. package/bundle/runtime.js +16 -13
  41. package/bundle/semantic.js +6 -5
  42. package/bundle/service.js +11 -6
  43. package/bundle/snapshot.js +6 -4
  44. package/bundle/status-cli.js +4 -4
  45. package/bundle/toolnet-cli.js +46 -46
  46. package/bundle/work-continuity.js +7 -7
  47. package/package.json +38 -2
  48. package/release-manifest.json +143 -4
@@ -0,0 +1,3 @@
1
+ import{createConnection as p}from"node:net";import{homedir as m}from"node:os";import{join as O}from"node:path";function a(){return process.env.TOOLNET_SERVICE_SOCKET??O(m(),".toolnet","run","service.sock")}var E=Math.max(250,Number(process.env.TOOLNET_DOCKER_HEALTH_TIMEOUT_MS??2e3));async function u(){let l=a();return new Promise(f=>{let e=p({path:l}),t=!1,n="",o=r=>{t||(t=!0,clearTimeout(T),e.destroy(),f(r))},T=setTimeout(()=>{o(!1)},E);e.setEncoding("utf8"),e.once("connect",()=>{e.write(JSON.stringify({type:"ping"})+`
2
+ `)}),e.on("data",r=>{n+=r;let s=n.indexOf(`
3
+ `);if(s<0)return;let c=n.slice(0,s).trim();if(!c){o(!1);return}try{let i=JSON.parse(c);o(i.ok===!0&&i.type==="ping")}catch{o(!1)}}),e.once("error",()=>{o(!1)}),e.once("end",()=>{t||o(!1)})})}var S=await u();S?console.log("TOOLNET_DOCKER_HEALTH=PASS"):(console.error("TOOLNET_DOCKER_HEALTH=FAIL"),process.exitCode=1);
package/bundle/doctor.js CHANGED
@@ -1,4 +1,4 @@
1
- import"dotenv/config";import{existsSync as me,readFileSync as ye}from"node:fs";import{homedir as he}from"node:os";import{join as Se}from"node:path";function ke(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 we(){let r=process.env.TOOLNET_GLOBAL_ENV??Se(he(),".config","toolnet-memory",".env");if(!me(r))return;let e=ye(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]=ke(n.slice(o+1)))}}we();function y(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function h(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function W(){return{memory:{autoCapture:y(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:y(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:y(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:y(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:h(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:h(process.env.MEMORY_RERANK_TOP,10),finalContext:h(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:h(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:y(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:h(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as be}from"node:crypto";import{existsSync as v,mkdirSync as ve,readFileSync as xe,renameSync as je,writeFileSync as Ae}from"node:fs";import{basename as Me,dirname as x,join as S,parse as U,resolve as f}from"node:path";var Y=".toolnet",Ce="project.json";function Re(r){return be("sha256").update(r).digest("hex").slice(0,16)}function j(r){return S(r,Y,Ce)}function Pe(r){return v(j(r))}function z(r,e){let t=f(r),n=U(t).root;for(;;){if(Pe(t))return t;if(t===n||e&&t===f(e))break;let o=x(t);if(o===t)break;t=o}return null}function H(r){let e=f(r),t=U(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>v(S(e,s))))return e;if(e===t)break;let o=x(e);if(o===e)break;e=o}return f(r)}function B(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:x(x(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 J(r,e){let t=S(r,Y);ve(t,{recursive:!0});let n=j(r),o=`${n}.tmp-${process.pid}`;Ae(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),je(o,n)}function D(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}}var A=class{findExisting(e=process.cwd()){let t=f(e),n=H(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>v(S(n,c))),i=z(t,s?n:void 0);if(!i)return null;let a=B(j(i));return D(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=f(e),n=H(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(p=>v(S(n,p))),i=z(t,s?n:void 0);if(i){let p=j(i),u=B(p);return u.rootPath!==i&&(u.rootPath=i,u.updatedAt=new Date().toISOString(),J(i,u)),D(u,i)}let a=new Date().toISOString(),c=Me(n),d={version:1,id:Re(n),name:c,remote:c,rootPath:n,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return J(n,d),D(d,n)}};var Ee=[{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"}],Te=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function G(r){return r.normalize("NFKC").trim().toLowerCase()}function $e(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 Ie(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function _e(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 Oe(r,e){return r.start<e.end&&e.start<r.end}function q(r){return r.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var M=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let n=G(t);n&&this.allowValues.add(n)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let s of Ee){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=q(t),o=[];for(let s of n)o.some(i=>Oe(i,s))||o.push(s);return q(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=G(e);return Te.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)||Ie(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||_e(e,o.index,o.index+s.length)&&($e(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 M(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 Ot=new C;import{homedir as st}from"node:os";import{join as it}from"node:path";import{DeleteObjectCommand as Ke,GetObjectCommand as De,HeadObjectCommand as Le,ListObjectsV2Command as Fe,PutObjectCommand as Ve,S3Client as We}from"@aws-sdk/client-s3";import{getSignedUrl as ze}from"@aws-sdk/s3-request-presigner";var R=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new We({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 Ve({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await ze(this.client,new De({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 Le({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 Ke({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Fe({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 Z,mkdir as He,readFile as Be,readdir as Je,rm as Ue,stat as Q,writeFile as Ye}from"node:fs/promises";import{dirname as Ge,join as qe,relative as X,resolve as Ze}from"node:path";var k=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Ze(this.root,t)}async put(e,t){let n=this.path(e);await He(Ge(n),{recursive:!0}),await Ye(n,t)}async get(e){try{return await Be(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 Z(this.path(e)),!0}catch{return!1}}async delete(e){await Ue(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),n=[];try{await Z(t)}catch{return n}let o=async i=>{let a=await Je(i,{withFileTypes:!0});for(let c of a){let d=qe(i,c.name);if(c.isDirectory()){await o(d);continue}let p=await Q(d);n.push({key:X(this.root,d),size:p.size,updatedAt:p.mtime.toISOString()})}},s=await Q(t);return s.isDirectory()?await o(t):n.push({key:X(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),n}};import{DeleteObjectCommand as Qe,GetObjectCommand as Xe,HeadObjectCommand as et,ListObjectsV2Command as tt,PutObjectCommand as rt,S3Client as nt}from"@aws-sdk/client-s3";import{getSignedUrl as ot}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 nt({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 rt({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await ot(this.client,new Xe({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 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 Qe({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}};function L(r,e){return console.warn(e),new k(r)}function ee(r){let e=r.localRoot??it(st(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.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}):L(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 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}):L(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 R({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):L(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new k(e)}var P=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 n=JSON.parse(t);if(!Array.isArray(n))throw new Error("Invalid memory snapshot");return n}async save(e,t){let n=JSON.stringify(t,null,2);await this.storage.put(this.key(e),n,"application/json")}};var E=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return null;let n=JSON.parse(t);if(n.version!==1||n.projectId!==e||!Array.isArray(n.records))throw new Error("Invalid vector snapshot");return n}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var T=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")}};var I=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","vectors","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 at(r){return new Promise(e=>setTimeout(e,r))}async function te(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)),d=Math.floor(Math.random()*Math.max(1,c*.2));await at(c+d)}throw s}var ct=new Set(["put","get","getText","delete","list"]);function re(r,e={}){return new Proxy(r,{get(t,n){let o=Reflect.get(t,n,t);return typeof o!="function"?o:ct.has(n)?(...s)=>te(()=>Promise.resolve(o.apply(t,s)),e):o.bind(t)}})}function ne(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 oe(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 _=class{constructor(e,t,n,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=n,this.folder=ne(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=oe(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 ut=["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"],O=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 p of ut){let u=`${i}/${p}`,g=await this.storage.get(u);g&&(await this.storage.put(`${a}/${p}`,g,"application/json"),c.push(p))}if(c.length===0)return null;let d={version:1,id:s,projectId:e,createdAt:o,reason:t,objects:c,metadata:n};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(d,null,2),"application/json"),d}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 d=await this.storage.get(`${n}/${c}`);d&&(await this.storage.put(`${i}/${c}`,d,"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}};function F(r,e){for(let t of r)process.env[t]||e.push(`Missing ${t}`)}function se(){let r=[],e=[],t=process.env.MEMORY_STORAGE_PROVIDER??"local";return t==="r2"?F(["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"],r):t==="s3"?F(["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"],r):t==="huggingface"?F(["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"],r):t!=="local"&&r.push(`Unsupported MEMORY_STORAGE_PROVIDER: ${t}`),process.env.HF_TOKEN||e.push("HF_TOKEN missing: Hugging Face S3 storage credentials incomplete"),{ok:r.length===0,errors:r,warnings:e}}import{randomUUID as dt}from"node:crypto";var N=class{constructor(e){this.storage=e}storage;async run(){let e=`_health/production-${dt()}.txt`,t=`toolnet-memory:${Date.now()}`,n=!1;try{await this.storage.put(e,t,"text/plain"),n=await this.storage.getText(e)===t}finally{try{await this.storage.delete(e)}catch{}}return{ok:n,storage:{ok:n,provider:this.storage.name},embedding:null}}};import{existsSync as de,readFileSync as mt,readdirSync as ae}from"node:fs";import{join as b}from"node:path";var An=new Int32Array(new SharedArrayBuffer(4));import{closeSync as En,existsSync as lt,fsyncSync as Tn,mkdirSync as $n,openSync as In,readFileSync as pt,renameSync as _n,writeFileSync as On}from"node:fs";import{dirname as Kn,join as gt}from"node:path";function ft(r){return gt(r.rootPath,".toolnet","work","current.json")}function ie(r){let e=ft(r);if(!lt(e))return null;try{let t=JSON.parse(pt(e,"utf8"));return t.version!==1||t.projectId!==r.id?null:t}catch{return null}}function le(r){if(!de(r))return null;try{return JSON.parse(mt(r,"utf8"))}catch{return null}}function ce(r,e){if(!de(r))return[];let t=[];for(let n of ae(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let o=b(r,n.name);for(let s of ae(o,{withFileTypes:!0})){if(!s.isDirectory())continue;let i=le(b(o,s.name,"state.json"));i&&i.version===1&&i.projectId===e.id&&t.push(i)}}return t}function yt(r){let e=b(r.rootPath,".toolnet","runtime","sources"),t=b(r.rootPath,".toolnet","sessions"),n=ce(e,r),o=ce(t,r),s=new Map;for(let i of o)s.set(`${i.agent}:${i.nativeSessionId}`,i);for(let i of n)s.set(`${i.agent}:${i.nativeSessionId}`,i);return Array.from(s.values())}function ue(r){return r.filter(e=>!!e).sort().at(-1)}function ht(r){let e=le(b(r.rootPath,".toolnet","runtime","opencode-status.json"));return!e||e.projectRoot&&e.projectRoot!==r.rootPath?null:e}function pe(r){let e=yt(r),n=[...e].sort((u,g)=>(u.lastLocalEventAt??u.updatedAt).localeCompare(g.lastLocalEventAt??g.updatedAt)).at(-1),o=Array.from(new Set(e.map(u=>u.agent))).sort(),s=e.reduce((u,g)=>u+Math.max(0,g.lastSequence-g.lastRemoteSequence),0),i=ht(r),a=i?.state==="capture-failed",c=i?.state==="sync-failed",d;a?d="degraded":s>0||c?d="pending":e.length>0?d="healthy":d="unknown";let p=ie(r);return{ok:d!=="degraded",agents:o,sessions:e.length,latestAgent:n?.agent,latestSessionId:n?.nativeSessionId,lastCaptureAt:ue(e.map(u=>u.lastLocalEventAt??u.updatedAt)),lastFlushAt:ue(e.map(u=>u.lastRemoteAt)),pendingWal:s,currentTask:p?.currentTask?.title,currentFile:p?.activeFiles?.at(-1)??p?.filesTouched.at(-1),syncHealth:d,opencode:i?{state:i.state,reason:i.reason,timestamp:i.timestamp,error:i.error}:void 0}}function fe(){return process.argv.includes("--json")}function St(r){let e=process.stdout.isTTY===!0&&process.env.TERM!=="dumb"&&process.env.NO_COLOR===void 0,t=(l,V)=>e?`\x1B[${l}m${V}\x1B[0m`:V,n=l=>t("38;5;82",l),o=l=>t("38;5;51",l),s=l=>t("38;5;214",l),i=l=>t("38;5;196",l),a=l=>t("38;5;255",l),c=l=>t("2",l),d=l=>t("1",l),p=c("\u2502"),u=c("\u251C");if(console.log(""),console.log(`${o("\u25C7")} ${d(a("ToolNet Memory Doctor"))}`),console.log(""),r.project&&console.log(`${n("\u25C6")} Project ${a(r.project)}`),console.log(p),r.storage){let l=typeof r.storage.provider=="string"?` \u2014 ${r.storage.provider}`:"";r.storage.ok?console.log(`${u} ${n("\u25C6")} Storage${l}`):console.log(`${u} ${i("\u2717")} Storage${l}`)}if((r.memory!==void 0||r.graphSymbols!==void 0||r.codeChunks!==void 0)&&(console.log(p),console.log(`${o("\u25C7")} ${a("Project data")}`),r.memory!==void 0&&console.log(`${u} ${c("Memories")} ${a(String(r.memory))}`),r.graphSymbols!==void 0&&console.log(`${u} ${c("Graph symbols")} ${a(String(r.graphSymbols))}`),r.graphEdges!==void 0&&console.log(`${u} ${c("Graph edges")} ${a(String(r.graphEdges))}`),r.memoryVectors!==void 0&&console.log(`${u} ${c("Memory vectors")} ${a(String(r.memoryVectors))}`),r.codeChunks!==void 0&&console.log(`${u} ${c("Code chunks")} ${a(String(r.codeChunks))}`),r.codeVectors!==void 0&&console.log(`${u} ${c("Code vectors")} ${a(String(r.codeVectors))}`),r.snapshots!==void 0&&console.log(`${u} ${c("Snapshots")} ${a(String(r.snapshots))}`)),r.capture){console.log(p),console.log(`${o("\u25C7")} ${a("Session continuity")}`),console.log(`${u} ${c("Agents")} ${a(r.capture.agents.length?r.capture.agents.join(", "):"none")}`),r.capture.latestAgent&&console.log(`${u} ${c("Latest agent")} ${a(String(r.capture.latestAgent))}`),r.capture.currentTask&&console.log(`${u} ${c("Current task")} ${a(r.capture.currentTask)}`),r.capture.currentFile&&console.log(`${u} ${c("Current file")} ${a(r.capture.currentFile)}`),r.capture.lastCaptureAt&&console.log(`${u} ${c("Last capture")} ${a(r.capture.lastCaptureAt)}`),r.capture.lastFlushAt&&console.log(`${u} ${c("Last flush")} ${a(r.capture.lastFlushAt)}`),console.log(`${u} ${c("Pending WAL")} ${a(String(r.capture.pendingWal))}`);let l=r.capture.syncHealth==="healthy"?n(r.capture.syncHealth):r.capture.syncHealth==="degraded"?i(r.capture.syncHealth):s(r.capture.syncHealth);console.log(`${u} ${c("Sync health")} ${l}`)}let g=r.config?.errors??[];if(g.length>0){console.log(p),console.log(`${i("\u2717")} ${a("Configuration required")}`);for(let l of g)console.log(`${u} ${i("\u2022")} ${l}`)}let K=[...r.config?.warnings??[],...r.warnings??[]],m=[...new Set(K)];if(m.length>0){console.log(p),console.log(`${s("\u25C7")} ${a("Warnings")}`);for(let l of m)console.log(`${u} ${s("!")} ${l}`)}console.log(p),r.ok?console.log(`${n("\u2514 \u25C6")} ${d(a("ToolNet Memory is ready"))}`):(console.log(`${i("\u2514 \u2717")} ${d(a("ToolNet Memory requires attention"))}`),g.length>0&&console.log(c(" Run: toolnet-memory setup"))),console.log("")}function ge(r){fe()?console.log(JSON.stringify(r,null,2)):St(r)}async function kt(){let r=se();if(!r.ok){let l={ok:!1,config:r,warnings:r.warnings};ge(l),process.exitCode=1;return}let e=W(),t=new A().detect(),n=re(ee({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),o=new _(n,t.id,t.name,t.remote??t.name),s=await new N(n).run(),i=await new P(o).load(t.id),a=await new T(o).load(t.id),c=await new E(o).load(t.id),d=await new $(o).load(t.id),p=await new I(o).load(t.id),u=await new O(o).list(t.id),g=pe(t),K=g.syncHealth==="degraded"?[`Session capture degraded${g.opencode?.error?`: ${g.opencode.error}`:""}`]:[],m={ok:s.ok&&g.ok,project:t.name,storage:s.storage,memory:i.length,graphSymbols:a?.symbols.length??0,graphEdges:a?.edges.length??0,memoryVectors:c?.records.length??0,codeChunks:d?.chunks.length??0,codeVectors:p?.records.length??0,snapshots:u.length,capture:g,warnings:[...r.warnings,...K]};ge(m),process.exitCode=m.ok?0:1}kt().catch(r=>{let e=r instanceof Error?r.message:String(r);fe()?console.log(JSON.stringify({ok:!1,error:e},null,2)):(console.error(""),console.error("ToolNet Memory Doctor"),console.error("====================="),console.error(""),console.error(`\u2717 ${e}`),console.error("")),process.exitCode=1});
1
+ import"dotenv/config";import{existsSync as Be,readFileSync as He}from"node:fs";import{homedir as Ue}from"node:os";import{join as Ge}from"node:path";function Je(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 qe(){let r=process.env.TOOLNET_GLOBAL_ENV??Ge(Ue(),".config","toolnet-memory",".env");if(!Be(r))return;let e=He(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]=Je(n.slice(o+1)))}}qe();function b(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function w(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function oe(){return{memory:{autoCapture:b(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:b(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:b(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:b(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:w(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:w(process.env.MEMORY_RERANK_TOP,10),finalContext:w(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:w(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:b(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:w(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as nt}from"node:crypto";import{existsSync as T,mkdirSync as ot,readFileSync as st,renameSync as it,writeFileSync as at}from"node:fs";import{basename as ct,dirname as _,join as E,parse as pe,resolve as y}from"node:path";import{createHash as ae}from"node:crypto";import{spawnSync as Ze}from"node:child_process";var x="git-remote-v1",Qe=new Set(["github.com","gitlab.com","bitbucket.org"]);function ce(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:(Qe.has(r)&&(t=t.toLowerCase()),t)}function Xe(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=ce(t,e.pathname);return s?`${o}/${s}`:null}function et(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=ce(t,e[2]??"");return n?`${t}/${n}`:null}function se(r){let e=r.trim();return e?e.includes("://")?Xe(e):et(e):null}function tt(r){return ae("sha256").update(`${x}:${r}`).digest("hex")}function ue(r){return ae("sha256").update(`toolnet-project:${x}:${r}`).digest("hex").slice(0,16)}function rt(r){return r.split("/").filter(Boolean).at(-1)?.trim()||null}function U(r,e){let t=Ze("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=rt(r);return t?{scheme:x,canonicalRemote:r,fingerprint:tt(r),repositoryName:t,source:e}:null}function de(r){let e=U(r,["remote","get-url","origin"]);if(e){let o=se(e);if(o)return ie(o,"origin")}let t=U(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=U(r,["remote","get-url",o]);if(!s)continue;let i=se(s);i&&n.add(i)}return n.size!==1?null:ie([...n][0],"unique-remote")}var me=".toolnet",ut="project.json";function dt(r){return nt("sha256").update(r).digest("hex").slice(0,16)}function S(r){return E(r,me,ut)}function fe(r){return T(S(r))}function le(r,e){let t=y(r),n=pe(t).root;for(;;){if(fe(t))return t;if(t===n||e&&t===y(e))break;let o=_(t);if(o===t)break;t=o}return null}function G(r){let e=y(r),t=pe(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>T(E(e,s))))return e;if(e===t)break;let o=_(e);if(o===e)break;e=o}return y(r)}function P(r){let e;try{e=JSON.parse(st(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:_(_(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 I(r,e){let t=E(r,me);ot(t,{recursive:!0});let n=S(r),o=`${n}.tmp-${process.pid}`;at(o,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),it(o,n)}function g(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 J(r){return{version:1,scheme:x,canonicalRemote:r.canonicalRemote,fingerprint:r.fingerprint,repositoryName:r.repositoryName}}function lt(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 O=class{adopt(e,t){let n=G(y(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(fe(n)){let a=P(S(n));if(a.id!==t.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${a.id}`,`requested=${t.id}`].join(" "));return g(a,n)}let o=new Date().toISOString(),s={...t.metadata};t.gitIdentity&&(s.toolnetIdentity=J(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 I(n,i),g(i,n)}recordGitIdentity(e,t,n={}){let o=this.requireExisting(e),s=S(o.rootPath),i=P(s),a=lt(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:J(t)},i.updatedAt=new Date().toISOString(),I(o.rootPath,i)),g(i,o.rootPath)}findExisting(e=process.cwd()){let t=y(e),n=G(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>T(E(n,c))),i=le(t,s?n:void 0);if(!i)return null;let a=P(S(i));return g(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=y(e),n=G(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>T(E(n,u))),i=le(t,s?n:void 0);if(i){let u=S(i),m=P(u);return m.rootPath!==i&&(m.rootPath=i,m.updatedAt=new Date().toISOString(),I(i,m)),g(m,i)}let a=new Date().toISOString(),c=ct(n),d=de(n),p={version:1,id:d?ue(d.canonicalRemote):dt(n),name:c,remote:d?.repositoryName??c,rootPath:n,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0,metadata:d?{toolnetIdentity:J(d)}:void 0};return I(n,p),g(p,n)}};var pt=[{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 ge(r){return r.normalize("NFKC").trim().toLowerCase()}function ft(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 gt(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function yt(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 ht(r,e){return r.start<e.end&&e.start<r.end}function ye(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=ge(t);n&&this.allowValues.add(n)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let s of pt){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=ye(t),o=[];for(let s of n)o.some(i=>ht(i,s))||o.push(s);return ye(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=ge(e);return mt.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)||gt(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||yt(e,o.index,o.index+s.length)&&(ft(s)<3.7||t.push({type:"high_entropy_secret",value:s,start:o.index,end:o.index+s.length,confidence:"heuristic"}))}return t}};var $=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 Cr=new $;import{homedir as Jt}from"node:os";import{join as qt}from"node:path";import{DeleteObjectCommand as kt,GetObjectCommand as bt,HeadObjectCommand as wt,ListObjectsV2Command as xt,PutObjectCommand as Et,S3Client as vt}from"@aws-sdk/client-s3";import{getSignedUrl as Rt}from"@aws-sdk/s3-request-presigner";var K=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new vt({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 Et({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Rt(this.client,new bt({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 wt({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 kt({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new xt({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 he,mkdir as At,readFile as Mt,readdir as jt,rm as Ct,stat as Se,writeFile as Pt}from"node:fs/promises";import{dirname as It,join as Tt,relative as ke,resolve as _t}from"node:path";var v=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return _t(this.root,t)}async put(e,t){let n=this.path(e);await At(It(n),{recursive:!0}),await Pt(n,t)}async get(e){try{return await Mt(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 he(this.path(e)),!0}catch{return!1}}async delete(e){await Ct(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),n=[];try{await he(t)}catch{return n}let o=async i=>{let a=await jt(i,{withFileTypes:!0});for(let c of a){let d=Tt(i,c.name);if(c.isDirectory()){await o(d);continue}let p=await Se(d);n.push({key:ke(this.root,d),size:p.size,updatedAt:p.mtime.toISOString()})}},s=await Se(t);return s.isDirectory()?await o(t):n.push({key:ke(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),n}};import{DeleteObjectCommand as Ot,GetObjectCommand as Nt,HeadObjectCommand as $t,ListObjectsV2Command as Kt,PutObjectCommand as Dt,S3Client as Lt}from"@aws-sdk/client-s3";import{getSignedUrl as Ft}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 Lt({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 Dt({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Ft(this.client,new Nt({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 $t({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 Ot({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Kt({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 Vt,createDecipheriv as Yt,createHash as zt,randomBytes as Wt,timingSafeEqual as Bt}from"node:crypto";import{readFileSync as Ht}from"node:fs";var h=Buffer.from("TNMEME01","ascii"),we=1,A=8,M=12,q=16,xe=h.length+1+A+M+q,Ut="toolnet-memory:remote-encryption:v1:",Ee="aes-256-gcm",Z=32,f=class extends Error{constructor(t,n){super(n);this.code=t;this.name="RemoteEncryptionError"}code};function Gt(r){return r?["1","true","yes","on","enabled"].includes(r.trim().toLowerCase()):!1}function Q(r=process.env){return Gt(r.TOOLNET_REMOTE_ENCRYPTION)}function be(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 n=e.slice(4);if(!/^[0-9a-f]{64}$/iu.test(n))throw new f("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 f("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!==Z)throw new f("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${Z} bytes.`);return t}function ve(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 be(e);if(t){let n;try{n=Ht(t,"utf8")}catch(o){throw new f("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${t}.`,o instanceof Error?o.message:String(o)].join(" "))}return be(n)}}function Re(r){return zt("sha256").update(r).digest().subarray(0,A)}function Ae(r){return Buffer.from(`${Ut}${r}`,"utf8")}function X(r){return r.byteLength<h.length?!1:Buffer.from(r).subarray(0,h.length).equals(h)}function Me(r,e,t){if(t.byteLength!==Z)throw new f("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=Wt(M),s=Vt(Ee,t,o);s.setAAD(Ae(r));let i=Buffer.concat([s.update(n),s.final()]),a=s.getAuthTag(),c=Buffer.alloc(xe),d=0;return h.copy(c,d),d+=h.length,c.writeUInt8(we,d),d+=1,Re(t).copy(c,d),d+=A,o.copy(c,d),d+=M,a.copy(c,d),Buffer.concat([c,i])}function je(r,e,t){let n=Buffer.from(e);if(!X(n))throw new f("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(n.length<xe)throw new f("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let o=h.length,s=n.readUInt8(o);if(o+=1,s!==we)throw new f("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${s}.`);let i=n.subarray(o,o+A);o+=A;let a=Re(t);if(!Bt(i,a))throw new f("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let c=n.subarray(o,o+M);o+=M;let d=n.subarray(o,o+q);o+=q;let p=n.subarray(o),u=Yt(Ee,t,c);u.setAAD(Ae(r)),u.setAuthTag(d);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 ee=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,n){if(!this.options.enabled){await this.inner.put(e,t,n);return}let o=this.options.key;if(!o)throw new f("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let s=Me(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(!X(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 n=this.options.key;if(!n)throw new f("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return je(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 Ce(r,e=process.env){if(r.name==="local")return Q(e)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),r;let t=Q(e),n=t?ve(e):void 0;return new ee(r,{enabled:t,key:n})}function j(r){return Ce(r)}function te(r,e){return console.warn(e),j(new v(r))}function Pe(r){let e=r.localRoot??qt(Jt(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?j(new R({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):te(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?j(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})):te(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?j(new K({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):te(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return j(new v(e))}var D=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 n=JSON.parse(t);if(!Array.isArray(n))throw new Error("Invalid memory snapshot");return n}async save(e,t){let n=JSON.stringify(t,null,2);await this.storage.put(this.key(e),n,"application/json")}};var L=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return null;let n=JSON.parse(t);if(n.version!==1||n.projectId!==e||!Array.isArray(n.records))throw new Error("Invalid vector snapshot");return n}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var F=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 V=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")}};var Y=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","vectors","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 Zt(r){return new Promise(e=>setTimeout(e,r))}async function Ie(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)),d=Math.floor(Math.random()*Math.max(1,c*.2));await Zt(c+d)}throw s}var Qt=new Set(["put","get","getText","delete","list"]);function Te(r,e={}){return new Proxy(r,{get(t,n){let o=Reflect.get(t,n,t);return typeof o!="function"?o:Qt.has(n)?(...s)=>Ie(()=>Promise.resolve(o.apply(t,s)),e):o.bind(t)}})}function _e(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 Oe(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 z=class{constructor(e,t,n,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=n,this.folder=_e(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=Oe(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 Xt=["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"],W=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 p of Xt){let u=`${i}/${p}`,m=await this.storage.get(u);m&&(await this.storage.put(`${a}/${p}`,m,"application/json"),c.push(p))}if(c.length===0)return null;let d={version:1,id:s,projectId:e,createdAt:o,reason:t,objects:c,metadata:n};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(d,null,2),"application/json"),d}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 d=await this.storage.get(`${n}/${c}`);d&&(await this.storage.put(`${i}/${c}`,d,"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}};function re(r,e){for(let t of r)process.env[t]||e.push(`Missing ${t}`)}function Ne(){let r=[],e=[],t=process.env.MEMORY_STORAGE_PROVIDER??"local";return t==="r2"?re(["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"],r):t==="s3"?re(["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"],r):t==="huggingface"?re(["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"],r):t!=="local"&&r.push(`Unsupported MEMORY_STORAGE_PROVIDER: ${t}`),process.env.HF_TOKEN||e.push("HF_TOKEN missing: Hugging Face S3 storage credentials incomplete"),{ok:r.length===0,errors:r,warnings:e}}import{randomUUID as er}from"node:crypto";var B=class{constructor(e){this.storage=e}storage;async run(){let e=`_health/production-${er()}.txt`,t=`toolnet-memory:${Date.now()}`,n=!1;try{await this.storage.put(e,t,"text/plain"),n=await this.storage.getText(e)===t}finally{try{await this.storage.delete(e)}catch{}}return{ok:n,storage:{ok:n,provider:this.storage.name},embedding:null}}};import{existsSync as Fe,readFileSync as sr,readdirSync as Ke}from"node:fs";import{join as C}from"node:path";var Io=new Int32Array(new SharedArrayBuffer(4));import{closeSync as $o,existsSync as tr,fsyncSync as Ko,mkdirSync as Do,openSync as Lo,readFileSync as rr,renameSync as Fo,writeFileSync as Vo}from"node:fs";import{dirname as zo,join as nr}from"node:path";function or(r){return nr(r.rootPath,".toolnet","work","current.json")}function $e(r){let e=or(r);if(!tr(e))return null;try{let t=JSON.parse(rr(e,"utf8"));return t.version!==1||t.projectId!==r.id?null:t}catch{return null}}function Ve(r){if(!Fe(r))return null;try{return JSON.parse(sr(r,"utf8"))}catch{return null}}function De(r,e){if(!Fe(r))return[];let t=[];for(let n of Ke(r,{withFileTypes:!0})){if(!n.isDirectory())continue;let o=C(r,n.name);for(let s of Ke(o,{withFileTypes:!0})){if(!s.isDirectory())continue;let i=Ve(C(o,s.name,"state.json"));i&&i.version===1&&i.projectId===e.id&&t.push(i)}}return t}function ir(r){let e=C(r.rootPath,".toolnet","runtime","sources"),t=C(r.rootPath,".toolnet","sessions"),n=De(e,r),o=De(t,r),s=new Map;for(let i of o)s.set(`${i.agent}:${i.nativeSessionId}`,i);for(let i of n)s.set(`${i.agent}:${i.nativeSessionId}`,i);return Array.from(s.values())}function Le(r){return r.filter(e=>!!e).sort().at(-1)}function ar(r){let e=Ve(C(r.rootPath,".toolnet","runtime","opencode-status.json"));return!e||e.projectRoot&&e.projectRoot!==r.rootPath?null:e}function Ye(r){let e=ir(r),n=[...e].sort((u,m)=>(u.lastLocalEventAt??u.updatedAt).localeCompare(m.lastLocalEventAt??m.updatedAt)).at(-1),o=Array.from(new Set(e.map(u=>u.agent))).sort(),s=e.reduce((u,m)=>u+Math.max(0,m.lastSequence-m.lastRemoteSequence),0),i=ar(r),a=i?.state==="capture-failed",c=i?.state==="sync-failed",d;a?d="degraded":s>0||c?d="pending":e.length>0?d="healthy":d="unknown";let p=$e(r);return{ok:d!=="degraded",agents:o,sessions:e.length,latestAgent:n?.agent,latestSessionId:n?.nativeSessionId,lastCaptureAt:Le(e.map(u=>u.lastLocalEventAt??u.updatedAt)),lastFlushAt:Le(e.map(u=>u.lastRemoteAt)),pendingWal:s,currentTask:p?.currentTask?.title,currentFile:p?.activeFiles?.at(-1)??p?.filesTouched.at(-1),syncHealth:d,opencode:i?{state:i.state,reason:i.reason,timestamp:i.timestamp,error:i.error}:void 0}}function We(){return process.argv.includes("--json")}function cr(r){let e=process.stdout.isTTY===!0&&process.env.TERM!=="dumb"&&process.env.NO_COLOR===void 0,t=(l,ne)=>e?`\x1B[${l}m${ne}\x1B[0m`:ne,n=l=>t("38;5;82",l),o=l=>t("38;5;51",l),s=l=>t("38;5;214",l),i=l=>t("38;5;196",l),a=l=>t("38;5;255",l),c=l=>t("2",l),d=l=>t("1",l),p=c("\u2502"),u=c("\u251C");if(console.log(""),console.log(`${o("\u25C7")} ${d(a("ToolNet Memory Doctor"))}`),console.log(""),r.project&&console.log(`${n("\u25C6")} Project ${a(r.project)}`),console.log(p),r.storage){let l=typeof r.storage.provider=="string"?` \u2014 ${r.storage.provider}`:"";r.storage.ok?console.log(`${u} ${n("\u25C6")} Storage${l}`):console.log(`${u} ${i("\u2717")} Storage${l}`)}if((r.memory!==void 0||r.graphSymbols!==void 0||r.codeChunks!==void 0)&&(console.log(p),console.log(`${o("\u25C7")} ${a("Project data")}`),r.memory!==void 0&&console.log(`${u} ${c("Memories")} ${a(String(r.memory))}`),r.graphSymbols!==void 0&&console.log(`${u} ${c("Graph symbols")} ${a(String(r.graphSymbols))}`),r.graphEdges!==void 0&&console.log(`${u} ${c("Graph edges")} ${a(String(r.graphEdges))}`),r.memoryVectors!==void 0&&console.log(`${u} ${c("Memory vectors")} ${a(String(r.memoryVectors))}`),r.codeChunks!==void 0&&console.log(`${u} ${c("Code chunks")} ${a(String(r.codeChunks))}`),r.codeVectors!==void 0&&console.log(`${u} ${c("Code vectors")} ${a(String(r.codeVectors))}`),r.snapshots!==void 0&&console.log(`${u} ${c("Snapshots")} ${a(String(r.snapshots))}`)),r.capture){console.log(p),console.log(`${o("\u25C7")} ${a("Session continuity")}`),console.log(`${u} ${c("Agents")} ${a(r.capture.agents.length?r.capture.agents.join(", "):"none")}`),r.capture.latestAgent&&console.log(`${u} ${c("Latest agent")} ${a(String(r.capture.latestAgent))}`),r.capture.currentTask&&console.log(`${u} ${c("Current task")} ${a(r.capture.currentTask)}`),r.capture.currentFile&&console.log(`${u} ${c("Current file")} ${a(r.capture.currentFile)}`),r.capture.lastCaptureAt&&console.log(`${u} ${c("Last capture")} ${a(r.capture.lastCaptureAt)}`),r.capture.lastFlushAt&&console.log(`${u} ${c("Last flush")} ${a(r.capture.lastFlushAt)}`),console.log(`${u} ${c("Pending WAL")} ${a(String(r.capture.pendingWal))}`);let l=r.capture.syncHealth==="healthy"?n(r.capture.syncHealth):r.capture.syncHealth==="degraded"?i(r.capture.syncHealth):s(r.capture.syncHealth);console.log(`${u} ${c("Sync health")} ${l}`)}let m=r.config?.errors??[];if(m.length>0){console.log(p),console.log(`${i("\u2717")} ${a("Configuration required")}`);for(let l of m)console.log(`${u} ${i("\u2022")} ${l}`)}let H=[...r.config?.warnings??[],...r.warnings??[]],k=[...new Set(H)];if(k.length>0){console.log(p),console.log(`${s("\u25C7")} ${a("Warnings")}`);for(let l of k)console.log(`${u} ${s("!")} ${l}`)}console.log(p),r.ok?console.log(`${n("\u2514 \u25C6")} ${d(a("ToolNet Memory is ready"))}`):(console.log(`${i("\u2514 \u2717")} ${d(a("ToolNet Memory requires attention"))}`),m.length>0&&console.log(c(" Run: toolnet-memory setup"))),console.log("")}function ze(r){We()?console.log(JSON.stringify(r,null,2)):cr(r)}async function ur(){let r=Ne();if(!r.ok){let l={ok:!1,config:r,warnings:r.warnings};ze(l),process.exitCode=1;return}let e=oe(),t=new O().detect(),n=Te(Pe({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),o=new z(n,t.id,t.name,t.remote??t.name),s=await new B(n).run(),i=await new D(o).load(t.id),a=await new F(o).load(t.id),c=await new L(o).load(t.id),d=await new V(o).load(t.id),p=await new Y(o).load(t.id),u=await new W(o).list(t.id),m=Ye(t),H=m.syncHealth==="degraded"?[`Session capture degraded${m.opencode?.error?`: ${m.opencode.error}`:""}`]:[],k={ok:s.ok&&m.ok,project:t.name,storage:s.storage,memory:i.length,graphSymbols:a?.symbols.length??0,graphEdges:a?.edges.length??0,memoryVectors:c?.records.length??0,codeChunks:d?.chunks.length??0,codeVectors:p?.records.length??0,snapshots:u.length,capture:m,warnings:[...r.warnings,...H]};ze(k),process.exitCode=k.ok?0:1}ur().catch(r=>{let e=r instanceof Error?r.message:String(r);We()?console.log(JSON.stringify({ok:!1,error:e},null,2)):(console.error(""),console.error("ToolNet Memory Doctor"),console.error("====================="),console.error(""),console.error(`\u2717 ${e}`),console.error("")),process.exitCode=1});