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.
- package/.env.example +74 -0
- package/.release-target +1 -1
- package/README.md +316 -6
- package/bin/toolnet-memory +12 -0
- package/bundle/agy-hook.js +66 -66
- package/bundle/agy.js +56 -56
- package/bundle/api.js +15 -15
- package/bundle/audit.js +2 -0
- package/bundle/background-refresh.js +7 -7
- package/bundle/claude-hook.js +49 -49
- package/bundle/code-capabilities.js +2 -0
- package/bundle/codex-context.js +29 -29
- package/bundle/codex.js +71 -71
- package/bundle/context-runtime.js +39 -39
- package/bundle/context.js +15 -15
- package/bundle/copilot-hook.js +78 -78
- package/bundle/cursor-hook.js +78 -78
- package/bundle/docker-healthcheck.js +3 -0
- package/bundle/doctor.js +4 -4
- package/bundle/full-index.js +22 -21
- package/bundle/gc.js +5 -3
- package/bundle/graph-cli.js +1 -1
- package/bundle/graph-index.js +5 -4
- package/bundle/graph-ui.js +4 -3
- package/bundle/grok-hook.js +56 -56
- package/bundle/handoff-refresh.js +10 -10
- package/bundle/help-cli.js +4 -4
- package/bundle/impact.js +5 -4
- package/bundle/incremental.js +5 -5
- package/bundle/init.js +36 -35
- package/bundle/kiro-hook.js +85 -85
- package/bundle/learner.js +8 -6
- package/bundle/mcp.js +19 -17
- package/bundle/memory-agent.js +6 -6
- package/bundle/memory-query.js +7 -7
- package/bundle/opencode.js +77 -77
- package/bundle/project-manual.js +7 -7
- package/bundle/public/index.html +51 -2
- package/bundle/recovery-certify.js +9 -9
- package/bundle/runtime.js +16 -13
- package/bundle/semantic.js +6 -5
- package/bundle/service.js +11 -6
- package/bundle/snapshot.js +6 -4
- package/bundle/status-cli.js +4 -4
- package/bundle/toolnet-cli.js +46 -46
- package/bundle/work-continuity.js +7 -7
- package/package.json +38 -2
- package/release-manifest.json +143 -4
package/bundle/audit.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createHash as W}from"node:crypto";import{existsSync as A,mkdirSync as tt,readFileSync as et,renameSync as nt,writeFileSync as rt}from"node:fs";import{basename as ot,dirname as w,join as m,parse as V,resolve as d}from"node:path";import{createHash as O}from"node:crypto";import{spawnSync as q}from"node:child_process";var g="git-remote-v1",K=new Set(["github.com","gitlab.com","bitbucket.org"]);function N(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:(K.has(n)&&(t=t.toLowerCase()),t)}function Y(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,i=N(t,e.pathname);return i?`${o}/${i}`:null}function B(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=N(t,e[2]??"");return r?`${t}/${r}`:null}function T(n){let e=n.trim();return e?e.includes("://")?Y(e):B(e):null}function X(n){return O("sha256").update(`${g}:${n}`).digest("hex")}function v(n){return O("sha256").update(`toolnet-project:${g}:${n}`).digest("hex").slice(0,16)}function Q(n){return n.split("/").filter(Boolean).at(-1)?.trim()||null}function x(n,e){let t=q("git",["-C",n,...e],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return t.error||t.status!==0?null:t.stdout?.trim()||null}function M(n,e){let t=Q(n);return t?{scheme:g,canonicalRemote:n,fingerprint:X(n),repositoryName:t,source:e}:null}function D(n){let e=x(n,["remote","get-url","origin"]);if(e){let o=T(e);if(o)return M(o,"origin")}let t=x(n,["remote"]);if(!t)return null;let r=new Set;for(let o of t.split(/\r?\n/u).map(i=>i.trim()).filter(Boolean)){let i=x(n,["remote","get-url",o]);if(!i)continue;let s=T(i);s&&r.add(s)}return r.size!==1?null:M([...r][0],"unique-remote")}var z=".toolnet",it="project.json";function st(n){return W("sha256").update(n).digest("hex").slice(0,16)}function l(n){return m(n,z,it)}function C(n){return A(l(n))}function L(n,e){let t=d(n),r=V(t).root;for(;;){if(C(t))return t;if(t===r||e&&t===d(e))break;let o=w(t);if(o===t)break;t=o}return null}function b(n){let e=d(n),t=V(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(i=>A(m(e,i))))return e;if(e===t)break;let o=w(e);if(o===e)break;e=o}return d(n)}function h(n){let e;try{e=JSON.parse(et(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:w(w(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 y(n,e){let t=m(n,z);tt(t,{recursive:!0});let r=l(n),o=`${r}.tmp-${process.pid}`;rt(o,JSON.stringify(e,null,2)+`
|
|
2
|
+
`,{encoding:"utf8",mode:384}),nt(o,r)}function u(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 _(n){return{version:1,scheme:g,canonicalRemote:n.canonicalRemote,fingerprint:n.fingerprint,repositoryName:n.repositoryName}}function ct(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 S=class{adopt(e,t){let r=b(d(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(C(r)){let c=h(l(r));if(c.id!==t.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${c.id}`,`requested=${t.id}`].join(" "));return u(c,r)}let o=new Date().toISOString(),i={...t.metadata};t.gitIdentity&&(i.toolnetIdentity=_(t.gitIdentity));let s={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(i).length?i:void 0};return y(r,s),u(s,r)}recordGitIdentity(e,t,r={}){let o=this.requireExisting(e),i=l(o.rootPath),s=h(i),c=ct(s);if(c&&c!==t.fingerprint&&!r.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${c}`,`current=${t.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let a=s.metadata?.toolnetIdentity;return a&&typeof a=="object"&&!Array.isArray(a)&&a.fingerprint===t.fingerprint||(s.metadata={...s.metadata,toolnetIdentity:_(t)},s.updatedAt=new Date().toISOString(),y(o.rootPath,s)),u(s,o.rootPath)}findExisting(e=process.cwd()){let t=d(e),r=b(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(a=>A(m(r,a))),s=L(t,i?r:void 0);if(!s)return null;let c=h(l(s));return u(c,s)}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=d(e),r=b(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(k=>A(m(r,k))),s=L(t,i?r:void 0);if(s){let k=l(s),p=h(k);return p.rootPath!==s&&(p.rootPath=s,p.updatedAt=new Date().toISOString(),y(s,p)),u(p,s)}let c=new Date().toISOString(),a=ot(r),f=D(r),j={version:1,id:f?v(f.canonicalRemote):st(r),name:a,remote:f?.repositoryName??a,rootPath:r,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0,metadata:f?{toolnetIdentity:_(f)}:void 0};return y(r,j),u(j,r)}};import{appendFileSync as zt,closeSync as Ct,existsSync as gt,mkdirSync as $t,openSync as Ht,readFileSync as mt,statSync as Gt,unlinkSync as Ft,writeFileSync as Jt}from"node:fs";import{createHash as ht,randomUUID as Ut}from"node:crypto";import{dirname as Kt,join as F}from"node:path";var at=[{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"}],ut=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function $(n){return n.normalize("NFKC").trim().toLowerCase()}function dt(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 lt(n){return/^[a-f0-9]{32}$/iu.test(n)||/^[a-f0-9]{40}$/iu.test(n)||/^[a-f0-9]{64}$/iu.test(n)}function ft(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 pt(n,e){return n.start<e.end&&e.start<n.end}function H(n){return n.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var E=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let r=$(t);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let i of at){let s=new RegExp(i.regex.source,i.regex.flags);for(let c of e.matchAll(s))c.index===void 0||!c[0]||this.allowed(c[0])||t.push({type:i.type,value:c[0],start:c.index,end:c.index+c[0].length,confidence:i.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let r=H(t),o=[];for(let i of r)o.some(s=>pt(s,i))||o.push(i);return H(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=$(e);return ut.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 i=o[0];this.allowed(i)||lt(i)||!/[A-Za-z]/u.test(i)||!/[0-9]/u.test(i)||ft(e,o.index,o.index+i.length)&&(dt(i)<3.7||t.push({type:"high_entropy_secret",value:i,start:o.index,end:o.index+i.length,confidence:"heuristic"}))}return t}};var I=class{scanner;constructor(e={}){this.scanner=new E(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((s,c)=>c.start-s.start),i=new Set;for(let s of o)i.add(s.type),r=r.slice(0,s.start)+`[REDACTED:${s.type}]`+r.slice(s.end);return{text:r,redacted:t.length,secretTypes:[...i].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 i=r.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");if(i.includes("password")||i.includes("passwd")||i==="pwd"||i.includes("secret")||i.includes("token")||i.includes("cookie")||i.includes("authorization")||i.includes("apikey")||i.includes("accesskey")||i.includes("privatekey")||i.includes("clientsecret")||i.includes("credential")){t[r]="[REDACTED]";continue}t[r]=this.sanitizeValue(o)}return t}return e}};var vt=new I;var G="0".repeat(64);function yt(n){return F(n.rootPath,".toolnet","audit")}function P(n){return F(yt(n),"events.jsonl")}function R(n){if(Array.isArray(n))return n.map(R);if(!n||typeof n!="object")return n;let e=n;return Object.fromEntries(Object.keys(e).sort().map(t=>[t,R(e[t])]))}function At(n){return JSON.stringify(R(n))}function wt(n){return ht("sha256").update(At(n),"utf8").digest("hex")}function J(n){let e=P(n);if(!gt(e))return[];let t=mt(e,"utf8"),r=[];for(let o of t.split(/\r?\n/u)){let i=o.trim();i&&r.push(JSON.parse(i))}return r}function Z(n){let e;try{e=J(n)}catch(o){return{valid:!1,records:0,lastHash:G,firstInvalidLine:1,error:o instanceof Error?o.message:String(o)}}let t=G,r=1;for(let o=0;o<e.length;o+=1){let i=e[o];if(i.sequence!==r)return{valid:!1,records:e.length,lastHash:t,firstInvalidLine:o+1,error:"AUDIT_SEQUENCE_INVALID"};if(i.previousHash!==t)return{valid:!1,records:e.length,lastHash:t,firstInvalidLine:o+1,error:"AUDIT_PREVIOUS_HASH_INVALID"};let{hash:s,...c}=i,a=wt(c);if(s!==a)return{valid:!1,records:e.length,lastHash:t,firstInvalidLine:o+1,error:"AUDIT_HASH_INVALID"};t=s,r+=1}return{valid:!0,records:e.length,lastHash:t}}function U(n,e=50){let t=Math.max(1,Math.min(1e3,Math.trunc(e)));return J(n).slice(-t)}function St(n){let e=n.find(o=>o.startsWith("--limit=")),t=e?e.slice(8):n[n.indexOf("--limit")+1];if(!t)return 50;let r=Number(t);if(!Number.isSafeInteger(r)||r<1)throw new Error(`Invalid --limit: ${t}`);return Math.min(r,1e3)}async function Et(){let n=process.argv.slice(2),e=n[0]??"list",t=new S().requireExisting(process.cwd());if(e==="verify"){let i=Z(t);console.log(JSON.stringify({file:P(t),...i},null,2)),i.valid||(process.exitCode=1);return}let r=St(n),o=U(t,r);if(n.includes("--json")){console.log(JSON.stringify(o,null,2));return}console.log(`Audit: ${P(t)}`),console.log(`Events: ${o.length}`);for(let i of o)console.log([i.at,i.action,i.outcome,i.actor.kind,i.actor.id??""].filter(Boolean).join(" | "))}Et().catch(n=>{console.error(n instanceof Error?n.message:String(n)),process.exitCode=1});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{existsSync as
|
|
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
|
|
3
|
-
`,{encoding:"utf8",mode:384}),Je(n,o)}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}}var E=class{findExisting(e=process.cwd()){let t=w(e),o=ne(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>C(A(o,d))),a=oe(t,s?o:void 0);if(!a)return null;let c=se(T(a));return F(c,a)}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=w(e),o=ne(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>C(A(o,m))),a=oe(t,s?o:void 0);if(a){let m=T(a),f=se(m);return f.rootPath!==a&&(f.rootPath=a,f.updatedAt=new Date().toISOString(),ie(a,f)),F(f,a)}let c=new Date().toISOString(),d=Ye(o),l={version:1,id:Ze(o),name:d,remote:d,rootPath:o,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return ie(o,l),F(l,o)}};var Qe=[{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"}],Xe=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 et(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 tt(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function rt(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 ot(r,e){return r.start<e.end&&e.start<r.end}function de(r){return r.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var _=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 Qe){let a=new RegExp(s.regex.source,s.regex.flags);for(let c of e.matchAll(a))c.index===void 0||!c[0]||this.allowed(c[0])||t.push({type:s.type,value:c[0],start:c.index,end:c.index+c[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let o=de(t),n=[];for(let s of o)n.some(a=>ot(a,s))||n.push(s);return de(n)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=ue(e);return Xe.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)||tt(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||rt(e,n.index,n.index+s.length)&&(et(s)<3.7||t.push({type:"high_entropy_secret",value:s,start:n.index,end:n.index+s.length,confidence:"heuristic"}))}return t}};var N=class{scanner;constructor(e={}){this.scanner=new _(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((a,c)=>c.start-a.start),s=new Set;for(let a of n)s.add(a.type),o=o.slice(0,a.start)+`[REDACTED:${a.type}]`+o.slice(a.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 hr=new N;import{homedir as At}from"node:os";import{join as Rt}from"node:path";import{DeleteObjectCommand as nt,GetObjectCommand as st,HeadObjectCommand as it,ListObjectsV2Command as at,PutObjectCommand as ct,S3Client as ut}from"@aws-sdk/client-s3";import{getSignedUrl as dt}from"@aws-sdk/s3-request-presigner";var D=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new ut({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 ct({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await dt(this.client,new st({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 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 nt({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new at({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 le,mkdir as lt,readFile as pt,readdir as mt,rm as ft,stat as pe,writeFile as gt}from"node:fs/promises";import{dirname as yt,join as ht,relative as me,resolve as St}from"node:path";var R=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return St(this.root,t)}async put(e,t){let o=this.path(e);await lt(yt(o),{recursive:!0}),await gt(o,t)}async get(e){try{return await pt(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 le(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 le(t)}catch{return o}let n=async a=>{let c=await mt(a,{withFileTypes:!0});for(let d of c){let l=ht(a,d.name);if(d.isDirectory()){await n(l);continue}let m=await pe(l);o.push({key:me(this.root,l),size:m.size,updatedAt:m.mtime.toISOString()})}},s=await pe(t);return s.isDirectory()?await n(t):o.push({key:me(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),o}};import{DeleteObjectCommand as kt,GetObjectCommand as bt,HeadObjectCommand as wt,ListObjectsV2Command as vt,PutObjectCommand as xt,S3Client as jt}from"@aws-sdk/client-s3";import{getSignedUrl as Mt}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 jt({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 xt({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await Mt(this.client,new bt({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 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=[],o;do{let n=await this.client.send(new vt({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}};function W(r,e){return console.warn(e),new R(r)}function fe(r){let e=r.localRoot??Rt(At(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new P({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):W(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 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}):W(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 D({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):W(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new R(e)}var $=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")}};function Pt(r){return new Promise(e=>setTimeout(e,r))}async function ge(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,n=e.maxDelayMs??2e3,s;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(s=c,a>=t)break;let d=Math.min(n,o*2**(a-1)),l=Math.floor(Math.random()*Math.max(1,d*.2));await Pt(d+l)}throw s}var It=new Set(["put","get","getText","delete","list"]);function ye(r,e={}){return new Proxy(r,{get(t,o){let n=Reflect.get(t,o,t);return typeof n!="function"?n:It.has(o)?(...s)=>ge(()=>Promise.resolve(n.apply(t,s)),e):n.bind(t)}})}function he(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 Se(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 K=class{constructor(e,t,o,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=o,this.folder=he(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 a;try{a=JSON.parse(n)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(o=a.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)+`
|
|
4
|
-
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=
|
|
5
|
-
`,{encoding:"utf8",mode:384}),
|
|
6
|
-
`,"application/json"),
|
|
7
|
-
`,"application/json"),t}function
|
|
1
|
+
import{existsSync as mt,readFileSync as ft}from"node:fs";import{homedir as gt}from"node:os";import{join as yt}from"node:path";function ht(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 St(){let r=process.env.TOOLNET_GLOBAL_ENV??yt(gt(),".config","toolnet-memory",".env");if(!mt(r))return;let e=ft(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]=ht(n.slice(o+1)))}}St();function A(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function j(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function Se(){return{memory:{autoCapture:A(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:A(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:A(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:A(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:j(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:j(process.env.MEMORY_RERANK_TOP,10),finalContext:j(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:j(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:A(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:j(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Et}from"node:crypto";import{existsSync as H,mkdirSync as Mt,readFileSync as It,renameSync as At,writeFileSync as jt}from"node:fs";import{basename as Pt,dirname as z,join as T,parse as Me,resolve as k}from"node:path";import{createHash as xe}from"node:crypto";import{spawnSync as bt}from"node:child_process";var P="git-remote-v1",wt=new Set(["github.com","gitlab.com","bitbucket.org"]);function ke(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:(wt.has(r)&&(t=t.toLowerCase()),t)}function xt(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=ke(t,e.pathname);return s?`${o}/${s}`:null}function kt(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=ke(t,e[2]??"");return n?`${t}/${n}`:null}function be(r){let e=r.trim();return e?e.includes("://")?xt(e):kt(e):null}function vt(r){return xe("sha256").update(`${P}:${r}`).digest("hex")}function ve(r){return xe("sha256").update(`toolnet-project:${P}:${r}`).digest("hex").slice(0,16)}function Rt(r){return r.split("/").filter(Boolean).at(-1)?.trim()||null}function X(r,e){let t=bt("git",["-C",r,...e],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return t.error||t.status!==0?null:t.stdout?.trim()||null}function we(r,e){let t=Rt(r);return t?{scheme:P,canonicalRemote:r,fingerprint:vt(r),repositoryName:t,source:e}:null}function Re(r){let e=X(r,["remote","get-url","origin"]);if(e){let o=be(e);if(o)return we(o,"origin")}let t=X(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=X(r,["remote","get-url",o]);if(!s)continue;let a=be(s);a&&n.add(a)}return n.size!==1?null:we([...n][0],"unique-remote")}var Ie=".toolnet",Tt="project.json";function Ot(r){return Et("sha256").update(r).digest("hex").slice(0,16)}function E(r){return T(r,Ie,Tt)}function Ae(r){return H(E(r))}function Ee(r,e){let t=k(r),n=Me(t).root;for(;;){if(Ae(t))return t;if(t===n||e&&t===k(e))break;let o=z(t);if(o===t)break;t=o}return null}function ee(r){let e=k(r),t=Me(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>H(T(e,s))))return e;if(e===t)break;let o=z(e);if(o===e)break;e=o}return k(r)}function $(r){let e;try{e=JSON.parse(It(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:z(z(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 L(r,e){let t=T(r,Ie);Mt(t,{recursive:!0});let n=E(r),o=`${n}.tmp-${process.pid}`;jt(o,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),At(o,n)}function x(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 te(r){return{version:1,scheme:P,canonicalRemote:r.canonicalRemote,fingerprint:r.fingerprint,repositoryName:r.repositoryName}}function Ct(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 B=class{adopt(e,t){let n=ee(k(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 c=$(E(n));if(c.id!==t.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${c.id}`,`requested=${t.id}`].join(" "));return x(c,n)}let o=new Date().toISOString(),s={...t.metadata};t.gitIdentity&&(s.toolnetIdentity=te(t.gitIdentity));let a={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 L(n,a),x(a,n)}recordGitIdentity(e,t,n={}){let o=this.requireExisting(e),s=E(o.rootPath),a=$(s),c=Ct(a);if(c&&c!==t.fingerprint&&!n.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${c}`,`current=${t.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let u=a.metadata?.toolnetIdentity;return u&&typeof u=="object"&&!Array.isArray(u)&&u.fingerprint===t.fingerprint||(a.metadata={...a.metadata,toolnetIdentity:te(t)},a.updatedAt=new Date().toISOString(),L(o.rootPath,a)),x(a,o.rootPath)}findExisting(e=process.cwd()){let t=k(e),n=ee(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>H(T(n,u))),a=Ee(t,s?n:void 0);if(!a)return null;let c=$(E(a));return x(c,a)}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=k(e),n=ee(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>H(T(n,m))),a=Ee(t,s?n:void 0);if(a){let m=E(a),l=$(m);return l.rootPath!==a&&(l.rootPath=a,l.updatedAt=new Date().toISOString(),L(a,l)),x(l,a)}let c=new Date().toISOString(),u=Pt(n),d=Re(n),f={version:1,id:d?ve(d.canonicalRemote):Ot(n),name:u,remote:d?.repositoryName??u,rootPath:n,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0,metadata:d?{toolnetIdentity:te(d)}:void 0};return L(n,f),x(f,n)}};var _t=[{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"}],Nt=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function je(r){return r.normalize("NFKC").trim().toLowerCase()}function Kt(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 Dt(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function $t(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 Pe(r){return r.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var V=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let n=je(t);n&&this.allowValues.add(n)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let s of _t){let a=new RegExp(s.regex.source,s.regex.flags);for(let c of e.matchAll(a))c.index===void 0||!c[0]||this.allowed(c[0])||t.push({type:s.type,value:c[0],start:c.index,end:c.index+c[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let n=Pe(t),o=[];for(let s of n)o.some(a=>Lt(a,s))||o.push(s);return Pe(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=je(e);return Nt.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)||Dt(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||$t(e,o.index,o.index+s.length)&&(Kt(s)<3.7||t.push({type:"high_entropy_secret",value:s,start:o.index,end:o.index+s.length,confidence:"heuristic"}))}return t}};var Y=class{scanner;constructor(e={}){this.scanner=new V(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((a,c)=>c.start-a.start),s=new Set;for(let a of o)s.add(a.type),n=n.slice(0,a.start)+`[REDACTED:${a.type}]`+n.slice(a.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 pn=new Y;import{homedir as yr}from"node:os";import{join as hr}from"node:path";import{DeleteObjectCommand as Ht,GetObjectCommand as zt,HeadObjectCommand as Bt,ListObjectsV2Command as Vt,PutObjectCommand as Yt,S3Client as Ft}from"@aws-sdk/client-s3";import{getSignedUrl as Ut}from"@aws-sdk/s3-request-presigner";var F=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ft({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 Yt({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Ut(this.client,new zt({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 Bt({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 Ht({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Vt({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 Te,mkdir as Gt,readFile as Wt,readdir as Jt,rm as qt,stat as Oe,writeFile as Zt}from"node:fs/promises";import{dirname as Qt,join as Xt,relative as Ce,resolve as er}from"node:path";var O=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return er(this.root,t)}async put(e,t){let n=this.path(e);await Gt(Qt(n),{recursive:!0}),await Zt(n,t)}async get(e){try{return await Wt(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 Te(this.path(e)),!0}catch{return!1}}async delete(e){await qt(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),n=[];try{await Te(t)}catch{return n}let o=async a=>{let c=await Jt(a,{withFileTypes:!0});for(let u of c){let d=Xt(a,u.name);if(u.isDirectory()){await o(d);continue}let f=await Oe(d);n.push({key:Ce(this.root,d),size:f.size,updatedAt:f.mtime.toISOString()})}},s=await Oe(t);return s.isDirectory()?await o(t):n.push({key:Ce(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),n}};import{DeleteObjectCommand as tr,GetObjectCommand as rr,HeadObjectCommand as nr,ListObjectsV2Command as or,PutObjectCommand as sr,S3Client as ir}from"@aws-sdk/client-s3";import{getSignedUrl as ar}from"@aws-sdk/s3-request-presigner";var C=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new ir({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 sr({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await ar(this.client,new rr({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 nr({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 tr({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new or({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 cr,createDecipheriv as ur,createHash as dr,randomBytes as lr,timingSafeEqual as pr}from"node:crypto";import{readFileSync as mr}from"node:fs";var v=Buffer.from("TNMEME01","ascii"),Ne=1,_=8,N=12,re=16,Ke=v.length+1+_+N+re,fr="toolnet-memory:remote-encryption:v1:",De="aes-256-gcm",ne=32,p=class extends Error{constructor(t,n){super(n);this.code=t;this.name="RemoteEncryptionError"}code};function gr(r){return r?["1","true","yes","on","enabled"].includes(r.trim().toLowerCase()):!1}function oe(r=process.env){return gr(r.TOOLNET_REMOTE_ENCRYPTION)}function _e(r){let e=r.trim();if(!e)throw new p("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 p("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 p("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!==ne)throw new p("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${ne} bytes.`);return t}function $e(r=process.env){let e=r.TOOLNET_REMOTE_ENCRYPTION_KEY?.trim(),t=r.TOOLNET_REMOTE_ENCRYPTION_KEY_FILE?.trim();if(e&&t)throw new p("REMOTE_ENCRYPTION_KEY_AMBIGUOUS","Configure either TOOLNET_REMOTE_ENCRYPTION_KEY or TOOLNET_REMOTE_ENCRYPTION_KEY_FILE, not both.");if(e)return _e(e);if(t){let n;try{n=mr(t,"utf8")}catch(o){throw new p("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${t}.`,o instanceof Error?o.message:String(o)].join(" "))}return _e(n)}}function Le(r){return dr("sha256").update(r).digest().subarray(0,_)}function He(r){return Buffer.from(`${fr}${r}`,"utf8")}function se(r){return r.byteLength<v.length?!1:Buffer.from(r).subarray(0,v.length).equals(v)}function ze(r,e,t){if(t.byteLength!==ne)throw new p("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=lr(N),s=cr(De,t,o);s.setAAD(He(r));let a=Buffer.concat([s.update(n),s.final()]),c=s.getAuthTag(),u=Buffer.alloc(Ke),d=0;return v.copy(u,d),d+=v.length,u.writeUInt8(Ne,d),d+=1,Le(t).copy(u,d),d+=_,o.copy(u,d),d+=N,c.copy(u,d),Buffer.concat([u,a])}function Be(r,e,t){let n=Buffer.from(e);if(!se(n))throw new p("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(n.length<Ke)throw new p("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let o=v.length,s=n.readUInt8(o);if(o+=1,s!==Ne)throw new p("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${s}.`);let a=n.subarray(o,o+_);o+=_;let c=Le(t);if(!pr(a,c))throw new p("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let u=n.subarray(o,o+N);o+=N;let d=n.subarray(o,o+re);o+=re;let f=n.subarray(o),m=ur(De,t,u);m.setAAD(He(r)),m.setAuthTag(d);try{return Buffer.concat([m.update(f),m.final()])}catch{throw new p("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 p("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 p("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let s=ze(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(!se(t))return t;if(!this.options.enabled)throw new p("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 p("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return Be(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 Ve(r,e=process.env){if(r.name==="local")return oe(e)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),r;let t=oe(e),n=t?$e(e):void 0;return new ie(r,{enabled:t,key:n})}function K(r){return Ve(r)}function ae(r,e){return console.warn(e),K(new O(r))}function Ye(r){let e=r.localRoot??hr(yr(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?K(new C({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(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?K(new C({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(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?K(new F({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):ae(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return K(new O(e))}var U=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")}};function Sr(r){return new Promise(e=>setTimeout(e,r))}async function Fe(r,e={}){let t=Math.max(1,e.attempts??3),n=e.baseDelayMs??150,o=e.maxDelayMs??2e3,s;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(s=c,a>=t)break;let u=Math.min(o,n*2**(a-1)),d=Math.floor(Math.random()*Math.max(1,u*.2));await Sr(u+d)}throw s}var br=new Set(["put","get","getText","delete","list"]);function Ue(r,e={}){return new Proxy(r,{get(t,n){let o=Reflect.get(t,n,t);return typeof o!="function"?o:br.has(n)?(...s)=>Fe(()=>Promise.resolve(o.apply(t,s)),e):o.bind(t)}})}function Ge(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 We(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 G=class{constructor(e,t,n,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=n,this.folder=Ge(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 a;try{a=JSON.parse(o)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(n=a.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=We(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))}};import{join as Ze}from"node:path";import{mkdirSync as Er}from"node:fs";import{createHash as wr}from"node:crypto";import{dirname as xr}from"node:path";import{mkdirSync as kr,readFileSync as ds,renameSync as vr,writeFileSync as Rr}from"node:fs";function Je(r){return wr("sha256").update(r).digest("hex")}function qe(r,e){kr(xr(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;Rr(t,JSON.stringify(e,null,2)+`
|
|
5
|
+
`,{encoding:"utf8",mode:384}),vr(t,r)}function Xe(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function h(r,e=20){let t=[],n=new Set;for(let o of r.slice().reverse()){let s=Xe(o);if(!(!s||n.has(s))&&(n.add(s),t.push(o),t.length>=e))break}return t.reverse()}function Mr(r,e=20){let t=new Map;for(let n of r){let o=`${n.kind}|${Xe(n.command)}`;t.delete(o),t.set(o,n)}return Array.from(t.values()).slice(-e)}function Ir(r){return r.kind==="phase"?/^Phase\s+\d+$/iu.test(r.text):r.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(r.text):!1}function Qe(r,e){let t=e.status??r?.status??"pending",n=t;r&&(r.status==="completed"&&t!=="completed"?n="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(n=r.status));let o=r&&Ir(e)?r.title:e.text;return{id:r?.id??Je(e.key).slice(0,24),title:o,status:n,order:e.order??r?.order,confidence:Math.max(e.confidence,r?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function et(r,e){let t=`projects/${r.id}/work/observations/`,n=await e.list(t),o=[];for(let s of n.filter(a=>a.key.endsWith(".json")).sort((a,c)=>a.key.localeCompare(c.key))){let a=await e.getText(s.key);if(a)try{let c=JSON.parse(a);c.version===1&&Array.isArray(c.observations)&&o.push(c)}catch{}}return o}async function Ar(r,e){let n=(await et(r,e)).flatMap(i=>i.observations).sort((i,y)=>{let ye=i.occurredAt.localeCompare(y.occurredAt);if(ye!==0)return ye;let he=i.sequence-y.sequence;return he!==0?he:i.id.localeCompare(y.id)}),o=new Map,s=new Map,a,c,u,d,f,m=[],l=[],g=[],b=[],M=[],I=new Map,ue=[],de=[],le=[],pe=[],me=[],fe=[];for(let i of n)switch(i.kind){case"request":a=i.text;break;case"activity":c=i.text;break;case"goal":u=i.text;break;case"plan":d=i.text;break;case"phase":o.set(i.key,Qe(o.get(i.key),i));break;case"task":s.set(i.key,Qe(s.get(i.key),i));break;case"decision":m.push(i.text);break;case"blocker":l.push(i.text);break;case"warning":g.push(i.text);break;case"next_action":b.push(i.text);break;case"file":{M.push(i.text);let y=i.fileAction??"active";I.delete(i.text),I.set(i.text,y),y==="modified"?ue.push(i.text):y==="created"?de.push(i.text):y==="deleted"&&le.push(i.text);break}case"command":pe.push(i.text);break;case"test":me.push(i.text),i.checkKind&&fe.push({kind:i.checkKind,command:i.text,status:i.checkStatus??"unknown",updatedAt:i.occurredAt,agent:i.agent,nativeSessionId:i.nativeSessionId});break;case"session":f={agent:i.agent,nativeSessionId:i.nativeSessionId,sessionKey:i.sessionKey,updatedAt:i.occurredAt};break}let w=Array.from(o.values()).sort((i,y)=>(i.order??Number.MAX_SAFE_INTEGER)-(y.order??Number.MAX_SAFE_INTEGER)),S=Array.from(s.values()).sort((i,y)=>(i.order??Number.MAX_SAFE_INTEGER)-(y.order??Number.MAX_SAFE_INTEGER)),Z=w.find(i=>i.status==="in_progress")??w.find(i=>i.status==="blocked")??w.find(i=>i.status==="pending"),D=S.find(i=>i.status==="in_progress")??S.find(i=>i.status==="blocked")??S.find(i=>i.status==="pending"),lt=h([...b,...D?[D.title]:[],...!D&&Z?[Z.title]:[],...S.filter(i=>i.status==="pending").slice(0,5).map(i=>i.title)],8),pt=h([...l,...w.filter(i=>i.status==="blocked").map(i=>i.title),...S.filter(i=>i.status==="blocked").map(i=>i.title)],20),Q={version:1,projectId:r.id,projectName:r.name,currentRequest:a,currentActivity:c,goal:u,plan:d,phases:w,tasks:S,decisions:h(m,20),blockers:pt,warnings:h(g,20),nextActions:lt,filesTouched:h(M,30),activeFiles:Array.from(I.entries()).filter(([,i])=>i!=="deleted").map(([i])=>i).slice(-5),modifiedFiles:h(ue,30),createdFiles:h(de,30),deletedFiles:h(le,30),commands:h(pe,20),tests:h(me,20),checks:Mr(fe,20),currentPhase:Z,currentTask:D,progress:{phasesTotal:w.length,phasesCompleted:w.filter(i=>i.status==="completed").length,tasksTotal:S.length,tasksCompleted:S.filter(i=>i.status==="completed").length,blocked:w.filter(i=>i.status==="blocked").length+S.filter(i=>i.status==="blocked").length},lastSession:f,updatedAt:n.length?n[n.length-1].occurredAt:new Date().toISOString()},ge=Ze(r.rootPath,".toolnet","work");return Er(ge,{recursive:!0}),qe(Ze(ge,"current.json"),Q),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(Q,null,2)+`
|
|
6
|
+
`,"application/json"),Q}async function tt(r,e){if((await et(r,e)).length>0)return Ar(r,e);let n=await e.getText(`projects/${r.id}/work/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}import{createHash as Nr}from"node:crypto";import{hostname as Kr}from"node:os";import{createHash as jr,randomUUID as Pr}from"node:crypto";function R(r,e){let t=r.trim();if(!t)throw new Error(`${e} is required`);return t}function W(r,e){return encodeURIComponent(R(r,e))}function Tr(r){let e=JSON.stringify(r);if(e===void 0)throw new Error("Multi-host operation payload must be JSON serializable");return e}function nt(r){return jr("sha256").update(r).digest("hex")}function ot(r){return nt(Tr(r))}function st(r){return Number.isFinite(Date.parse(r))}function Or(r){return nt(JSON.stringify({version:r.version,operationId:r.operationId,projectId:r.projectId,scope:r.scope,hostId:r.hostId,occurredAt:r.occurredAt,payloadSha256:r.payloadSha256,payload:r.payload}))}function it(r){let e=R(r.projectId,"projectId"),t=R(r.scope,"scope"),n=R(r.hostId,"hostId"),o=R(r.operationId??Pr(),"operationId"),s=r.occurredAt??new Date().toISOString();if(!st(s))throw new Error(`Invalid operation timestamp: ${s}`);return{version:1,operationId:o,projectId:e,scope:t,hostId:n,occurredAt:s,payloadSha256:ot(r.payload),payload:r.payload}}function at(r,e){let t=["projects",W(r,"projectId"),"operations"];return e?`${[...t,W(e,"scope")].join("/")}/`:`${t.join("/")}/`}function Cr(r){return[at(r.projectId,r.scope).replace(/\/$/,""),W(r.hostId,"hostId"),`${W(r.operationId,"operationId")}-${r.payloadSha256}.json`].join("/")}async function ct(r,e){let t=Cr(e);return await r.put(t,`${JSON.stringify(e,null,2)}
|
|
7
|
+
`,"application/json"),t}function _r(r){let e;try{e=JSON.parse(r)}catch{return null}return e.version!==1||typeof e.operationId!="string"||typeof e.projectId!="string"||typeof e.scope!="string"||typeof e.hostId!="string"||typeof e.occurredAt!="string"||!st(e.occurredAt)||typeof e.payloadSha256!="string"||ot(e.payload)!==e.payloadSha256?null:e}function rt(r,e){let t=r.occurredAt.localeCompare(e.occurredAt);if(t!==0)return t;let n=r.hostId.localeCompare(e.hostId);if(n!==0)return n;let o=r.operationId.localeCompare(e.operationId);return o!==0?o:r.payloadSha256.localeCompare(e.payloadSha256)}async function ut(r,e,t){let n=R(e,"projectId"),o=R(t,"scope"),s=at(n,o),a=await r.list(s),c=[],u=[];for(let l of a){let g=await r.getText(l.key);if(!g){c.push(l.key);continue}let b=_r(g);if(!b){c.push(l.key);continue}if(b.projectId!==n){c.push(l.key);continue}if(b.scope!==o){c.push(l.key);continue}u.push(b)}let d=new Map;for(let l of u){let g=d.get(l.operationId);if(!g){d.set(l.operationId,[l]);continue}g.push(l)}let f=[],m=[];for(let[l,g]of d){if(new Set(g.map(I=>Or(I))).size>1){m.push({operationId:l,variants:[...g].sort(rt)});continue}let M=g[0];M&&f.push(M)}return f.sort(rt),m.sort((l,g)=>l.operationId.localeCompare(g.operationId)),c.sort(),{operations:f,conflicts:m,invalidKeys:c}}function Dr(r){let t=(r??process.env.TOOLNET_HOST_ID??Kr()).trim();return t||`pid-${process.pid}`}function $r(r){let e=t=>{if(t===null||typeof t!="object")return t;if(Array.isArray(t))return t.map(o=>e(o));let n=t;return Object.fromEntries(Object.keys(n).sort().map(o=>[o,e(n[o])]))};return JSON.stringify(e(r))}function J(r){return Nr("sha256").update($r(r)).digest("hex")}function Lr(r,e){let t=J(r).slice(0,12),n=J(e);return["memory",t,n].join("-")}function Hr(r,e){let t=r.updatedAt.localeCompare(e.updatedAt);if(t!==0)return t;let n=r.createdAt.localeCompare(e.createdAt);return n!==0?n:J(r).localeCompare(J(e))}function zr(r,e){return Hr(r,e)>=0?r:e}function ce(r,e){let t=new Map;for(let n of r)t.set(n.id,n);for(let n of e){if(n.payload.kind!=="upsert")continue;let o=n.payload.memory,s=t.get(o.id);if(!s){t.set(o.id,o);continue}t.set(o.id,zr(s,o))}return[...t.values()].sort((n,o)=>n.id.localeCompare(o.id))}var q=class{constructor(e,t={}){this.storage=e;this.legacy=new U(e),this.hostId=Dr(t.hostId)}storage;legacy;hostId;diagnostics={operationCount:0,conflicts:0,invalidKeys:0};getDiagnostics(){return{...this.diagnostics}}async operations(e){let t=await ut(this.storage,e,"memory");return this.diagnostics={operationCount:t.operations.length,conflicts:t.conflicts.length,invalidKeys:t.invalidKeys.length},t.operations}async load(e){let t=await this.legacy.load(e),n=await this.operations(e);return ce(t,n)}async appendMemory(e,t){let n=it({projectId:e,scope:"memory",hostId:this.hostId,operationId:Lr(this.hostId,t),occurredAt:t.updatedAt,payload:{kind:"upsert",memory:t}});await ct(this.storage,n)}async refreshProjection(e){let t=await this.legacy.load(e),n=await this.operations(e),o=ce(t,n);return await this.legacy.save(e,o),o}async save(e,t){let n=[...t].sort((a,c)=>a.id.localeCompare(c.id));for(let a of n)await this.appendMemory(e,a);let o=await this.operations(e),s=ce(t,o);await this.legacy.save(e,s)}};async function dt(r,e){let t=new Date().toISOString(),o=await new q(e).refreshProjection(r.id),s=await tt(r,e);return{startedAt:t,finishedAt:new Date().toISOString(),memories:o.length,workAvailable:s!==null}}function Br(r,e){let t=r.indexOf(e);if(!(t<0))return r[t+1]}function Vr(r){return{projectPath:Br(r,"--project"),json:r.includes("--json"),quiet:r.includes("--quiet")}}async function Yr(r){let e=Se(),t=Ue(Ye({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new G(t,r.id,r.name,r.remote??r.name)}async function Fr(){let r=Vr(process.argv.slice(2)),e=r.projectPath??process.cwd(),t=new B().detect(e);if(!t){r.quiet||console.error(`ToolNet project not found: ${e}`),process.exitCode=2;return}let n;try{n=await Yr(t)}catch(o){r.quiet||console.error(o instanceof Error?o.message:String(o)),process.exitCode=3;return}try{let o=await dt(t,n);if(r.quiet)return;if(r.json){console.log(JSON.stringify(o));return}console.log(["ToolNet Memory refreshed",`memory=${o.memories}`,`work=${o.workAvailable?"yes":"no"}`].join(" "))}catch(o){r.quiet||console.error(o instanceof Error?o.message:String(o)),process.exitCode=4}}Fr().then(()=>{process.exit(process.exitCode??0)});
|