toolnet-memory 0.2.9 → 0.2.11
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 +30 -10
- package/LICENSE +21 -0
- package/README.md +146 -376
- package/bin/toolnet-memory +71 -26
- package/bundle/agy-hook.js +30 -30
- package/bundle/agy.js +24 -24
- package/bundle/codex-context.js +12 -12
- package/bundle/codex.js +31 -31
- package/bundle/context-runtime.js +39 -12
- package/bundle/context.js +13 -13
- package/bundle/doctor.js +3 -3
- package/bundle/full-index.js +8 -8
- package/bundle/graph-index.js +4 -4
- package/bundle/impact.js +4 -4
- package/bundle/incremental.js +4 -4
- package/bundle/learner.js +5 -5
- package/bundle/mcp.js +7 -7
- package/bundle/opencode.js +34 -34
- package/bundle/project-manual.js +7 -7
- package/bundle/runtime.js +11 -11
- package/bundle/semantic.js +6 -6
- package/bundle/setup.js +57 -32
- package/bundle/snapshot.js +4 -4
- package/bundle/work-continuity.js +7 -7
- package/package.json +24 -1
package/bundle/semantic.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import"dotenv/config";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}),Pe(o,r)}function re(s,e){return{id:s.id,name:s.name,remote:s.remote,rootPath:e,createdAt:s.createdAt,updatedAt:s.updatedAt,graphVersion:s.graphVersion,memoryVersion:s.memoryVersion,metadata:s.metadata}}var O=class{detect(e=process.cwd()){let t=j(e),r=Ae(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>J(F(r,d))),i=Ne(t,n?r:void 0);if(i){let d=q(i),m=Me(d);return m.rootPath!==i&&(m.rootPath=i,m.updatedAt=new Date().toISOString(),te(i,m)),re(m,i)}let c=new Date().toISOString(),l=Te(r),a={version:1,id:je(r),name:l,remote:l,rootPath:r,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return te(r,a),re(a,r)}};var Ie=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_\-]{20,}\b/g},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._\-]{16,}\b/gi},{type:"jwt",regex:/\beyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b/g},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g},{type:"password_assignment",regex:/\b(password|passwd|pwd)\s*[:=]\s*["']?[^"'\s]{6,}["']?/gi},{type:"secret_assignment",regex:/\b(secret|api[_-]?key|access[_-]?token|refresh[_-]?token)\s*[:=]\s*["']?[^"'\s]{8,}["']?/gi},{type:"cookie",regex:/\b(cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi}],_=class{scan(e){let t=[];for(let r of Ie){let o=new RegExp(r.regex.source,r.regex.flags);for(let n of e.matchAll(o))t.push({type:r.type,value:n[0]})}return t}hasSecrets(e){return this.scan(e).length>0}};var L=class{scanner=new _;sanitize(e){let t=e,r=this.scanner.scan(e),o=new Set;for(let n of r)o.add(n.type),t=t.split(n.value).join(`[REDACTED:${n.type}]`);return{text:t,redacted:r.length,secretTypes:[...o]}}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 n=r.toLowerCase();n.includes("password")||n.includes("secret")||n.includes("token")||n.includes("cookie")||n.includes("authorization")?t[r]="[REDACTED]":t[r]=this.sanitizeValue(o)}return t}return e}};import{homedir as Ye}from"node:os";import{join as Ze}from"node:path";import{DeleteObjectCommand as Oe,GetObjectCommand as Fe,HeadObjectCommand as _e,ListObjectsV2Command as $e,PutObjectCommand as Ge,S3Client as De}from"@aws-sdk/client-s3";import{getSignedUrl as ze}from"@aws-sdk/s3-request-presigner";var $=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new De({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ge({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await ze(this.client,new Fe({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new _e({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Oe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new $e({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as ne,mkdir as Be,readFile as He,readdir as Ke,rm as We,stat as ie,writeFile as Ve}from"node:fs/promises";import{dirname as Ue,join as Je,relative as ae,resolve as qe}from"node:path";var N=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return qe(this.root,t)}async put(e,t){let r=this.path(e);await Be(Ue(r),{recursive:!0}),await Ve(r,t)}async get(e){try{return await He(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 ne(this.path(e)),!0}catch{return!1}}async delete(e){await We(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await ne(t)}catch{return r}let o=async i=>{let c=await Ke(i,{withFileTypes:!0});for(let l of c){let a=Je(i,l.name);if(l.isDirectory()){await o(a);continue}let d=await ie(a);r.push({key:ae(this.root,a),size:d.size,updatedAt:d.mtime.toISOString()})}},n=await ie(t);return n.isDirectory()?await o(t):r.push({key:ae(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),r}};function ce(s){let e=s.localRoot??Ze(Ye(),".toolnet-memory","storage");if(s.provider==="huggingface"){let t=s.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new $({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):(console.warn("[storage] Hugging Face credentials missing. Using local fallback."),new N(e))}return new N(e)}var A=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var G=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","chunks","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var D=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function le(s){let e=s.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function de(s){let e=s.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var z=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=le(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let i;try{i=JSON.parse(o)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
|
|
4
|
-
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=
|
|
5
|
-
`).trim();if(h){let x=this.sanitizer.sanitize(h).text,P=
|
|
6
|
-
`)),
|
|
1
|
+
import"dotenv/config";import{existsSync as Se,readFileSync as be}from"node:fs";import{homedir as xe}from"node:os";import{join as Ee}from"node:path";function Ce(s){let e=s.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function we(){let s=process.env.TOOLNET_GLOBAL_ENV??Ee(xe(),".config","toolnet-memory",".env");if(!Se(s))return;let e=be(s,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let n=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=Ce(r.slice(o+1)))}}we();function R(s,e){return s===void 0?e:["1","true","yes","on"].includes(s.toLowerCase())}function j(s,e){if(!s)return e;let t=Number(s);return Number.isFinite(t)?t:e}function re(){return{memory:{autoCapture:R(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:R(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:R(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:R(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:R(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 ve}from"node:crypto";import{existsSync as q,mkdirSync as Pe,readFileSync as ke,renameSync as Te,writeFileSync as Re}from"node:fs";import{basename as je,dirname as O,join as F,parse as ne,resolve as A}from"node:path";var ie=".toolnet",Ae="project.json";function Le(s){return ve("sha256").update(s).digest("hex").slice(0,16)}function J(s){return F(s,ie,Ae)}function Ne(s){return q(J(s))}function Me(s,e){let t=A(s),r=ne(t).root;for(;;){if(Ne(t))return t;if(t===r||e&&t===A(e))break;let o=O(t);if(o===t)break;t=o}return null}function Ie(s){let e=A(s),t=ne(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(n=>q(F(e,n))))return e;if(e===t)break;let o=O(e);if(o===e)break;e=o}return A(s)}function Oe(s){let e;try{e=JSON.parse(ke(s,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${s}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${s}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${s}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${s}`);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:O(O(s)),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 se(s,e){let t=F(s,ie);Pe(t,{recursive:!0});let r=J(s),o=`${r}.tmp-${process.pid}`;Re(o,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),Te(o,r)}function oe(s,e){return{id:s.id,name:s.name,remote:s.remote,rootPath:e,createdAt:s.createdAt,updatedAt:s.updatedAt,graphVersion:s.graphVersion,memoryVersion:s.memoryVersion,metadata:s.metadata}}var _=class{detect(e=process.cwd()){let t=A(e),r=Ie(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>q(F(r,d))),i=Me(t,n?r:void 0);if(i){let d=J(i),m=Oe(d);return m.rootPath!==i&&(m.rootPath=i,m.updatedAt=new Date().toISOString(),se(i,m)),oe(m,i)}let c=new Date().toISOString(),l=je(r),a={version:1,id:Le(r),name:l,remote:l,rootPath:r,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return se(r,a),oe(a,r)}};var _e=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_\-]{20,}\b/g},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._\-]{16,}\b/gi},{type:"jwt",regex:/\beyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b/g},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g},{type:"password_assignment",regex:/\b(password|passwd|pwd)\s*[:=]\s*["']?[^"'\s]{6,}["']?/gi},{type:"secret_assignment",regex:/\b(secret|api[_-]?key|access[_-]?token|refresh[_-]?token)\s*[:=]\s*["']?[^"'\s]{8,}["']?/gi},{type:"cookie",regex:/\b(cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi}],$=class{scan(e){let t=[];for(let r of _e){let o=new RegExp(r.regex.source,r.regex.flags);for(let n of e.matchAll(o))t.push({type:r.type,value:n[0]})}return t}hasSecrets(e){return this.scan(e).length>0}};var L=class{scanner=new $;sanitize(e){let t=e,r=this.scanner.scan(e),o=new Set;for(let n of r)o.add(n.type),t=t.split(n.value).join(`[REDACTED:${n.type}]`);return{text:t,redacted:r.length,secretTypes:[...o]}}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 n=r.toLowerCase();n.includes("password")||n.includes("secret")||n.includes("token")||n.includes("cookie")||n.includes("authorization")?t[r]="[REDACTED]":t[r]=this.sanitizeValue(o)}return t}return e}};import{homedir as nt}from"node:os";import{join as it}from"node:path";import{DeleteObjectCommand as Fe,GetObjectCommand as $e,HeadObjectCommand as De,ListObjectsV2Command as Ge,PutObjectCommand as Ke,S3Client as ze}from"@aws-sdk/client-s3";import{getSignedUrl as Be}from"@aws-sdk/s3-request-presigner";var D=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new ze({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ke({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Be(this.client,new $e({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new De({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 Fe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Ge({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as ae,mkdir as He,readFile as We,readdir as Ue,rm as Ve,stat as ce,writeFile as Ye}from"node:fs/promises";import{dirname as qe,join as Je,relative as le,resolve as Qe}from"node:path";var N=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Qe(this.root,t)}async put(e,t){let r=this.path(e);await He(qe(r),{recursive:!0}),await Ye(r,t)}async get(e){try{return await We(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 ae(this.path(e)),!0}catch{return!1}}async delete(e){await Ve(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await ae(t)}catch{return r}let o=async i=>{let c=await Ue(i,{withFileTypes:!0});for(let l of c){let a=Je(i,l.name);if(l.isDirectory()){await o(a);continue}let d=await ce(a);r.push({key:le(this.root,a),size:d.size,updatedAt:d.mtime.toISOString()})}},n=await ce(t);return n.isDirectory()?await o(t):r.push({key:le(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),r}};import{DeleteObjectCommand as Ze,GetObjectCommand as Xe,HeadObjectCommand as et,ListObjectsV2Command as tt,PutObjectCommand as rt,S3Client as st}from"@aws-sdk/client-s3";import{getSignedUrl as ot}from"@aws-sdk/s3-request-presigner";var M=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new st({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new rt({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await ot(this.client,new Xe({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new et({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Ze({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new tt({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};function Q(s,e){return console.warn(e),new N(s)}function de(s){let e=s.localRoot??it(nt(),".toolnet-memory","storage");if(s.provider==="r2"){let t=s.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new M({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):Q(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(s.provider==="s3"){let t=s.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new M({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):Q(e,"[storage] S3 credentials missing. Using local fallback.")}if(s.provider==="huggingface"){let t=s.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new D({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):Q(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new N(e)}var I=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var G=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","chunks","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var K=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function pe(s){let e=s.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 me(s){let e=s.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var z=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=pe(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let i;try{i=JSON.parse(o)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
|
|
4
|
+
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=me(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import*as u from"typescript";var v=class{symbols=new Map;edges=new Map;addSymbol(e){this.symbols.set(e.id,e)}addEdge(e){this.edges.set(e.id,e)}getSymbol(e){return this.symbols.get(e)}findByName(e,t){return[...this.symbols.values()].filter(r=>r.projectId===e&&(r.name===t||r.qualifiedName===t))}allSymbols(e){return[...this.symbols.values()].filter(t=>t.projectId===e)}allEdges(e){return[...this.edges.values()].filter(t=>t.projectId===e)}import(e,t){for(let r of e)this.addSymbol(r);for(let r of t)this.addEdge(r)}clearProject(e){for(let[t,r]of this.symbols)r.projectId===e&&this.symbols.delete(t);for(let[t,r]of this.edges)r.projectId===e&&this.edges.delete(t)}};import{execFile as dt}from"node:child_process";import{promisify as pt}from"node:util";var js=pt(dt);import{createHash as mt}from"node:crypto";import{readFile as ft}from"node:fs/promises";import{join as ut}from"node:path";function fe(s){return mt("sha256").update(s).digest("hex")}var B=class{sanitizer=new L;async build(e,t,r,o={}){let n=o.maxLines??80,i=o.overlapLines??10,c=new Map;for(let a of r){let d=c.get(a.filePath)??[];d.push(a),c.set(a.filePath,d)}let l=[];for(let[a,d]of c){let m;try{m=await ft(ut(t,a),"utf8")}catch{continue}let g=m.split(/\r?\n/),S=d.filter(h=>h.type!=="file"&&typeof h.startLine=="number"&&typeof h.endLine=="number");if(S.length>0)for(let h of S)l.push(...this.chunkRange({projectId:e,filePath:a,lines:g,symbol:h,startLine:h.startLine,endLine:h.endLine,maxLines:n,overlapLines:i}));else l.push(...this.chunkRange({projectId:e,filePath:a,lines:g,startLine:1,endLine:Math.max(1,g.length),maxLines:n,overlapLines:i}))}return l}chunkRange(e){let{projectId:t,filePath:r,lines:o,symbol:n,maxLines:i}=e,c=Math.min(e.overlapLines,Math.max(0,i-1)),l=Math.max(1,e.startLine),a=Math.min(o.length,Math.max(l,e.endLine)),d=[],m=l,g=0;for(;m<=a;){let S=Math.min(a,m+i-1),h=o.slice(m-1,S).join(`
|
|
5
|
+
`).trim();if(h){let x=this.sanitizer.sanitize(h).text,P=fe(x),k=[t,r,n?.id??"file",String(g),P].join(":");d.push({id:fe(k).slice(0,32),projectId:t,filePath:r,symbolId:n?.id,symbolName:n?.qualifiedName??n?.name,symbolType:n?.type,startLine:m,endLine:S,content:x,contentHash:P})}if(S>=a)break;m=S-c+1,g++}return d}};var H=class{records=new Map;upsert(e){this.records.set(e.id,e)}get(e){return this.records.get(e)}has(e){return this.records.has(e)}remove(e){return this.records.delete(e)}clearProject(e){let t=0;for(let[r,o]of this.records)o.projectId===e&&(this.records.delete(r),t++);return t}exportProject(e){return[...this.records.values()].filter(t=>t.projectId===e)}importRecords(e){let t=0;for(let r of e)!r?.id||!r?.projectId||!Array.isArray(r.vector)||(this.records.set(r.id,r),t++);return t}search(e,t,r=10){let o=[];for(let n of this.records.values()){if(n.projectId!==e)continue;let i=gt(t,n.vector);o.push({id:n.id,score:i,metadata:n.metadata})}return o.sort((n,i)=>i.score-n.score).slice(0,r)}size(){return this.records.size}};function gt(s,e){if(s.length===0||e.length===0||s.length!==e.length)return 0;let t=0,r=0,o=0;for(let n=0;n<s.length;n++){let i=s[n]??0,c=e[n]??0;t+=i*c,r+=i*i,o+=c*c}return r===0||o===0?0:t/(Math.sqrt(r)*Math.sqrt(o))}function Z(s){return s.toLowerCase().normalize("NFKC").replace(/[^\p{L}\p{N}_\-./]+/gu," ").split(/\s+/).map(e=>e.trim()).filter(e=>e.length>1)}function ue(s,e){let t=s.toLowerCase().replaceAll("\\","/"),r=e.toLowerCase(),o=/\b(test|tests|spec|fixture|mock)\b/.test(r);return(t.startsWith("tests/")||t.includes("/tests/")||t.includes("__tests__")||/\.(test|spec)\.[^.]+$/.test(t))&&!o?.35:t.includes("/generated/")||t.includes(".generated.")?.25:1}var W=class{constructor(e){this.options=e}options;vectors=new H;chunks=new Map;async initialize(){let{projectId:e,rootPath:t,storage:r,embeddings:o,graph:n,model:i}=this.options,c=new G(r),l=new K(r),a=await new B().build(e,t,n.allSymbols(e));this.chunks.clear();for(let f of a)this.chunks.set(f.id,f);let d=await c.load(e),m=!d||d.chunks.length!==a.length||d.chunks.some(f=>!this.chunks.has(f.id));m&&await c.save({version:1,projectId:e,updatedAt:new Date().toISOString(),chunks:a});let g=await l.load(e),S=0;g&&g.model===i&&(S=this.vectors.importRecords(g.records));let h=new Set(a.map(f=>f.id)),x=0;for(let f of this.vectors.exportProject(e))h.has(f.id)||this.vectors.remove(f.id)&&x++;let P=a.filter(f=>{let C=this.vectors.get(f.id);return C?C.metadata?.contentHash!==f.contentHash:!0}),k=0,ee=8,T=this.vectors.exportProject(e)[0]?.vector.length;for(let f=0;f<P.length;f+=ee){let C=P.slice(f,f+ee),he=C.map(b=>[`file: ${b.filePath}`,b.symbolName?`symbol: ${b.symbolName}`:"",b.symbolType?`type: ${b.symbolType}`:"",b.content].filter(Boolean).join(`
|
|
6
|
+
`)),ye=await o.embedMany(he);for(let b=0;b<C.length;b++){let E=C[b],w=ye[b];if(!(!E||!w||w.length===0)){if(T===void 0&&(T=w.length),w.length!==T){console.warn(`[code-semantic] skip dimension mismatch: ${w.length} != ${T}`);continue}this.vectors.upsert({id:E.id,projectId:e,vector:w,metadata:{contentHash:E.contentHash,filePath:E.filePath,symbolName:E.symbolName,symbolType:E.symbolType,startLine:E.startLine,endLine:E.endLine}}),k++}}}let Y=this.vectors.exportProject(e),te=k>0||x>0||!g||g.model!==i;return te&&await l.save({version:1,projectId:e,model:i,dimensions:Y[0]?.vector.length??T??0,updatedAt:new Date().toISOString(),records:Y}),{chunks:a.length,vectorsLoaded:S,vectorsIndexed:k,vectorsRemoved:x,totalVectors:Y.length,savedChunks:m,savedVectors:te}}async search(e,t=8){let r=await this.options.embeddings.embed(e),o=this.vectors.search(this.options.projectId,r,Math.max(t*4,20)),n=new Set(Z(e)),i=[];for(let c of o){let l=this.chunks.get(c.id);if(!l)continue;let a=new Set(Z([l.filePath,l.symbolName??"",l.symbolType??"",l.content].join(" "))),d=0;for(let x of n)a.has(x)&&d++;let m=n.size===0?0:d/n.size,g=Math.max(0,c.score),S=ue(l.filePath,e),h=(g*.75+m*.25)*S;i.push({chunk:l,score:h,vectorScore:g,lexicalScore:m})}return i.sort((c,l)=>l.score-c.score).slice(0,t)}size(){return this.chunks.size}};import*as y from"typescript";import*as p from"typescript";var _o=new Set([p.SyntaxKind.EqualsToken,p.SyntaxKind.PlusEqualsToken,p.SyntaxKind.MinusEqualsToken,p.SyntaxKind.AsteriskEqualsToken,p.SyntaxKind.SlashEqualsToken,p.SyntaxKind.PercentEqualsToken,p.SyntaxKind.QuestionQuestionEqualsToken,p.SyntaxKind.AmpersandAmpersandEqualsToken,p.SyntaxKind.BarBarEqualsToken]);var U=class{constructor(e){this.options=e}options;name="huggingface";dimensions=384;async embed(e){let[t]=await this.embedMany([e]);return t}async embedMany(e){if(e.length===0)return[];let t=`https://router.huggingface.co/hf-inference/models/${this.options.model}/pipeline/feature-extraction`,r=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.options.token}`,"Content-Type":"application/json"},body:JSON.stringify({inputs:e,options:{wait_for_model:!0}})});if(!r.ok)throw new Error(`HF embedding failed: ${r.status} ${await r.text()}`);let o=await r.json();if(!Array.isArray(o))throw new Error("HF embedding returned invalid response");return o.map(n=>yt(n))}};function yt(s){if(!Array.isArray(s))throw new Error("Invalid embedding");if(s.length>0&&typeof s[0]=="number")return s;if(s.length>0&&Array.isArray(s[0])){let e=s;if(e.length===0)return[];let t=e[0].length,r=new Array(t).fill(0);for(let o of e)for(let n=0;n<t;n++)r[n]+=o[n]??0;for(let o=0;o<t;o++)r[o]/=e.length;return r}throw new Error("Unsupported embedding shape")}import{createHash as St}from"node:crypto";var V=class{name="hash-fallback";dimensions=128;async embed(e){let t=new Array(this.dimensions).fill(0),r=e.toLowerCase().split(/\s+/).filter(Boolean);for(let o of r){let i=St("sha256").update(o).digest().readUInt32BE(0)%this.dimensions;t[i]+=1}return bt(t)}async embedMany(e){return Promise.all(e.map(t=>this.embed(t)))}};function bt(s){let e=Math.sqrt(s.reduce((t,r)=>t+r*r,0));return e===0?s:s.map(t=>t/e)}var X=class{constructor(e,t=new V){this.primary=e;this.fallback=t}primary;fallback;name="resilient";async embed(e){if(this.primary)try{return await this.primary.embed(e)}catch(t){console.warn("[embedding] primary failed, fallback enabled:",t instanceof Error?t.message:t)}return this.fallback.embed(e)}async embedMany(e){if(this.primary)try{return await this.primary.embedMany(e)}catch(t){console.warn("[embedding] primary failed, fallback enabled:",t instanceof Error?t.message:t)}return this.fallback.embedMany(e)}};function ge(){let s=process.env.HF_TOKEN,e=process.env.HF_EMBEDDING_MODEL??"sentence-transformers/all-MiniLM-L6-v2",t=s?new U({token:s,model:e}):null;return new X(t)}async function xt(){let s=re(),e=new _().detect(),t=de({provider:s.storage.provider,huggingface:s.storage.huggingface,localRoot:s.storage.localRoot}),r=new z(t,e.id,e.name,e.remote??e.name),o=new v,n=await new I(r).load(e.id);if(!n)throw new Error("Code graph missing. Run npm run code:index:test first.");o.import(n.symbols,n.edges);let i=new W({projectId:e.id,rootPath:e.rootPath,model:process.env.HF_EMBEDDING_MODEL??"sentence-transformers/all-MiniLM-L6-v2",storage:r,embeddings:ge(),graph:o}),c=await i.initialize(),l=process.argv.slice(2).join(" ")||"memory storage hugging face",a=await i.search(l,5);console.log({ok:!0,query:l,...c,results:a.map(d=>({score:Number(d.score.toFixed(4)),file:d.chunk.filePath,symbol:d.chunk.symbolName,lines:`${d.chunk.startLine}-${d.chunk.endLine}`,preview:d.chunk.content.replace(/\s+/g," ").slice(0,140)}))})}xt().catch(s=>{console.error(s),process.exit(1)});
|
package/bundle/setup.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
`,{encoding:"utf8",mode:384}),
|
|
1
|
+
import m from"node:fs";import Te from"node:os";import q from"node:path";import Ae from"node:readline/promises";import{stdin as d,stdout as S}from"node:process";import{existsSync as A}from"node:fs";import{homedir as R}from"node:os";import{join as N}from"node:path";import{spawnSync as me}from"node:child_process";import{existsSync as V,mkdirSync as W,readFileSync as X,writeFileSync as Z}from"node:fs";import{homedir as Q}from"node:os";import{dirname as z,join as ee}from"node:path";function te(e){return`'${e.replace(/'/g,"'\\''")}'`}function Y(e={}){let t=e.hooksFile??ee(Q(),".gemini","config","hooks.json");W(z(t),{recursive:!0});let o={};if(V(t))try{o=JSON.parse(X(t,"utf8"))}catch(s){throw new Error(`Invalid existing Agy hooks.json: ${s instanceof Error?s.message:String(s)}`)}let r=e.binary??"toolnet-memory",n=`${te(r)} session:agy-hook`;return o["toolnet-memory"]={enabled:!0,PreInvocation:[{type:"command",command:`${n} pre`,timeout:15}],PostInvocation:[{type:"command",command:`${n} post`,timeout:15}],Stop:[{type:"command",command:`${n} stop`,timeout:30}]},Z(t,JSON.stringify(o,null,2)+`
|
|
2
|
+
`,{encoding:"utf8",mode:384}),t}import{existsSync as oe,mkdirSync as K,readFileSync as ne,writeFileSync as D}from"node:fs";import{homedir as O}from"node:os";import{join as p}from"node:path";var $="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",C="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function re(){let e=p(O(),".config","opencode","AGENTS.md");K(p(O(),".config","opencode"),{recursive:!0});let t=`${$}
|
|
3
3
|
## ToolNet Memory \u2014 Project Continuity
|
|
4
4
|
|
|
5
5
|
When the current workspace belongs to a ToolNet project, identified by a nearest ancestor containing \`.toolnet/project.json\`:
|
|
@@ -12,10 +12,10 @@ When the current workspace belongs to a ToolNet project, identified by a nearest
|
|
|
12
12
|
6. Never invent missing rationale. If ToolNet says a reason was not explicitly recorded, inspect the implementation or ask rather than guessing.
|
|
13
13
|
|
|
14
14
|
This applies automatically to every ToolNet-managed project.
|
|
15
|
-
${
|
|
15
|
+
${C}`,o=oe(e)?ne(e,"utf8"):"",r=o.indexOf($),n=o.indexOf(C);return r>=0&&n>=r?o=o.slice(0,r)+t+o.slice(n+C.length):(o=o.trimEnd(),o&&(o+=`
|
|
16
16
|
|
|
17
|
-
`),o+=
|
|
18
|
-
`,{encoding:"utf8",mode:384}),
|
|
17
|
+
`),o+=t),D(e,o.trimEnd()+`
|
|
18
|
+
`,{encoding:"utf8",mode:384}),e}function w(e={}){let t=e.directory??p(O(),".config","opencode","plugins");K(t,{recursive:!0}),re();let o=p(t,"toolnet-memory.js"),r=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=`// Generated by ToolNet Memory.
|
|
19
19
|
// OpenCode session capture + continuity bootstrap.
|
|
20
20
|
|
|
21
21
|
function getSessionId(event) {
|
|
@@ -172,41 +172,66 @@ export const ToolNetMemoryPlugin = async ({ directory }) => {
|
|
|
172
172
|
},
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
-
`;return
|
|
176
|
-
`),
|
|
177
|
-
`);
|
|
178
|
-
`,{encoding:"utf8",mode:384}),r=
|
|
175
|
+
`;return D(o,n,{encoding:"utf8",mode:384}),o}import{existsSync as ie,mkdirSync as k,readFileSync as se,writeFileSync as b}from"node:fs";import{homedir as H}from"node:os";import{dirname as P,join as T}from"node:path";function ce(e){let t=[],o=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,r;for(;r=o.exec(e);){let n=r[1]??r[2]??"";try{t.push(r[1]!==void 0?JSON.parse(`"${n}"`):n)}catch{t.push(n)}}return t}function L(e={}){let t=e.configFile??T(process.env.CODEX_HOME??T(H(),".codex"),"config.toml"),o=e.previousFile??T(H(),".config","toolnet-memory","codex-notify-previous.json");k(P(t),{recursive:!0}),k(P(o),{recursive:!0});let r=ie(t)?se(t,"utf8"):"",n=e.binary??"toolnet-memory",s=`notify = [${JSON.stringify(n)}, "session:codex-notify"]`,i=r.split(`
|
|
176
|
+
`),a=i.findIndex(l=>/^\s*\[/.test(l));a<0&&(a=i.length);let f=-1,c=-1;for(let l=0;l<a;l+=1)if(/^\s*notify\s*=/.test(i[l])){if(f=l,c=l,i[l].includes("[")&&!i[l].includes("]"))for(;c+1<a&&(c+=1,!i[c].includes("]")););break}let g=[];if(f>=0){let l=i.slice(f,c+1).join(`
|
|
177
|
+
`);g=ce(l),i.splice(f,c-f+1,s)}else a=i.findIndex(l=>/^\s*\[/.test(l)),a<0&&(a=i.length),i.splice(a,0,s);let E=g.length>=2&&g[g.length-1]==="session:codex-notify";return g.length>0&&!E&&b(o,JSON.stringify(g,null,2)+`
|
|
178
|
+
`,{encoding:"utf8",mode:384}),r=i.join(`
|
|
179
179
|
`),r.endsWith(`
|
|
180
180
|
`)||(r+=`
|
|
181
|
-
`),
|
|
182
|
-
`,{encoding:"utf8",mode:384}),
|
|
181
|
+
`),b(t,r,{encoding:"utf8",mode:384}),{configFile:t,previousFile:o,preservedPrevious:g.length>0&&!E}}import{existsSync as ae,mkdirSync as le,readFileSync as ge,writeFileSync as de}from"node:fs";import{homedir as ue}from"node:os";import{dirname as fe,join as U}from"node:path";function Ee(e){return`'${e.replace(/'/g,"'\\''")}'`}function v(e={}){let t=e.hooksFile??U(process.env.CODEX_HOME??U(ue(),".codex"),"hooks.json");le(fe(t),{recursive:!0});let o={};if(ae(t))try{o=JSON.parse(ge(t,"utf8"))}catch(a){throw new Error(`Invalid existing Codex hooks.json: ${a instanceof Error?a.message:String(a)}`)}let r=o.hooks&&typeof o.hooks=="object"&&!Array.isArray(o.hooks)?o.hooks:{};o.hooks=r;let s=(Array.isArray(r.SessionStart)?r.SessionStart:[]).filter(a=>{try{return!JSON.stringify(a).includes("session:codex-context")}catch{return!0}}),i=e.binary??"toolnet-memory";return s.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${Ee(i)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),r.SessionStart=s,de(t,JSON.stringify(o,null,2)+`
|
|
182
|
+
`,{encoding:"utf8",mode:384}),t}function h(e){return me("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function _e(){return h("agy")||A(N(R(),".gemini"))}function Se(){return h("opencode")||A(N(R(),".config","opencode"))}function pe(){return h("codex")||A(process.env.CODEX_HOME??N(R(),".codex"))}function I(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=[];if(!(e.force===!0||_e()))o.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let n=Y({binary:t});o.push({agent:"agy",detected:!0,installed:!0,targets:[n]})}catch(n){o.push({agent:"agy",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(e.force===!0||Se()))o.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let n=w({binary:t});o.push({agent:"opencode",detected:!0,installed:!0,targets:[n]})}catch(n){o.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(e.force===!0||pe()))o.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let n=L({binary:t}),s=v({binary:t}),i=[n.configFile,s];n.preservedPrevious&&i.push(n.previousFile),o.push({agent:"codex",detected:!0,installed:!0,targets:i})}catch(n){o.push({agent:"codex",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}return o}function ye(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let t of e){let o=t.agent==="agy"?"Agy / Antigravity":t.agent==="opencode"?"OpenCode":"Codex";if(!t.detected){console.log(`- ${o}: not detected`);continue}if(t.installed){console.log(`\u2713 ${o}: automatic memory enabled`);continue}console.log(`\u2717 ${o}: integration failed`),t.error&&console.log(` ${t.error}`)}console.log("")}async function Ce(){let e=process.argv.slice(2),t=e.includes("--all"),o=e.includes("--json"),r=I({force:t});if(o){console.log(JSON.stringify(r,null,2));return}ye(r)}var Oe=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Oe&&Ce().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});var x=q.join(Te.homedir(),".config","toolnet-memory"),_=q.join(x,".env");function Re(e){let t=new Map;for(let o of e.split(/\r?\n/)){let r=o.trim();if(!r||r.startsWith("#"))continue;let n=r.indexOf("=");n!==-1&&t.set(r.slice(0,n).trim(),r.slice(n+1).trim())}return t}function J(e){let t=e.get("MEMORY_STORAGE_PROVIDER")?.trim();return t==="r2"||t==="s3"||t==="local"||t==="huggingface"?t:e.get("R2_ACCOUNT_ID")&&e.get("R2_BUCKET")?"r2":e.get("S3_BUCKET")?"s3":e.get("HF_NAMESPACE")&&e.get("HF_BUCKET")?"huggingface":"r2"}function B(e){return e==="r2"?["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"]:e==="s3"?["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"]:e==="huggingface"?["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"]:[]}function y(e){return e==="r2"?"Cloudflare R2":e==="s3"?"S3-compatible storage":e==="huggingface"?"Hugging Face S3 (legacy)":"Local storage"}function j(e){m.mkdirSync(x,{recursive:!0,mode:448});let o=`# ==========================================================
|
|
183
183
|
# TOOLNET MEMORY
|
|
184
184
|
# ==========================================================
|
|
185
185
|
|
|
186
|
-
MEMORY_STORAGE_PROVIDER
|
|
187
|
-
|
|
188
|
-
#
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
186
|
+
MEMORY_STORAGE_PROVIDER=${J(e)}
|
|
187
|
+
|
|
188
|
+
# Cloudflare R2
|
|
189
|
+
R2_ACCOUNT_ID=${e.get("R2_ACCOUNT_ID")??""}
|
|
190
|
+
R2_BUCKET=${e.get("R2_BUCKET")??"toolnet-memory"}
|
|
191
|
+
R2_ACCESS_KEY_ID=${e.get("R2_ACCESS_KEY_ID")??""}
|
|
192
|
+
R2_SECRET_ACCESS_KEY=${e.get("R2_SECRET_ACCESS_KEY")??""}
|
|
193
|
+
|
|
194
|
+
# Generic S3 / S3-compatible storage
|
|
195
|
+
# Leave S3_ENDPOINT empty for AWS S3.
|
|
196
|
+
S3_ENDPOINT=${e.get("S3_ENDPOINT")??""}
|
|
197
|
+
S3_REGION=${e.get("S3_REGION")??"us-east-1"}
|
|
198
|
+
S3_BUCKET=${e.get("S3_BUCKET")??"toolnet-memory"}
|
|
199
|
+
S3_ACCESS_KEY_ID=${e.get("S3_ACCESS_KEY_ID")??""}
|
|
200
|
+
S3_SECRET_ACCESS_KEY=${e.get("S3_SECRET_ACCESS_KEY")??""}
|
|
201
|
+
S3_FORCE_PATH_STYLE=${e.get("S3_FORCE_PATH_STYLE")??"false"}
|
|
202
|
+
|
|
203
|
+
# Hugging Face S3 (legacy compatibility)
|
|
204
|
+
HF_NAMESPACE=${e.get("HF_NAMESPACE")??""}
|
|
205
|
+
HF_BUCKET=${e.get("HF_BUCKET")??"toolnet-memory"}
|
|
206
|
+
HF_S3_ACCESS_KEY_ID=${e.get("HF_S3_ACCESS_KEY_ID")??""}
|
|
207
|
+
HF_S3_SECRET_ACCESS_KEY=${e.get("HF_S3_SECRET_ACCESS_KEY")??""}
|
|
208
|
+
|
|
209
|
+
# Embedding
|
|
210
|
+
HF_TOKEN=${e.get("HF_TOKEN")??""}
|
|
211
|
+
HF_EMBEDDING_MODEL=${e.get("HF_EMBEDDING_MODEL")??"sentence-transformers/all-MiniLM-L6-v2"}
|
|
193
212
|
|
|
194
213
|
# Local cache
|
|
195
|
-
MEMORY_LOCAL_STORAGE_PATH=${
|
|
196
|
-
MEMORY_LOCAL_CACHE_MB=${
|
|
214
|
+
MEMORY_LOCAL_STORAGE_PATH=${e.get("MEMORY_LOCAL_STORAGE_PATH")??""}
|
|
215
|
+
MEMORY_LOCAL_CACHE_MB=${e.get("MEMORY_LOCAL_CACHE_MB")??"200"}
|
|
197
216
|
|
|
198
217
|
# Automation
|
|
199
|
-
MEMORY_AUTO_CAPTURE=${
|
|
200
|
-
MEMORY_AUTO_RETRIEVE=${
|
|
201
|
-
MEMORY_AUTO_SUMMARIZE=${
|
|
202
|
-
MEMORY_AUTO_SYNC=${
|
|
218
|
+
MEMORY_AUTO_CAPTURE=${e.get("MEMORY_AUTO_CAPTURE")??"true"}
|
|
219
|
+
MEMORY_AUTO_RETRIEVE=${e.get("MEMORY_AUTO_RETRIEVE")??"true"}
|
|
220
|
+
MEMORY_AUTO_SUMMARIZE=${e.get("MEMORY_AUTO_SUMMARIZE")??"true"}
|
|
221
|
+
MEMORY_AUTO_SYNC=${e.get("MEMORY_AUTO_SYNC")??"true"}
|
|
203
222
|
|
|
204
223
|
# Retrieval
|
|
205
|
-
MEMORY_MAX_CANDIDATES=${
|
|
206
|
-
MEMORY_RERANK_TOP=${
|
|
207
|
-
MEMORY_FINAL_CONTEXT=${
|
|
208
|
-
MEMORY_TOKEN_BUDGET=${
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
224
|
+
MEMORY_MAX_CANDIDATES=${e.get("MEMORY_MAX_CANDIDATES")??"50"}
|
|
225
|
+
MEMORY_RERANK_TOP=${e.get("MEMORY_RERANK_TOP")??"10"}
|
|
226
|
+
MEMORY_FINAL_CONTEXT=${e.get("MEMORY_FINAL_CONTEXT")??"5"}
|
|
227
|
+
MEMORY_TOKEN_BUDGET=${e.get("MEMORY_TOKEN_BUDGET")??"2000"}
|
|
228
|
+
|
|
229
|
+
# Automatic Session Memory
|
|
230
|
+
TOOLNET_SESSION_LEARNING=${e.get("TOOLNET_SESSION_LEARNING")??"1"}
|
|
231
|
+
TOOLNET_WORK_CONTINUITY=${e.get("TOOLNET_WORK_CONTINUITY")??"1"}
|
|
232
|
+
TOOLNET_SEMANTIC_CONTINUITY=${e.get("TOOLNET_SEMANTIC_CONTINUITY")??"1"}
|
|
233
|
+
TOOLNET_SMART_HANDOFF=${e.get("TOOLNET_SMART_HANDOFF")??"1"}
|
|
234
|
+
`;m.writeFileSync(_,o,{encoding:"utf8",mode:384}),m.chmodSync(x,448),m.chmodSync(_,384)}function G(e,t=!0){let o=e.trim().toLowerCase();return o?o==="y"||o==="yes":t}async function Ne(e){return d.isTTY?(S.write(e),new Promise(t=>{let o="",r=()=>{d.off("data",n),d.setRawMode?.(!1),d.pause(),S.write(`
|
|
235
|
+
`),t(o)},n=s=>{for(let i of s.toString("utf8")){if(i==="\r"||i===`
|
|
236
|
+
`){r();return}if(i===""&&(d.setRawMode?.(!1),S.write(`
|
|
237
|
+
`),process.exit(130)),i==="\x7F"){o=o.slice(0,-1);continue}o+=i}};d.resume(),d.setRawMode?.(!0),d.on("data",n)})):""}function M(){try{let t=I().filter(o=>o.installed);if(t.length>0){console.log(""),console.log("Automatic AI memory:");for(let o of t){let r=o.agent==="agy"?"Agy / Antigravity":o.agent==="opencode"?"OpenCode":"Codex";console.log(` \u2713 ${r}`)}}}catch{}}function u(e,t,o,r){o.trim()?e.set(t,o.trim()):!e.get(t)&&r!==void 0&&e.set(t,r)}async function he(e,t){console.log("Storage provider:"),console.log(" 1. Cloudflare R2 (recommended)"),console.log(" 2. S3 / S3-compatible"),console.log(" 3. Local"),console.log(" 4. Hugging Face S3 (legacy)"),console.log("");let o=t==="s3"?"2":t==="local"?"3":t==="huggingface"?"4":"1",n=(await e.question(`Choose [${o}]: `)).trim()||o;return n==="2"?"s3":n==="3"?"local":n==="4"?"huggingface":"r2"}async function Ie(){let e=m.existsSync(_),t=e?Re(m.readFileSync(_,"utf8")):new Map,o=J(t),r=B(o),n=r.every(c=>!!t.get(c)?.trim());if(console.log(""),console.log("TOOLNET MEMORY SETUP"),console.log("===================="),console.log(""),console.log(`Config: ${_}`),console.log(""),!d.isTTY||!S.isTTY){e||(t.set("MEMORY_STORAGE_PROVIDER",o),j(t)),n?console.log(`\u2713 ${y(o)} already configured`):(console.log("Configuration pending."),console.log("Run:"),console.log(" toolnet-memory setup")),M();return}let s=Ae.createInterface({input:d,output:S});if(n){console.log(`\u2713 ${y(o)} already configured`),console.log("");let c=await s.question("Use existing storage configuration? (Y/n) [Y]: ");if(G(c)){s.close(),console.log(""),console.log(`\u2713 Existing ${y(o)} configuration kept`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor");return}console.log("")}o=await he(s,o),t.set("MEMORY_STORAGE_PROVIDER",o),console.log("");let i="",a="";if(o==="r2"){let c=await s.question(t.get("R2_ACCOUNT_ID")?`Cloudflare Account ID [${t.get("R2_ACCOUNT_ID")}]: `:"Cloudflare Account ID: "),g=await s.question(`R2 Bucket [${t.get("R2_BUCKET")||"toolnet-memory"}]: `),E=await s.question(t.get("R2_ACCESS_KEY_ID")?"R2 Access Key ID [configured]: ":"R2 Access Key ID: ");u(t,"R2_ACCOUNT_ID",c),u(t,"R2_BUCKET",g,"toolnet-memory"),u(t,"R2_ACCESS_KEY_ID",E),i="R2_SECRET_ACCESS_KEY",a=t.get(i)?"R2 Secret Access Key [configured]: ":"R2 Secret Access Key: "}else if(o==="s3"){let c=await s.question(t.get("S3_ENDPOINT")?`S3 Endpoint [${t.get("S3_ENDPOINT")}]: `:"S3 Endpoint [blank = AWS S3]: "),g=await s.question(`S3 Region [${t.get("S3_REGION")||"us-east-1"}]: `),E=await s.question(`S3 Bucket [${t.get("S3_BUCKET")||"toolnet-memory"}]: `),l=await s.question(t.get("S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: "),F=await s.question(`Force path-style? (y/N) [${t.get("S3_FORCE_PATH_STYLE")==="true"?"Y":"N"}]: `);c.trim()&&t.set("S3_ENDPOINT",c.trim()),u(t,"S3_REGION",g,"us-east-1"),u(t,"S3_BUCKET",E,"toolnet-memory"),u(t,"S3_ACCESS_KEY_ID",l),F.trim()?t.set("S3_FORCE_PATH_STYLE",G(F,!1)?"true":"false"):t.get("S3_FORCE_PATH_STYLE")||t.set("S3_FORCE_PATH_STYLE","false"),i="S3_SECRET_ACCESS_KEY",a=t.get(i)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}else if(o==="huggingface"){let c=await s.question(t.get("HF_NAMESPACE")?`Hugging Face namespace [${t.get("HF_NAMESPACE")}]: `:"Hugging Face namespace: "),g=await s.question(`Bucket [${t.get("HF_BUCKET")||"toolnet-memory"}]: `),E=await s.question(t.get("HF_S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: ");u(t,"HF_NAMESPACE",c),u(t,"HF_BUCKET",g,"toolnet-memory"),u(t,"HF_S3_ACCESS_KEY_ID",E),i="HF_S3_SECRET_ACCESS_KEY",a=t.get(i)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}if(s.close(),i){let c=await Ne(a);c.trim()&&t.set(i,c.trim())}j(t),r=B(o);let f=r.filter(c=>!t.get(c)?.trim());if(console.log(""),console.log("\u2713 Configuration saved"),console.log(` ${_}`),console.log(""),f.length){console.log("Missing configuration:");for(let c of f)console.log(` - ${c}`);console.log(""),console.log("Run setup again:"),console.log(" toolnet-memory setup");return}console.log(`\u2713 ${y(o)} configuration complete`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor")}Ie().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1)});
|
package/bundle/snapshot.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import"dotenv/config";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}),
|
|
4
|
-
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=z(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};var
|
|
1
|
+
import"dotenv/config";import{existsSync as B,readFileSync as V}from"node:fs";import{homedir as U}from"node:os";import{join as J}from"node:path";function F(o){let e=o.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Y(){let o=process.env.TOOLNET_GLOBAL_ENV??J(U(),".config","toolnet-memory",".env");if(!B(o))return;let e=V(o,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let s=r.indexOf("=");if(s<=0)continue;let n=r.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=F(r.slice(s+1)))}}Y();function g(o,e){return o===void 0?e:["1","true","yes","on"].includes(o.toLowerCase())}function l(o,e){if(!o)return e;let t=Number(o);return Number.isFinite(t)?t:e}function R(){return{memory:{autoCapture:g(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:g(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:g(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:g(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:l(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:l(process.env.MEMORY_RERANK_TOP,10),finalContext:l(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:l(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:g(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:l(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as H}from"node:crypto";import{existsSync as P,mkdirSync as G,readFileSync as W,renameSync as Z,writeFileSync as Q}from"node:fs";import{basename as X,dirname as S,join as v,parse as T,resolve as f}from"node:path";var I=".toolnet",q="project.json";function ee(o){return H("sha256").update(o).digest("hex").slice(0,16)}function b(o){return v(o,I,q)}function te(o){return P(b(o))}function re(o,e){let t=f(o),r=T(t).root;for(;;){if(te(t))return t;if(t===r||e&&t===f(e))break;let s=S(t);if(s===t)break;t=s}return null}function oe(o){let e=f(o),t=T(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(n=>P(v(e,n))))return e;if(e===t)break;let s=S(e);if(s===e)break;e=s}return f(o)}function se(o){let e;try{e=JSON.parse(W(o,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${o}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${o}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${o}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${o}`);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:S(S(o)),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 E(o,e){let t=v(o,I);G(t,{recursive:!0});let r=b(o),s=`${r}.tmp-${process.pid}`;Q(s,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),Z(s,r)}function C(o,e){return{id:o.id,name:o.name,remote:o.remote,rootPath:e,createdAt:o.createdAt,updatedAt:o.updatedAt,graphVersion:o.graphVersion,memoryVersion:o.memoryVersion,metadata:o.metadata}}var j=class{detect(e=process.cwd()){let t=f(e),r=oe(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>P(v(r,d))),i=re(t,n?r:void 0);if(i){let d=b(i),u=se(d);return u.rootPath!==i&&(u.rootPath=i,u.updatedAt=new Date().toISOString(),E(i,u)),C(u,i)}let a=new Date().toISOString(),c=X(r),p={version:1,id:ee(r),name:c,remote:c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return E(r,p),C(p,r)}};import{homedir as Re}from"node:os";import{join as Ee}from"node:path";import{DeleteObjectCommand as ne,GetObjectCommand as ie,HeadObjectCommand as ae,ListObjectsV2Command as ce,PutObjectCommand as pe,S3Client as de}from"@aws-sdk/client-s3";import{getSignedUrl as ue}from"@aws-sdk/s3-request-presigner";var w=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new de({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new pe({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await ue(this.client,new ie({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new ae({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 ne({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new ce({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};import{access as M,mkdir as me,readFile as ge,readdir as le,rm as fe,stat as O,writeFile as ye}from"node:fs/promises";import{dirname as he,join as Se,relative as _,resolve as je}from"node:path";var y=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return je(this.root,t)}async put(e,t){let r=this.path(e);await me(he(r),{recursive:!0}),await ye(r,t)}async get(e){try{return await ge(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 M(this.path(e)),!0}catch{return!1}}async delete(e){await fe(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await M(t)}catch{return r}let s=async i=>{let a=await le(i,{withFileTypes:!0});for(let c of a){let p=Se(i,c.name);if(c.isDirectory()){await s(p);continue}let d=await O(p);r.push({key:_(this.root,p),size:d.size,updatedAt:d.mtime.toISOString()})}},n=await O(t);return n.isDirectory()?await s(t):r.push({key:_(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),r}};import{DeleteObjectCommand as ve,GetObjectCommand as we,HeadObjectCommand as xe,ListObjectsV2Command as Pe,PutObjectCommand as be,S3Client as ke}from"@aws-sdk/client-s3";import{getSignedUrl as Ae}from"@aws-sdk/s3-request-presigner";var h=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new ke({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new be({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await Ae(this.client,new we({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new xe({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 ve({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new Pe({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};function k(o,e){return console.warn(e),new y(o)}function $(o){let e=o.localRoot??Ee(Re(),".toolnet-memory","storage");if(o.provider==="r2"){let t=o.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new h({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):k(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(o.provider==="s3"){let t=o.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new h({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):k(e,"[storage] S3 credentials missing. Using local fallback.")}if(o.provider==="huggingface"){let t=o.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new w({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):k(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new y(e)}function Ce(o){return new Promise(e=>setTimeout(e,o))}async function K(o,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,s=e.maxDelayMs??2e3,n;for(let i=1;i<=t;i++)try{return await o()}catch(a){if(n=a,i>=t)break;let c=Math.min(s,r*2**(i-1)),p=Math.floor(Math.random()*Math.max(1,c*.2));await Ce(c+p)}throw n}var Te=new Set(["put","get","getText","delete","list"]);function N(o,e={}){return new Proxy(o,{get(t,r){let s=Reflect.get(t,r,t);return typeof s!="function"?s:Te.has(r)?(...n)=>K(()=>Promise.resolve(s.apply(t,n)),e):s.bind(t)}})}function D(o){let e=o.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function z(o){let e=o.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var x=class{constructor(e,t,r,s){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=D(s??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,s=await this.provider.getText(e);if(s){let i;try{i=JSON.parse(s)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
|
|
4
|
+
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=z(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};var Ie=["memories/current.json","vectors/current.json","graph/current.json","graph/manifest.json","graph/resolution/current.json","code/chunks/current.json","code/vectors/current.json","code/architecture/current.json","code/architecture/entry-points.json","code/architecture/hotspots.json","code/architecture/layers.json","code/architecture/clusters.json","code/analysis/current.json","code/analysis/dead-code.json","code/analysis/dependencies.json","code/visualization/graph.json"],m=class{constructor(e){this.storage=e}storage;projectPrefix(e){return`projects/${e}`}snapshotPrefix(e,t){return[this.projectPrefix(e),"snapshots",t].join("/")}async create(e,t="automatic",r){let s=new Date().toISOString(),n=s.replace(/[:.]/g,"-"),i=this.projectPrefix(e),a=this.snapshotPrefix(e,n),c=[];for(let d of Ie){let u=`${i}/${d}`,A=await this.storage.get(u);A&&(await this.storage.put(`${a}/${d}`,A,"application/json"),c.push(d))}if(c.length===0)return null;let p={version:1,id:n,projectId:e,createdAt:s,reason:t,objects:c,metadata:r};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(p,null,2),"application/json"),p}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),s=(await this.storage.list(t)).filter(i=>i.key.endsWith("/snapshot.json")),n=[];for(let i of s){let a=await this.storage.getText(i.key);if(a)try{n.push(JSON.parse(a))}catch{}}return n.sort((i,a)=>a.createdAt.localeCompare(i.createdAt))}async restore(e,t){let r=this.snapshotPrefix(e,t),s=await this.storage.getText(`${r}/snapshot.json`);if(!s)throw new Error(`Snapshot not found: ${t}`);let n=JSON.parse(s);if(n.projectId!==e)throw new Error("Snapshot project mismatch");let i=this.projectPrefix(e),a=[];for(let c of n.objects){let p=await this.storage.get(`${r}/${c}`);p&&(await this.storage.put(`${i}/${c}`,p,"application/json"),a.push(c))}return{restored:a}}async remove(e,t){let r=this.snapshotPrefix(e,t),s=await this.storage.list(r);for(let n of s)await this.storage.delete(n.key);return s.length}async prune(e,t=10){let s=(await this.list(e)).slice(t),n=0;for(let i of s)await this.remove(e,i.id),n++;return n}};async function L(o,e){let t=new m(o),s=(await t.list(e))[0];if(!s)throw new Error("No snapshot available");let n=await t.restore(e,s.id);return{snapshot:s.id,createdAt:s.createdAt,...n}}async function Me(){let o=process.argv[2],e=R(),t=new j().detect(),r=N($({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),s=new x(r,t.id,t.name,t.remote??t.name),n=new m(s);if(o==="list"){let i=await n.list(t.id);console.log(i.map(a=>({id:a.id,createdAt:a.createdAt,reason:a.reason})));return}if(o==="create"){let i=process.argv.slice(3).join(" ")||"manual-cli";console.log(await n.create(t.id,i));return}if(o==="restore"){let i=process.argv[3];if(!i)throw new Error("snapshot id required");await n.create(t.id,"before-cli-restore"),console.log(await n.restore(t.id,i));return}if(o==="recover-latest"){console.log(await L(s,t.id));return}throw new Error("Usage: snapshot-cli list|create|restore|recover-latest")}Me().catch(o=>{console.error(o instanceof Error?o.message:o),process.exit(1)});
|
|
@@ -1,12 +1,12 @@
|
|
|
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}),
|
|
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"),
|
|
1
|
+
import{existsSync as ae,readFileSync as ce}from"node:fs";import{homedir as pe}from"node:os";import{join as ue}from"node:path";function de(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 le(){let r=process.env.TOOLNET_GLOBAL_ENV??ue(pe(),".config","toolnet-memory",".env");if(!ae(r))return;let e=ce(r,"utf8");for(let t of e.split(/\r?\n/)){let o=t.trim();if(!o||o.startsWith("#"))continue;o.startsWith("export ")&&(o=o.slice(7));let s=o.indexOf("=");if(s<=0)continue;let i=o.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=de(o.slice(s+1)))}}le();function h(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function S(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function B(){return{memory:{autoCapture:h(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:h(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:h(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:h(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:S(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:S(process.env.MEMORY_RERANK_TOP,10),finalContext:S(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:S(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:h(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:S(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as me}from"node:crypto";import{existsSync as E,mkdirSync as ge,readFileSync as fe,renameSync as ye,writeFileSync as he}from"node:fs";import{basename as Se,dirname as x,join as P,parse as W,resolve as k}from"node:path";var J=".toolnet",ke="project.json";function je(r){return me("sha256").update(r).digest("hex").slice(0,16)}function C(r){return P(r,J,ke)}function ve(r){return E(C(r))}function we(r,e){let t=k(r),o=W(t).root;for(;;){if(ve(t))return t;if(t===o||e&&t===k(e))break;let s=x(t);if(s===t)break;t=s}return null}function xe(r){let e=k(r),t=W(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(i=>E(P(e,i))))return e;if(e===t)break;let s=x(e);if(s===e)break;e=s}return k(r)}function be(r){let e;try{e=JSON.parse(fe(r,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${r}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let o=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:x(x(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:o,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:o,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function L(r,e){let t=P(r,J);ge(t,{recursive:!0});let o=C(r),s=`${o}.tmp-${process.pid}`;he(s,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),ye(s,o)}function V(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 b=class{detect(e=process.cwd()){let t=k(e),o=xe(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>E(P(o,d))),a=we(t,i?o:void 0);if(a){let d=C(a),m=be(d);return m.rootPath!==a&&(m.rootPath=a,m.updatedAt=new Date().toISOString(),L(a,m)),V(m,a)}let c=new Date().toISOString(),p=Se(o),u={version:1,id:je(o),name:p,remote:p,rootPath:o,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return L(o,u),V(u,o)}};import{homedir as He}from"node:os";import{join as Ye}from"node:path";import{DeleteObjectCommand as Pe,GetObjectCommand as Ae,HeadObjectCommand as Re,ListObjectsV2Command as Te,PutObjectCommand as Ie,S3Client as Ee}from"@aws-sdk/client-s3";import{getSignedUrl as Ce}from"@aws-sdk/s3-request-presigner";var A=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ee({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 s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ie({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Ce(this.client,new Ae({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 Re({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 Pe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new Te({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let i of s.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};import{access as F,mkdir as Oe,readFile as Me,readdir as _e,rm as Ne,stat as U,writeFile as $e}from"node:fs/promises";import{dirname as Ke,join as De,relative as H,resolve as ze}from"node:path";var j=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return ze(this.root,t)}async put(e,t){let o=this.path(e);await Oe(Ke(o),{recursive:!0}),await $e(o,t)}async get(e){try{return await Me(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 F(this.path(e)),!0}catch{return!1}}async delete(e){await Ne(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await F(t)}catch{return o}let s=async a=>{let c=await _e(a,{withFileTypes:!0});for(let p of c){let u=De(a,p.name);if(p.isDirectory()){await s(u);continue}let d=await U(u);o.push({key:H(this.root,u),size:d.size,updatedAt:d.mtime.toISOString()})}},i=await U(t);return i.isDirectory()?await s(t):o.push({key:H(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),o}};import{DeleteObjectCommand as Be,GetObjectCommand as Le,HeadObjectCommand as Ve,ListObjectsV2Command as We,PutObjectCommand as Je,S3Client as Fe}from"@aws-sdk/client-s3";import{getSignedUrl as Ue}from"@aws-sdk/s3-request-presigner";var v=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Fe({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 s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Je({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Ue(this.client,new Le({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 Ve({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 Be({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new We({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let i of s.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};function O(r,e){return console.warn(e),new j(r)}function Y(r){let e=r.localRoot??Ye(He(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new v({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(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 v({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(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 A({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new j(e)}function Ge(r){return new Promise(e=>setTimeout(e,r))}async function G(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,s=e.maxDelayMs??2e3,i;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(i=c,a>=t)break;let p=Math.min(s,o*2**(a-1)),u=Math.floor(Math.random()*Math.max(1,p*.2));await Ge(p+u)}throw i}var Ze=new Set(["put","get","getText","delete","list"]);function Z(r,e={}){return new Proxy(r,{get(t,o){let s=Reflect.get(t,o,t);return typeof s!="function"?s:Ze.has(o)?(...i)=>G(()=>Promise.resolve(s.apply(t,i)),e):s.bind(t)}})}function Q(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 X(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 s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var R=class{constructor(e,t,o,s){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=o,this.folder=Q(s??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,s=await this.provider.getText(e);if(s){let a;try{a=JSON.parse(s)}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 i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:o,updatedAt:t};await this.provider.put(e,JSON.stringify(i,null,2)+`
|
|
4
|
+
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=X(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,o){return await this.ensureRegistered(),this.provider.put(this.key(e),t,o)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as te}from"node:path";import{mkdirSync as rt}from"node:fs";import{createHash as Qe}from"node:crypto";import{dirname as Xe}from"node:path";import{mkdirSync as qe,readFileSync as to,renameSync as et,writeFileSync as tt}from"node:fs";function q(r){return Qe("sha256").update(r).digest("hex")}function ee(r,e){qe(Xe(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;tt(t,JSON.stringify(e,null,2)+`
|
|
5
|
+
`,{encoding:"utf8",mode:384}),et(t,r)}function ot(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function y(r,e=20){let t=[],o=new Set;for(let s of r.slice().reverse()){let i=ot(s);if(!(!i||o.has(i))&&(o.add(i),t.push(s),t.length>=e))break}return t.reverse()}function st(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 re(r,e){let t=e.status??r?.status??"pending",o=t;r&&(r.status==="completed"&&t!=="completed"?o="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(o=r.status));let s=r&&st(e)?r.title:e.text;return{id:r?.id??q(e.key).slice(0,24),title:s,status:o,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 nt(r,e){let t=`projects/${r.id}/work/observations/`,o=await e.list(t),s=[];for(let i of o.filter(a=>a.key.endsWith(".json")).sort((a,c)=>a.key.localeCompare(c.key))){let a=await e.getText(i.key);if(a)try{let c=JSON.parse(a);c.version===1&&Array.isArray(c.observations)&&s.push(c)}catch{}}return s}async function oe(r,e){let o=(await nt(r,e)).flatMap(n=>n.observations).sort((n,f)=>{let D=n.occurredAt.localeCompare(f.occurredAt);if(D!==0)return D;let z=n.sequence-f.sequence;return z!==0?z:n.id.localeCompare(f.id)}),s=new Map,i=new Map,a,c,p,u=[],d=[],m=[],_=[],N=[],$=[];for(let n of o)switch(n.kind){case"goal":a=n.text;break;case"plan":c=n.text;break;case"phase":s.set(n.key,re(s.get(n.key),n));break;case"task":i.set(n.key,re(i.get(n.key),n));break;case"decision":u.push(n.text);break;case"blocker":d.push(n.text);break;case"warning":m.push(n.text);break;case"next_action":_.push(n.text);break;case"file":N.push(n.text);break;case"test":$.push(n.text);break;case"session":p={agent:n.agent,nativeSessionId:n.nativeSessionId,sessionKey:n.sessionKey,updatedAt:n.occurredAt};break}let g=Array.from(s.values()).sort((n,f)=>(n.order??Number.MAX_SAFE_INTEGER)-(f.order??Number.MAX_SAFE_INTEGER)),l=Array.from(i.values()).sort((n,f)=>(n.order??Number.MAX_SAFE_INTEGER)-(f.order??Number.MAX_SAFE_INTEGER)),T=g.find(n=>n.status==="in_progress")??g.find(n=>n.status==="blocked")??g.find(n=>n.status==="pending"),w=l.find(n=>n.status==="in_progress")??l.find(n=>n.status==="blocked")??l.find(n=>n.status==="pending"),ne=y([..._,...w?[w.title]:[],...!w&&T?[T.title]:[],...l.filter(n=>n.status==="pending").slice(0,5).map(n=>n.title)],8),ie=y([...d,...g.filter(n=>n.status==="blocked").map(n=>n.title),...l.filter(n=>n.status==="blocked").map(n=>n.title)],20),I={version:1,projectId:r.id,projectName:r.name,goal:a,plan:c,phases:g,tasks:l,decisions:y(u,20),blockers:ie,warnings:y(m,20),nextActions:ne,filesTouched:y(N,30),tests:y($,20),currentPhase:T,currentTask:w,progress:{phasesTotal:g.length,phasesCompleted:g.filter(n=>n.status==="completed").length,tasksTotal:l.length,tasksCompleted:l.filter(n=>n.status==="completed").length,blocked:g.filter(n=>n.status==="blocked").length+l.filter(n=>n.status==="blocked").length},lastSession:p,updatedAt:o.length?o[o.length-1].occurredAt:new Date().toISOString()},K=te(r.rootPath,".toolnet","work");return rt(K,{recursive:!0}),ee(te(K,"current.json"),I),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(I,null,2)+`
|
|
6
|
+
`,"application/json"),I}async function M(r,e){let t=await e.getText(`projects/${r.id}/work/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function it(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function at(r){let e=B(),t=Z(Y({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new R(t,r.id,r.name,r.remote??r.name)}function se(r){console.log(),console.log(`Project: ${r.projectName}`),r.goal&&console.log(`Goal: ${r.goal}`),r.currentPhase&&console.log(`Current phase: ${r.currentPhase.title} [${r.currentPhase.status}]`),r.currentTask&&console.log(`Current task: ${r.currentTask.title} [${r.currentTask.status}]`),console.log(`Phases: ${r.progress.phasesCompleted}/${r.progress.phasesTotal}`),console.log(`Tasks: ${r.progress.tasksCompleted}/${r.progress.tasksTotal}`),r.nextActions.length&&(console.log(),console.log("Next actions:"),r.nextActions.forEach((e,t)=>console.log(`${t+1}. ${e}`))),r.blockers.length&&(console.log(),console.log("Blockers:"),r.blockers.forEach(e=>console.log(`- ${e}`))),r.lastSession&&(console.log(),console.log(`Last session: ${r.lastSession.agent} / ${r.lastSession.nativeSessionId}`))}async function ct(){let[r="help",...e]=process.argv.slice(2),t=new b().detect(it(e,"--project")??process.cwd()),o=at(t);if(r==="reconcile"){let s=await oe(t,o);se(s);return}if(r==="status"){let s=await M(t,o);if(!s){console.log("No active work state yet.");return}se(s);return}if(r==="json"){let s=await M(t,o);console.log(JSON.stringify(s,null,2));return}console.log(`Work Continuity
|
|
7
7
|
|
|
8
8
|
Commands:
|
|
9
9
|
status [--project PATH]
|
|
10
10
|
json [--project PATH]
|
|
11
11
|
reconcile [--project PATH]
|
|
12
|
-
`)}
|
|
12
|
+
`)}ct().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolnet-memory",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
|
+
"description": "Persistent project memory, work continuity, and code intelligence for AI coding agents.",
|
|
4
5
|
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/LBT-AI/toolnet-memory.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/LBT-AI/toolnet-memory#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/LBT-AI/toolnet-memory/issues"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ai",
|
|
20
|
+
"memory",
|
|
21
|
+
"coding-agents",
|
|
22
|
+
"mcp",
|
|
23
|
+
"opencode",
|
|
24
|
+
"codex",
|
|
25
|
+
"antigravity",
|
|
26
|
+
"code-intelligence"
|
|
27
|
+
],
|
|
5
28
|
"scripts": {
|
|
6
29
|
"dev": "tsx src/index.ts",
|
|
7
30
|
"build": "tsc -p tsconfig.json",
|