toolnet-memory 0.3.16 → 0.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +74 -0
- package/.release-target +1 -1
- package/README.md +157 -7
- package/bin/toolnet-memory +12 -0
- package/bundle/agy-hook.js +59 -59
- package/bundle/agy.js +58 -58
- package/bundle/api.js +15 -15
- package/bundle/audit.js +2 -0
- package/bundle/background-refresh.js +7 -7
- package/bundle/code-capabilities.js +2 -0
- package/bundle/codex.js +63 -63
- package/bundle/context-runtime.js +41 -41
- package/bundle/context.js +15 -15
- package/bundle/copilot-hook.js +73 -73
- package/bundle/cursor-hook.js +73 -73
- package/bundle/docker-healthcheck.js +3 -0
- package/bundle/doctor.js +4 -4
- package/bundle/full-index.js +20 -19
- package/bundle/gc.js +5 -3
- package/bundle/graph-cli.js +1 -1
- package/bundle/graph-index.js +5 -4
- package/bundle/graph-ui.js +4 -3
- package/bundle/grok-hook.js +52 -52
- package/bundle/handoff-refresh.js +2 -2
- package/bundle/help-cli.js +4 -4
- package/bundle/impact.js +5 -4
- package/bundle/incremental.js +5 -5
- package/bundle/init.js +35 -35
- package/bundle/kiro-hook.js +74 -74
- package/bundle/learner.js +8 -6
- package/bundle/mcp.js +19 -17
- package/bundle/memory-agent.js +4 -4
- package/bundle/memory-query.js +3 -3
- package/bundle/opencode.js +73 -73
- package/bundle/project-manual.js +7 -7
- package/bundle/public/index.html +1089 -2306
- package/bundle/runtime.js +16 -13
- package/bundle/semantic.js +6 -5
- package/bundle/service.js +11 -6
- package/bundle/snapshot.js +6 -4
- package/bundle/status-cli.js +4 -4
- package/bundle/toolnet-cli.js +49 -49
- package/bundle/work-continuity.js +7 -7
- package/package.json +26 -2
- package/release-manifest.json +57 -1
package/bundle/project-manual.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{readFileSync as
|
|
2
|
-
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function
|
|
3
|
-
`,{encoding:"utf8",mode:384}),Re(o,r)}function d(n,t){return{id:n.id,name:n.name,remote:n.remote,rootPath:t,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}function _(n){return{version:1,scheme:h,canonicalRemote:n.canonicalRemote,fingerprint:n.fingerprint,repositoryName:n.repositoryName}}function Ie(n){let t=n.metadata?.toolnetIdentity;if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.fingerprint=="string"?e.fingerprint:null}var R=class{adopt(t,e){let r=T(l(t));if(!e.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!e.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!e.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(Y(r)){let a=w(p(r));if(a.id!==e.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${a.id}`,`requested=${e.id}`].join(" "));return d(a,r)}let o=new Date().toISOString(),i={...e.metadata};e.gitIdentity&&(i.toolnetIdentity=_(e.gitIdentity));let s={version:1,id:e.id.trim(),name:e.name.trim(),remote:e.remote.trim(),rootPath:r,createdAt:e.createdAt??o,updatedAt:o,graphVersion:e.graphVersion??0,memoryVersion:e.memoryVersion??0,metadata:Object.keys(i).length?i:void 0};return v(r,s),d(s,r)}recordGitIdentity(t,e,r={}){let o=this.requireExisting(t),i=p(o.rootPath),s=w(i),a=Ie(s);if(a&&a!==e.fingerprint&&!r.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${a}`,`current=${e.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let c=s.metadata?.toolnetIdentity;return c&&typeof c=="object"&&!Array.isArray(c)&&c.fingerprint===e.fingerprint||(s.metadata={...s.metadata,toolnetIdentity:_(e)},s.updatedAt=new Date().toISOString(),v(o.rootPath,s)),d(s,o.rootPath)}findExisting(t=process.cwd()){let e=l(t),r=T(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>j(S(r,c))),s=J(e,i?r:void 0);if(!s)return null;let a=w(p(s));return d(a,s)}requireExisting(t=process.cwd()){let e=this.findExisting(t);if(!e)throw new Error("PROJECT_NOT_INITIALIZED");return e}detect(t=process.cwd()){let e=l(t),r=T(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(I=>j(S(r,I))),s=J(e,i?r:void 0);if(s){let I=p(s),f=w(I);return f.rootPath!==s&&(f.rootPath=s,f.updatedAt=new Date().toISOString(),v(s,f)),d(f,s)}let a=new Date().toISOString(),c=Ae(r),u=G(r),m={version:1,id:u?B(u.canonicalRemote):Ce(r),name:c,remote:u?.repositoryName??c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0,metadata:u?{toolnetIdentity:_(u)}:void 0};return v(r,m),d(m,r)}};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 W(n){return n.normalize("NFKC").trim().toLowerCase()}function _e(n){if(n.length===0)return 0;let t=new Map;for(let r of n)t.set(r,(t.get(r)??0)+1);let e=0;for(let r of t.values()){let o=r/n.length;e-=o*Math.log2(o)}return e}function Oe(n){return/^[a-f0-9]{32}$/iu.test(n)||/^[a-f0-9]{40}$/iu.test(n)||/^[a-f0-9]{64}$/iu.test(n)}function Ne(n,t,e){let r=n.slice(Math.max(0,t-48),t),o=n.slice(e,Math.min(n.length,e+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${o}`)}function Ke(n,t){return n.start<t.end&&t.start<n.end}function Z(n){return n.sort((t,e)=>t.start!==e.start?t.start-e.start:e.end-e.start-(t.end-t.start))}var k=class{allowValues=new Set;enableEntropyHeuristic;constructor(t={}){for(let e of t.allowValues??[]){let r=W(e);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=t.enableEntropyHeuristic??!0}scan(t){let e=[];for(let i of Ee){let s=new RegExp(i.regex.source,i.regex.flags);for(let a of t.matchAll(s))a.index===void 0||!a[0]||this.allowed(a[0])||e.push({type:i.type,value:a[0],start:a.index,end:a.index+a[0].length,confidence:i.confidence})}this.enableEntropyHeuristic&&e.push(...this.entropyMatches(t));let r=Z(e),o=[];for(let i of r)o.some(s=>Ke(s,i))||o.push(i);return Z(o)}hasSecrets(t){return this.scan(t).length>0}allowed(t){let e=W(t);return Te.has(e)?!0:this.allowValues.has(e)}entropyMatches(t){let e=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of t.matchAll(r)){if(o.index===void 0||!o[0])continue;let i=o[0];this.allowed(i)||Oe(i)||!/[A-Za-z]/u.test(i)||!/[0-9]/u.test(i)||Ne(t,o.index,o.index+i.length)&&(_e(i)<3.7||e.push({type:"high_entropy_secret",value:i,start:o.index,end:o.index+i.length,confidence:"heuristic"}))}return e}};var A=class{scanner;constructor(t={}){this.scanner=new k(t)}sanitize(t){let e=this.scanner.scan(t);if(e.length===0)return{text:t,redacted:0,secretTypes:[]};let r=t,o=[...e].sort((s,a)=>a.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:e.length,secretTypes:[...i].sort()}}sanitizeValue(t){if(typeof t=="string")return this.sanitize(t).text;if(Array.isArray(t))return t.map(e=>this.sanitizeValue(e));if(t&&typeof t=="object"){let e={};for(let[r,o]of Object.entries(t)){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")){e[r]="[REDACTED]";continue}e[r]=this.sanitizeValue(o)}return e}return t}};var Vt=new A;import{homedir as it}from"node:os";import{join as st}from"node:path";import{DeleteObjectCommand as $e,GetObjectCommand as De,HeadObjectCommand as ze,ListObjectsV2Command as Le,PutObjectCommand as Ve,S3Client as Fe}from"@aws-sdk/client-s3";import{getSignedUrl as Be}from"@aws-sdk/s3-request-presigner";var P=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Fe({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,r="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Ve({Bucket:this.bucket,Key:t,Body:o,ContentType:r}))}async get(t){let e=await Be(this.client,new De({Bucket:this.bucket,Key:t}),{expiresIn:60}),r=await fetch(e,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new ze({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new $e({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],r;do{let o=await this.client.send(new Le({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return e}};import{access as q,mkdir as Ge,readFile as Je,readdir as He,rm as Ue,stat as Q,writeFile as Ye}from"node:fs/promises";import{dirname as We,join as Ze,relative as X,resolve as qe}from"node:path";var x=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return qe(this.root,e)}async put(t,e){let r=this.path(t);await Ge(We(r),{recursive:!0}),await Ye(r,e)}async get(t){try{return await Je(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await q(this.path(t)),!0}catch{return!1}}async delete(t){await Ue(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),r=[];try{await q(e)}catch{return r}let o=async s=>{let a=await He(s,{withFileTypes:!0});for(let c of a){let u=Ze(s,c.name);if(c.isDirectory()){await o(u);continue}let m=await Q(u);r.push({key:X(this.root,u),size:m.size,updatedAt:m.mtime.toISOString()})}},i=await Q(e);return i.isDirectory()?await o(e):r.push({key:X(this.root,e),size:i.size,updatedAt:i.mtime.toISOString()}),r}};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 b=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new nt({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,r="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new rt({Bucket:this.bucket,Key:t,Body:o,ContentType:r}))}async get(t){let e=await ot(this.client,new Xe({Bucket:this.bucket,Key:t}),{expiresIn:60}),r=await fetch(e,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new et({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Qe({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],r;do{let o=await this.client.send(new tt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return e}};function O(n,t){return console.warn(t),new x(n)}function ee(n){let t=n.localRoot??st(it(),".toolnet-memory","storage");if(n.provider==="r2"){let e=n.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new b({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):O(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let e=n.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new b({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):O(t,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let e=n.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new P({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):O(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new x(t)}function at(n){return new Promise(t=>setTimeout(t,n))}async function te(n,t={}){let e=Math.max(1,t.attempts??3),r=t.baseDelayMs??150,o=t.maxDelayMs??2e3,i;for(let s=1;s<=e;s++)try{return await n()}catch(a){if(i=a,s>=e)break;let c=Math.min(o,r*2**(s-1)),u=Math.floor(Math.random()*Math.max(1,c*.2));await at(c+u)}throw i}var ct=new Set(["put","get","getText","delete","list"]);function re(n,t={}){return new Proxy(n,{get(e,r){let o=Reflect.get(e,r,e);return typeof o!="function"?o:ct.has(r)?(...i)=>te(()=>Promise.resolve(o.apply(e,i)),t):o.bind(e)}})}function ne(n){let t=n.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!t||t==="."||t==="..")throw new Error("Invalid project storage folder");return t}function oe(n){let t=n.replace(/^\/+/,"");if(t.startsWith("memories/"))return"memory/records/"+t.slice(9);if(t.startsWith("vectors/"))return"memory/vectors/"+t.slice(8);if(t.startsWith("graph/"))return"code/graph/"+t.slice(6);let e=t.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(e)return`${e[1]}memory/records/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),e)return`${e[1]}memory/vectors/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),e)return`${e[1]}code/graph/${e[2]}`;let r=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=t.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=t.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=t.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:t))}var C=class{constructor(t,e,r,o){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=r,this.folder=ne(o??r),this.sourcePrefix=`projects/${e}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let t=`${this.targetPrefix}/project.json`,e=new Date().toISOString(),r=e,o=await this.provider.getText(t);if(o){let s;try{s=JSON.parse(o)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${a instanceof Error?a.message:String(a)}`)}if(typeof s.id=="string"&&s.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${s.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof s.createdAt=="string"&&(r=s.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:e};await this.provider.put(t,JSON.stringify(i,null,2)+`
|
|
4
|
-
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=
|
|
1
|
+
import{readFileSync as Gt}from"node:fs";import{existsSync as Te,readFileSync as Ie}from"node:fs";import{homedir as Ce}from"node:os";import{join as _e}from"node:path";function Oe(n){let t=n.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function Ne(){let n=process.env.TOOLNET_GLOBAL_ENV??_e(Ce(),".config","toolnet-memory",".env");if(!Te(n))return;let t=Ie(n,"utf8");for(let e of t.split(/\r?\n/)){let r=e.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let i=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=Oe(r.slice(o+1)))}}Ne();function S(n,t){return n===void 0?t:["1","true","yes","on"].includes(n.toLowerCase())}function b(n,t){if(!n)return t;let e=Number(n);return Number.isFinite(e)?e:t}function J(){return{memory:{autoCapture:S(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:S(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:S(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:S(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:b(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:b(process.env.MEMORY_RERANK_TOP,10),finalContext:b(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:b(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:S(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:b(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Ye}from"node:crypto";import{existsSync as k,mkdirSync as Be,readFileSync as Ue,renameSync as Fe,writeFileSync as Ge}from"node:fs";import{basename as He,dirname as T,join as x,parse as re,resolve as f}from"node:path";import{createHash as q}from"node:crypto";import{spawnSync as Ke}from"node:child_process";var w="git-remote-v1",De=new Set(["github.com","gitlab.com","bitbucket.org"]);function Q(n,t){let e=t.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!e||e==="."||e===".."||e.split("/").some(r=>!r||r==="."||r==="..")?null:(De.has(n)&&(e=e.toLowerCase()),e)}function $e(n){let t;try{t=new URL(n)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(t.protocol))return null;let e=t.hostname.trim().toLowerCase();if(!e)return null;let r=t.protocol==="https:"&&t.port==="443"||t.protocol==="http:"&&t.port==="80"||t.protocol==="ssh:"&&t.port==="22",o=t.port&&!r?`${e}:${t.port}`:e,i=Q(e,t.pathname);return i?`${o}/${i}`:null}function Le(n){let t=n.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!t)return null;let e=t[1]?.trim().toLowerCase();if(!e||e.length===1)return null;let r=Q(e,t[2]??"");return r?`${e}/${r}`:null}function W(n){let t=n.trim();return t?t.includes("://")?$e(t):Le(t):null}function ze(n){return q("sha256").update(`${w}:${n}`).digest("hex")}function X(n){return q("sha256").update(`toolnet-project:${w}:${n}`).digest("hex").slice(0,16)}function Ve(n){return n.split("/").filter(Boolean).at(-1)?.trim()||null}function K(n,t){let e=Ke("git",["-C",n,...t],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return e.error||e.status!==0?null:e.stdout?.trim()||null}function Z(n,t){let e=Ve(n);return e?{scheme:w,canonicalRemote:n,fingerprint:ze(n),repositoryName:e,source:t}:null}function ee(n){let t=K(n,["remote","get-url","origin"]);if(t){let o=W(t);if(o)return Z(o,"origin")}let e=K(n,["remote"]);if(!e)return null;let r=new Set;for(let o of e.split(/\r?\n/u).map(i=>i.trim()).filter(Boolean)){let i=K(n,["remote","get-url",o]);if(!i)continue;let s=W(i);s&&r.add(s)}return r.size!==1?null:Z([...r][0],"unique-remote")}var ne=".toolnet",Je="project.json";function We(n){return Ye("sha256").update(n).digest("hex").slice(0,16)}function y(n){return x(n,ne,Je)}function oe(n){return k(y(n))}function te(n,t){let e=f(n),r=re(e).root;for(;;){if(oe(e))return e;if(e===r||t&&e===f(t))break;let o=T(e);if(o===e)break;e=o}return null}function D(n){let t=f(n),e=re(t).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(i=>k(x(t,i))))return t;if(t===e)break;let o=T(t);if(o===t)break;t=o}return f(n)}function j(n){let t;try{t=JSON.parse(Ue(n,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${n}: ${o instanceof Error?o.message:String(o)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${n}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${n}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${n}`);let r=new Date().toISOString();return{version:1,id:e.id,name:e.name,remote:typeof e.remote=="string"&&e.remote.trim()?e.remote:e.name,rootPath:typeof e.rootPath=="string"?e.rootPath:T(T(n)),createdAt:typeof e.createdAt=="string"?e.createdAt:r,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:r,graphVersion:typeof e.graphVersion=="number"?e.graphVersion:0,memoryVersion:typeof e.memoryVersion=="number"?e.memoryVersion:0,metadata:e.metadata&&typeof e.metadata=="object"?e.metadata:void 0}}function A(n,t){let e=x(n,ne);Be(e,{recursive:!0});let r=y(n),o=`${r}.tmp-${process.pid}`;Ge(o,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),Fe(o,r)}function m(n,t){return{id:n.id,name:n.name,remote:n.remote,rootPath:t,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}function $(n){return{version:1,scheme:w,canonicalRemote:n.canonicalRemote,fingerprint:n.fingerprint,repositoryName:n.repositoryName}}function Ze(n){let t=n.metadata?.toolnetIdentity;if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.fingerprint=="string"?e.fingerprint:null}var I=class{adopt(t,e){let r=D(f(t));if(!e.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!e.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!e.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(oe(r)){let a=j(y(r));if(a.id!==e.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${a.id}`,`requested=${e.id}`].join(" "));return m(a,r)}let o=new Date().toISOString(),i={...e.metadata};e.gitIdentity&&(i.toolnetIdentity=$(e.gitIdentity));let s={version:1,id:e.id.trim(),name:e.name.trim(),remote:e.remote.trim(),rootPath:r,createdAt:e.createdAt??o,updatedAt:o,graphVersion:e.graphVersion??0,memoryVersion:e.memoryVersion??0,metadata:Object.keys(i).length?i:void 0};return A(r,s),m(s,r)}recordGitIdentity(t,e,r={}){let o=this.requireExisting(t),i=y(o.rootPath),s=j(i),a=Ze(s);if(a&&a!==e.fingerprint&&!r.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${a}`,`current=${e.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let c=s.metadata?.toolnetIdentity;return c&&typeof c=="object"&&!Array.isArray(c)&&c.fingerprint===e.fingerprint||(s.metadata={...s.metadata,toolnetIdentity:$(e)},s.updatedAt=new Date().toISOString(),A(o.rootPath,s)),m(s,o.rootPath)}findExisting(t=process.cwd()){let e=f(t),r=D(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>k(x(r,c))),s=te(e,i?r:void 0);if(!s)return null;let a=j(y(s));return m(a,s)}requireExisting(t=process.cwd()){let e=this.findExisting(t);if(!e)throw new Error("PROJECT_NOT_INITIALIZED");return e}detect(t=process.cwd()){let e=f(t),r=D(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(l=>k(x(r,l))),s=te(e,i?r:void 0);if(s){let l=y(s),h=j(l);return h.rootPath!==s&&(h.rootPath=s,h.updatedAt=new Date().toISOString(),A(s,h)),m(h,s)}let a=new Date().toISOString(),c=He(r),u=ee(r),p={version:1,id:u?X(u.canonicalRemote):We(r),name:c,remote:u?.repositoryName??c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0,metadata:u?{toolnetIdentity:$(u)}:void 0};return A(r,p),m(p,r)}};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"}],Qe=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function ie(n){return n.normalize("NFKC").trim().toLowerCase()}function Xe(n){if(n.length===0)return 0;let t=new Map;for(let r of n)t.set(r,(t.get(r)??0)+1);let e=0;for(let r of t.values()){let o=r/n.length;e-=o*Math.log2(o)}return e}function et(n){return/^[a-f0-9]{32}$/iu.test(n)||/^[a-f0-9]{40}$/iu.test(n)||/^[a-f0-9]{64}$/iu.test(n)}function tt(n,t,e){let r=n.slice(Math.max(0,t-48),t),o=n.slice(e,Math.min(n.length,e+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${o}`)}function rt(n,t){return n.start<t.end&&t.start<n.end}function se(n){return n.sort((t,e)=>t.start!==e.start?t.start-e.start:e.end-e.start-(t.end-t.start))}var C=class{allowValues=new Set;enableEntropyHeuristic;constructor(t={}){for(let e of t.allowValues??[]){let r=ie(e);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=t.enableEntropyHeuristic??!0}scan(t){let e=[];for(let i of qe){let s=new RegExp(i.regex.source,i.regex.flags);for(let a of t.matchAll(s))a.index===void 0||!a[0]||this.allowed(a[0])||e.push({type:i.type,value:a[0],start:a.index,end:a.index+a[0].length,confidence:i.confidence})}this.enableEntropyHeuristic&&e.push(...this.entropyMatches(t));let r=se(e),o=[];for(let i of r)o.some(s=>rt(s,i))||o.push(i);return se(o)}hasSecrets(t){return this.scan(t).length>0}allowed(t){let e=ie(t);return Qe.has(e)?!0:this.allowValues.has(e)}entropyMatches(t){let e=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of t.matchAll(r)){if(o.index===void 0||!o[0])continue;let i=o[0];this.allowed(i)||et(i)||!/[A-Za-z]/u.test(i)||!/[0-9]/u.test(i)||tt(t,o.index,o.index+i.length)&&(Xe(i)<3.7||e.push({type:"high_entropy_secret",value:i,start:o.index,end:o.index+i.length,confidence:"heuristic"}))}return e}};var _=class{scanner;constructor(t={}){this.scanner=new C(t)}sanitize(t){let e=this.scanner.scan(t);if(e.length===0)return{text:t,redacted:0,secretTypes:[]};let r=t,o=[...e].sort((s,a)=>a.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:e.length,secretTypes:[...i].sort()}}sanitizeValue(t){if(typeof t=="string")return this.sanitize(t).text;if(Array.isArray(t))return t.map(e=>this.sanitizeValue(e));if(t&&typeof t=="object"){let e={};for(let[r,o]of Object.entries(t)){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")){e[r]="[REDACTED]";continue}e[r]=this.sanitizeValue(o)}return e}return t}};var gr=new _;import{homedir as _t}from"node:os";import{join as Ot}from"node:path";import{DeleteObjectCommand as nt,GetObjectCommand as ot,HeadObjectCommand as it,ListObjectsV2Command as st,PutObjectCommand as at,S3Client as ct}from"@aws-sdk/client-s3";import{getSignedUrl as ut}from"@aws-sdk/s3-request-presigner";var O=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new ct({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,r="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new at({Bucket:this.bucket,Key:t,Body:o,ContentType:r}))}async get(t){let e=await ut(this.client,new ot({Bucket:this.bucket,Key:t}),{expiresIn:60}),r=await fetch(e,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new it({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new nt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],r;do{let o=await this.client.send(new st({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return e}};import{access as ae,mkdir as dt,readFile as lt,readdir as pt,rm as mt,stat as ce,writeFile as ft}from"node:fs/promises";import{dirname as gt,join as yt,relative as ue,resolve as ht}from"node:path";var E=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return ht(this.root,e)}async put(t,e){let r=this.path(t);await dt(gt(r),{recursive:!0}),await ft(r,e)}async get(t){try{return await lt(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await ae(this.path(t)),!0}catch{return!1}}async delete(t){await mt(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),r=[];try{await ae(e)}catch{return r}let o=async s=>{let a=await pt(s,{withFileTypes:!0});for(let c of a){let u=yt(s,c.name);if(c.isDirectory()){await o(u);continue}let p=await ce(u);r.push({key:ue(this.root,u),size:p.size,updatedAt:p.mtime.toISOString()})}},i=await ce(e);return i.isDirectory()?await o(e):r.push({key:ue(this.root,e),size:i.size,updatedAt:i.mtime.toISOString()}),r}};import{DeleteObjectCommand as St,GetObjectCommand as bt,HeadObjectCommand as wt,ListObjectsV2Command as xt,PutObjectCommand as Et,S3Client as Rt}from"@aws-sdk/client-s3";import{getSignedUrl as vt}from"@aws-sdk/s3-request-presigner";var R=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new Rt({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,r="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Et({Bucket:this.bucket,Key:t,Body:o,ContentType:r}))}async get(t){let e=await vt(this.client,new bt({Bucket:this.bucket,Key:t}),{expiresIn:60}),r=await fetch(e,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new wt({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new St({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],r;do{let o=await this.client.send(new xt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return e}};import{createCipheriv as Mt,createDecipheriv as Pt,createHash as jt,randomBytes as At,timingSafeEqual as kt}from"node:crypto";import{readFileSync as Tt}from"node:fs";var g=Buffer.from("TNMEME01","ascii"),le=1,v=8,M=12,L=16,pe=g.length+1+v+M+L,It="toolnet-memory:remote-encryption:v1:",me="aes-256-gcm",z=32,d=class extends Error{constructor(e,r){super(r);this.code=e;this.name="RemoteEncryptionError"}code};function Ct(n){return n?["1","true","yes","on","enabled"].includes(n.trim().toLowerCase()):!1}function V(n=process.env){return Ct(n.TOOLNET_REMOTE_ENCRYPTION)}function de(n){let t=n.trim();if(!t)throw new d("REMOTE_ENCRYPTION_KEY_EMPTY","Remote encryption key is empty.");let e;if(t.startsWith("hex:")){let r=t.slice(4);if(!/^[0-9a-f]{64}$/iu.test(r))throw new d("REMOTE_ENCRYPTION_KEY_INVALID","hex: remote encryption key must contain exactly 64 hexadecimal characters.");e=Buffer.from(r,"hex")}else if(/^[0-9a-f]{64}$/iu.test(t))e=Buffer.from(t,"hex");else{let r=t.startsWith("base64:")?t.slice(7):t;if(!/^[A-Za-z0-9+/_-]+={0,2}$/u.test(r))throw new d("REMOTE_ENCRYPTION_KEY_INVALID","Remote encryption key must be 32 raw bytes encoded as hexadecimal or base64.");e=Buffer.from(r,r.includes("-")||r.includes("_")?"base64url":"base64")}if(e.length!==z)throw new d("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${z} bytes.`);return e}function fe(n=process.env){let t=n.TOOLNET_REMOTE_ENCRYPTION_KEY?.trim(),e=n.TOOLNET_REMOTE_ENCRYPTION_KEY_FILE?.trim();if(t&&e)throw new d("REMOTE_ENCRYPTION_KEY_AMBIGUOUS","Configure either TOOLNET_REMOTE_ENCRYPTION_KEY or TOOLNET_REMOTE_ENCRYPTION_KEY_FILE, not both.");if(t)return de(t);if(e){let r;try{r=Tt(e,"utf8")}catch(o){throw new d("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${e}.`,o instanceof Error?o.message:String(o)].join(" "))}return de(r)}}function ge(n){return jt("sha256").update(n).digest().subarray(0,v)}function ye(n){return Buffer.from(`${It}${n}`,"utf8")}function Y(n){return n.byteLength<g.length?!1:Buffer.from(n).subarray(0,g.length).equals(g)}function he(n,t,e){if(e.byteLength!==z)throw new d("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH","AES-256-GCM requires a 32-byte key.");let r=typeof t=="string"?Buffer.from(t,"utf8"):Buffer.from(t),o=At(M),i=Mt(me,e,o);i.setAAD(ye(n));let s=Buffer.concat([i.update(r),i.final()]),a=i.getAuthTag(),c=Buffer.alloc(pe),u=0;return g.copy(c,u),u+=g.length,c.writeUInt8(le,u),u+=1,ge(e).copy(c,u),u+=v,o.copy(c,u),u+=M,a.copy(c,u),Buffer.concat([c,s])}function Se(n,t,e){let r=Buffer.from(t);if(!Y(r))throw new d("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(r.length<pe)throw new d("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let o=g.length,i=r.readUInt8(o);if(o+=1,i!==le)throw new d("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${i}.`);let s=r.subarray(o,o+v);o+=v;let a=ge(e);if(!kt(s,a))throw new d("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let c=r.subarray(o,o+M);o+=M;let u=r.subarray(o,o+L);o+=L;let p=r.subarray(o),l=Pt(me,e,c);l.setAAD(ye(n)),l.setAuthTag(u);try{return Buffer.concat([l.update(p),l.final()])}catch{throw new d("REMOTE_ENCRYPTION_AUTH_FAILED","Encrypted remote object failed AES-GCM authentication.")}}var B=class{constructor(t,e){this.inner=t;this.options=e;if(this.name=t.name,e.enabled&&!e.key)throw new d("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote client-side encryption is enabled but no encryption key is configured.")}inner;options;name;async put(t,e,r){if(!this.options.enabled){await this.inner.put(t,e,r);return}let o=this.options.key;if(!o)throw new d("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let i=he(t,e,o);await this.inner.put(t,i,"application/octet-stream")}async get(t){let e=await this.inner.get(t);if(!e)return null;if(!Y(e))return e;if(!this.options.enabled)throw new d("REMOTE_ENCRYPTION_REQUIRED",["Remote object is client-side encrypted.","Enable TOOLNET_REMOTE_ENCRYPTION and configure the matching key."].join(" "));let r=this.options.key;if(!r)throw new d("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return Se(t,e,r)}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){return this.inner.exists(t)}async delete(t){await this.inner.delete(t)}async list(t=""){return this.inner.list(t)}};function be(n,t=process.env){if(n.name==="local")return V(t)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),n;let e=V(t),r=e?fe(t):void 0;return new B(n,{enabled:e,key:r})}function P(n){return be(n)}function U(n,t){return console.warn(t),P(new E(n))}function we(n){let t=n.localRoot??Ot(_t(),".toolnet-memory","storage");if(n.provider==="r2"){let e=n.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?P(new R({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):U(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let e=n.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?P(new R({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):U(t,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let e=n.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?P(new O({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):U(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return P(new E(t))}function Nt(n){return new Promise(t=>setTimeout(t,n))}async function xe(n,t={}){let e=Math.max(1,t.attempts??3),r=t.baseDelayMs??150,o=t.maxDelayMs??2e3,i;for(let s=1;s<=e;s++)try{return await n()}catch(a){if(i=a,s>=e)break;let c=Math.min(o,r*2**(s-1)),u=Math.floor(Math.random()*Math.max(1,c*.2));await Nt(c+u)}throw i}var Kt=new Set(["put","get","getText","delete","list"]);function Ee(n,t={}){return new Proxy(n,{get(e,r){let o=Reflect.get(e,r,e);return typeof o!="function"?o:Kt.has(r)?(...i)=>xe(()=>Promise.resolve(o.apply(e,i)),t):o.bind(e)}})}function Re(n){let t=n.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!t||t==="."||t==="..")throw new Error("Invalid project storage folder");return t}function ve(n){let t=n.replace(/^\/+/,"");if(t.startsWith("memories/"))return"memory/records/"+t.slice(9);if(t.startsWith("vectors/"))return"memory/vectors/"+t.slice(8);if(t.startsWith("graph/"))return"code/graph/"+t.slice(6);let e=t.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(e)return`${e[1]}memory/records/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),e)return`${e[1]}memory/vectors/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),e)return`${e[1]}code/graph/${e[2]}`;let r=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=t.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=t.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=t.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:t))}var N=class{constructor(t,e,r,o){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=r,this.folder=Re(o??r),this.sourcePrefix=`projects/${e}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let t=`${this.targetPrefix}/project.json`,e=new Date().toISOString(),r=e,o=await this.provider.getText(t);if(o){let s;try{s=JSON.parse(o)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${a instanceof Error?a.message:String(a)}`)}if(typeof s.id=="string"&&s.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${s.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof s.createdAt=="string"&&(r=s.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:e};await this.provider.put(t,JSON.stringify(i,null,2)+`
|
|
4
|
+
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=ve(t),t===this.sourcePrefix)return this.targetPrefix;if(t.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+t.slice(this.sourcePrefix.length);if(t===this.targetPrefix||t.startsWith(`${this.targetPrefix}/`))return t;if(t.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${t}`].join(" "));return t}async put(t,e,r){return await this.ensureRegistered(),this.provider.put(this.key(t),e,r)}async get(t){return await this.ensureRegistered(),this.provider.get(this.key(t))}async getText(t){return await this.ensureRegistered(),this.provider.getText(this.key(t))}async delete(t){return await this.ensureRegistered(),this.provider.delete(this.key(t))}async exists(t){return await this.ensureRegistered(),this.provider.exists(this.key(t))}async list(t){return await this.ensureRegistered(),this.provider.list(this.key(t))}};import{existsSync as je,mkdirSync as $t,readFileSync as Lt,statSync as Me,writeFileSync as zt}from"node:fs";import{dirname as Vt,join as Yt}from"node:path";import{createHash as Dt}from"node:crypto";function F(n){return Dt("sha256").update(n).digest("hex")}var Pe=64*1024,Bt=`# ToolNet Project Operating Manual
|
|
5
5
|
|
|
6
6
|
This file contains persistent instructions for AI agents working on this project.
|
|
7
7
|
|
|
@@ -44,11 +44,11 @@ Things an AI agent should always remember.
|
|
|
44
44
|
<!--
|
|
45
45
|
- [advisory] Prefer small focused files.
|
|
46
46
|
-->
|
|
47
|
-
`;function
|
|
48
|
-
`,"application/json"),e}function
|
|
47
|
+
`;function Ae(n){return Yt(n.rootPath,".toolnet","PROJECT.md")}function Ut(n){return n.normalize("NFKC").replace(/\s+/g," ").trim()}function Ft(n){let t=[],e=new Set,r=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,o;for(;o=r.exec(n);){let i=o[1].toLowerCase(),s=Ut(o[2]);if(!s)continue;let a=`${i}:${s.toLowerCase()}`;e.has(a)||(e.add(a),t.push({id:F(a).slice(0,24),mode:i,text:s,source:"manual"}))}return t}function G(n){let t=Ae(n);return je(t)||($t(Vt(t),{recursive:!0}),zt(t,Bt,{encoding:"utf8",mode:384})),t}function H(n,t=!1){let e=t?G(n):Ae(n);if(!je(e))return null;if(Me(e).size>Pe)throw new Error(`PROJECT.md exceeds ${Pe} bytes`);let o=Lt(e,"utf8");return{path:e,content:o,digest:F(o),rules:Ft(o),bytes:Buffer.byteLength(o,"utf8"),updatedAt:new Date(Me(e).mtimeMs).toISOString()}}async function ke(n,t){let e=H(n,!0);if(!e)throw new Error("Project manual unavailable");let r=`projects/${n.id}/project`;return await t.put(`${r}/manual.md`,e.content,"text/markdown"),await t.put(`${r}/manual.json`,JSON.stringify({version:1,projectId:n.id,projectName:n.name,digest:e.digest,bytes:e.bytes,ruleCount:e.rules.length,enforceRules:e.rules.filter(o=>o.mode==="enforce").length,advisoryRules:e.rules.filter(o=>o.mode==="advisory").length,updatedAt:e.updatedAt},null,2)+`
|
|
48
|
+
`,"application/json"),e}function Ht(n,t){let e=n.indexOf(t);return e>=0?n[e+1]:void 0}function Jt(n){let t=J(),e=Ee(we({provider:t.storage.provider,huggingface:t.storage.huggingface,localRoot:t.storage.localRoot}),{attempts:3});return new N(e,n.id,n.name,n.remote??n.name)}async function Wt(){let[n="help",...t]=process.argv.slice(2),e=new I().detect(Ht(t,"--project")??process.cwd());if(n==="init"){let r=G(e);console.log(`\u2705 Project manual: ${r}`);return}if(n==="show"){let r=H(e,!1);if(!r){console.log("No .toolnet/PROJECT.md yet.");return}process.stdout.write(Gt(r.path,"utf8"));return}if(n==="sync"){let r=await ke(e,Jt(e));console.log(JSON.stringify({project:e.name,path:r.path,digest:r.digest,bytes:r.bytes,rules:r.rules.length,enforce:r.rules.filter(o=>o.mode==="enforce").length,advisory:r.rules.filter(o=>o.mode==="advisory").length},null,2));return}console.log(`Project Manual
|
|
49
49
|
|
|
50
50
|
Commands:
|
|
51
51
|
init [--project PATH]
|
|
52
52
|
show [--project PATH]
|
|
53
53
|
sync [--project PATH]
|
|
54
|
-
`)}
|
|
54
|
+
`)}Wt().catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1)});
|