toolnet-memory 0.3.12 → 0.3.13

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/bundle/doctor.js CHANGED
@@ -1,6 +1,4 @@
1
- import"dotenv/config";import{existsSync as Re,readFileSync as Te}from"node:fs";import{homedir as $e}from"node:os";import{join as Le}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 Ne(){let r=process.env.TOOLNET_GLOBAL_ENV??Le($e(),".config","toolnet-memory",".env");if(!Re(r))return;let e=Te(r,"utf8");for(let t of e.split(/\r?\n/)){let o=t.trim();if(!o||o.startsWith("#"))continue;o.startsWith("export ")&&(o=o.slice(7));let n=o.indexOf("=");if(n<=0)continue;let s=o.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=De(o.slice(n+1)))}}Ne();function S(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function E(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function se(){return{memory:{autoCapture:S(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:S(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:S(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:S(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:E(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:E(process.env.MEMORY_RERANK_TOP,10),finalContext:E(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:E(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:S(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:E(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Ke}from"node:crypto";import{existsSync as Z,mkdirSync as Ue,readFileSync as Be,renameSync as Fe,writeFileSync as He}from"node:fs";import{basename as Ge,dirname as O,join as C,parse as ce,resolve as k}from"node:path";var de=".toolnet",Ve="project.json";function Ye(r){return Ke("sha256").update(r).digest("hex").slice(0,16)}function X(r){return C(r,de,Ve)}function ze(r){return Z(X(r))}function We(r,e){let t=k(r),o=ce(t).root;for(;;){if(ze(t))return t;if(t===o||e&&t===k(e))break;let n=O(t);if(n===t)break;t=n}return null}function Je(r){let e=k(r),t=ce(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(s=>Z(C(e,s))))return e;if(e===t)break;let n=O(e);if(n===e)break;e=n}return k(r)}function qe(r){let e;try{e=JSON.parse(Be(r,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${r}: ${n instanceof Error?n.message:String(n)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let o=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:O(O(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 ie(r,e){let t=C(r,de);Ue(t,{recursive:!0});let o=X(r),n=`${o}.tmp-${process.pid}`;He(n,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),Fe(n,o)}function ae(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 x=class{detect(e=process.cwd()){let t=k(e),o=Je(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>Z(C(o,u))),a=We(t,s?o:void 0);if(a){let u=X(a),l=qe(u);return l.rootPath!==a&&(l.rootPath=a,l.updatedAt=new Date().toISOString(),ie(a,l)),ae(l,a)}let c=new Date().toISOString(),d=Ge(o),m={version:1,id:Ye(o),name:d,remote:d,rootPath:o,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return ie(o,m),ae(m,o)}};import{homedir as vt}from"node:os";import{join as At}from"node:path";import{DeleteObjectCommand as Qe,GetObjectCommand as Ze,HeadObjectCommand as Xe,ListObjectsV2Command as et,PutObjectCommand as tt,S3Client as rt}from"@aws-sdk/client-s3";import{getSignedUrl as ot}from"@aws-sdk/s3-request-presigner";var M=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new rt({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,o="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new tt({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await ot(this.client,new Ze({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 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 Qe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new et({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return t}};import{access as le,mkdir as nt,readFile as st,readdir as it,rm as at,stat as pe,writeFile as ct}from"node:fs/promises";import{dirname as dt,join as lt,relative as ue,resolve as pt}from"node:path";var w=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return pt(this.root,t)}async put(e,t){let o=this.path(e);await nt(dt(o),{recursive:!0}),await ct(o,t)}async get(e){try{return await st(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await le(this.path(e)),!0}catch{return!1}}async delete(e){await at(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await le(t)}catch{return o}let n=async a=>{let c=await it(a,{withFileTypes:!0});for(let d of c){let m=lt(a,d.name);if(d.isDirectory()){await n(m);continue}let u=await pe(m);o.push({key:ue(this.root,m),size:u.size,updatedAt:u.mtime.toISOString()})}},s=await pe(t);return s.isDirectory()?await n(t):o.push({key:ue(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),o}};import{DeleteObjectCommand as ut,GetObjectCommand as mt,HeadObjectCommand as gt,ListObjectsV2Command as ft,PutObjectCommand as yt,S3Client as ht}from"@aws-sdk/client-s3";import{getSignedUrl as bt}from"@aws-sdk/s3-request-presigner";var _=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new ht({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,o="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new yt({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await bt(this.client,new mt({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 gt({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new ut({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new ft({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return t}};function ee(r,e){return console.warn(e),new w(r)}function me(r){let e=r.localRoot??At(vt(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new _({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):ee(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 _({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):ee(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 M({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):ee(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new w(e)}var j=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"memories","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return[];let o=JSON.parse(t);if(!Array.isArray(o))throw new Error("Invalid memory snapshot");return o}async save(e,t){let o=JSON.stringify(t,null,2);await this.storage.put(this.key(e),o,"application/json")}};var R=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return null;let o=JSON.parse(t);if(o.version!==1||o.projectId!==e||!Array.isArray(o.records))throw new Error("Invalid vector snapshot");return o}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var T=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var $=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","chunks","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var L=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 St(r){return new Promise(e=>setTimeout(e,r))}async function ge(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,n=e.maxDelayMs??2e3,s;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(s=c,a>=t)break;let d=Math.min(n,o*2**(a-1)),m=Math.floor(Math.random()*Math.max(1,d*.2));await St(d+m)}throw s}var Et=new Set(["put","get","getText","delete","list"]);function fe(r,e={}){return new Proxy(r,{get(t,o){let n=Reflect.get(t,o,t);return typeof n!="function"?n:Et.has(o)?(...s)=>ge(()=>Promise.resolve(n.apply(t,s)),e):n.bind(t)}})}function ye(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 he(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(o)return`${o[1]}memory/records/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),o)return`${o[1]}memory/vectors/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),o)return`${o[1]}code/graph/${o[2]}`;let n=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:e))}var D=class{constructor(e,t,o,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=o,this.folder=ye(n??o),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),o=t,n=await this.provider.getText(e);if(n){let a;try{a=JSON.parse(n)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(o=a.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:o,updatedAt:t};await this.provider.put(e,JSON.stringify(s,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=he(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))}};var be=[{id:"openai-compatible",label:"OpenAI-compatible",requiresApiKey:!0,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"alibaba",label:"Alibaba / DashScope",requiresApiKey:!0,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"openrouter",label:"OpenRouter",defaultBaseUrl:"https://openrouter.ai/api/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"groq",label:"Groq",defaultBaseUrl:"https://api.groq.com/openai/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"deepseek",label:"DeepSeek",defaultBaseUrl:"https://api.deepseek.com",defaultModel:"deepseek-v4-flash",requiresApiKey:!0,transport:"openai-compatible"},{id:"nvidia",label:"NVIDIA NIM",defaultBaseUrl:"https://integrate.api.nvidia.com/v1",defaultModel:"deepseek-ai/deepseek-v4-pro",requiresApiKey:!0,transport:"openai-compatible"},{id:"gemini",label:"Gemini",defaultBaseUrl:"https://generativelanguage.googleapis.com/v1beta",requiresApiKey:!0,transport:"gemini"},{id:"huggingface",label:"Hugging Face",defaultBaseUrl:"https://router.huggingface.co/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"ollama",label:"Ollama / Local",defaultBaseUrl:"http://127.0.0.1:11434/v1",requiresApiKey:!1,transport:"openai-compatible"},{id:"custom",label:"Custom endpoint",requiresApiKey:!1,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"cloudflare",label:"Cloudflare Workers AI",requiresApiKey:!0,requiresAccountId:!0,transport:"cloudflare"}];function b(r){let e=be.find(t=>t.id===r);if(!e)throw new Error(`Unsupported AI provider: ${r}`);return e}function N(r){return be.some(e=>e.id===r)}function i(r){return process.env[r]?.trim()||void 0}function g(...r){return r.find(e=>!!e?.trim())}function ve(){if(i("GROQ_API_KEY"))return"groq";if(i("DEEPSEEK_API_KEY"))return"deepseek";if(i("NVIDIA_API_KEY")||i("NVIDIA_NIM_API_KEY"))return"nvidia";if(i("OPENROUTER_API_KEY"))return"openrouter";if(i("ALIBABA_API_KEY")||i("DASHSCOPE_API_KEY"))return"alibaba";if(i("GEMINI_API_KEY")||i("GOOGLE_API_KEY"))return"gemini";if(i("CLOUDFLARE_API_TOKEN")&&i("CLOUDFLARE_ACCOUNT_ID"))return"cloudflare";if(i("HF_TOKEN"))return"huggingface";if(i("OLLAMA_MODEL")||i("OLLAMA_BASE_URL"))return"ollama"}function Ae(){let r=i("TOOLNET_LLM_PROVIDER");return r&&N(r)?r:ve()??"openai-compatible"}function K(r){let e=b(r);switch(r){case"alibaba":return{provider:r,apiKey:g(i("ALIBABA_API_KEY"),i("DASHSCOPE_API_KEY")),baseUrl:g(i("ALIBABA_BASE_URL"),i("DASHSCOPE_BASE_URL"),e.defaultBaseUrl),model:g(i("ALIBABA_MODEL"),i("DASHSCOPE_MODEL"))};case"openrouter":return{provider:r,apiKey:i("OPENROUTER_API_KEY"),baseUrl:g(i("OPENROUTER_BASE_URL"),e.defaultBaseUrl),model:i("OPENROUTER_MODEL")};case"groq":return{provider:r,apiKey:i("GROQ_API_KEY"),baseUrl:g(i("GROQ_BASE_URL"),e.defaultBaseUrl),model:i("GROQ_MODEL")};case"deepseek":return{provider:r,apiKey:i("DEEPSEEK_API_KEY"),baseUrl:g(i("DEEPSEEK_BASE_URL"),e.defaultBaseUrl),model:g(i("DEEPSEEK_MODEL"),e.defaultModel)};case"nvidia":return{provider:r,apiKey:g(i("NVIDIA_API_KEY"),i("NVIDIA_NIM_API_KEY")),baseUrl:g(i("NVIDIA_BASE_URL"),i("NVIDIA_NIM_BASE_URL"),e.defaultBaseUrl),model:g(i("NVIDIA_MODEL"),i("NVIDIA_NIM_MODEL"),e.defaultModel)};case"gemini":return{provider:r,apiKey:g(i("GEMINI_API_KEY"),i("GOOGLE_API_KEY")),baseUrl:g(i("GEMINI_BASE_URL"),e.defaultBaseUrl),model:i("GEMINI_MODEL")};case"huggingface":return{provider:r,apiKey:i("HF_TOKEN"),baseUrl:g(i("HF_INFERENCE_BASE_URL"),e.defaultBaseUrl),model:g(i("HF_LLM_MODEL"),i("HF_MODEL"))};case"ollama":return{provider:r,apiKey:i("OLLAMA_API_KEY"),baseUrl:g(i("OLLAMA_BASE_URL"),e.defaultBaseUrl),model:i("OLLAMA_MODEL")};case"cloudflare":return{provider:r,accountId:i("CLOUDFLARE_ACCOUNT_ID"),apiKey:i("CLOUDFLARE_API_TOKEN"),baseUrl:i("CLOUDFLARE_AI_BASE_URL"),model:i("CLOUDFLARE_MODEL")};case"custom":return{provider:r,apiKey:i("CUSTOM_AI_API_KEY"),baseUrl:i("CUSTOM_AI_BASE_URL"),model:i("CUSTOM_AI_MODEL")};default:return{provider:"openai-compatible",apiKey:g(i("OPENAI_API_KEY"),i("MODEL_API_KEY")),baseUrl:g(i("OPENAI_BASE_URL"),i("MODEL_BASE_URL")),model:g(i("OPENAI_MODEL"),i("MODEL_NAME"))}}}function kt(){let r=i("TOOLNET_LLM_PROVIDER"),e=r&&N(r)?r:Ae(),t=b(e),o=K(e);return{provider:e,apiKey:g(i("TOOLNET_LLM_API_KEY"),o.apiKey),baseUrl:g(i("TOOLNET_LLM_BASE_URL"),o.baseUrl,t.defaultBaseUrl),model:g(i("TOOLNET_LLM_MODEL"),o.model,t.defaultModel),accountId:g(i("TOOLNET_LLM_ACCOUNT_ID"),o.accountId)}}function wt(){let r=i("TOOLNET_EMBEDDING_PROVIDER");return r==="local"?"local":r&&N(r)?r:i("HF_TOKEN")||i("HF_EMBEDDING_MODEL")?"huggingface":"local"}function _t(){let r=wt();if(r==="local")return{provider:"local",model:g(i("TOOLNET_EMBEDDING_MODEL"),i("LOCAL_EMBEDDING_MODEL"))};let e=b(r),t,o,n,s;switch(r){case"huggingface":t=i("HF_TOKEN"),o=i("HF_INFERENCE_BASE_URL"),n=i("HF_EMBEDDING_MODEL");break;case"openai-compatible":t=i("OPENAI_API_KEY"),o=i("OPENAI_BASE_URL"),n=i("OPENAI_EMBEDDING_MODEL");break;case"cloudflare":t=i("CLOUDFLARE_API_TOKEN"),o=i("CLOUDFLARE_AI_BASE_URL"),n=i("CLOUDFLARE_EMBEDDING_MODEL"),s=i("CLOUDFLARE_ACCOUNT_ID");break;default:t=K(r).apiKey,o=K(r).baseUrl,n=i(`${r.toUpperCase().replace(/-/g,"_")}_EMBEDDING_MODEL`)}let a=g(i("TOOLNET_EMBEDDING_ACCOUNT_ID"),s),c=g(i("TOOLNET_EMBEDDING_BASE_URL"),o,e.defaultBaseUrl),d=r==="cloudflare"&&!c&&a?`https://api.cloudflare.com/client/v4/accounts/${a}/ai/v1`:c;return{provider:r,apiKey:g(i("TOOLNET_EMBEDDING_API_KEY"),t),baseUrl:d,model:g(i("TOOLNET_EMBEDDING_MODEL"),n),accountId:a}}function P(){let r=kt(),e=_t();return{llm:r,embedding:e,legacy:{llm:!i("TOOLNET_LLM_PROVIDER")&&!!ve(),embedding:!i("TOOLNET_EMBEDDING_PROVIDER")&&!!(i("HF_TOKEN")||i("HF_EMBEDDING_MODEL"))}}}function Se(r=Ae()){let e=P().llm;if(r===e.provider)return{id:r,apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model,accountId:e.accountId};let t=K(r);return{id:r,apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,accountId:t.accountId}}var U=class{constructor(e){this.options=e}options;name="gemini";async embed(e){let t=this.options.model.replace(/^models\//,""),n=`${this.options.baseUrl?.replace(/\/+$/,"")||"https://generativelanguage.googleapis.com/v1beta"}/models/${encodeURIComponent(t)}:embedContent?key=${encodeURIComponent(this.options.apiKey)}`,s=await fetch(n,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({model:`models/${t}`,content:{parts:[{text:e}]}})});if(!s.ok)throw new Error(`Gemini embedding failed: ${s.status} ${await s.text()}`);let c=(await s.json()).embedding?.values;if(!Array.isArray(c)||c.length===0)throw new Error("Gemini embedding returned invalid response");return c}async embedMany(e){let t=[];for(let o of e)t.push(await this.embed(o));return t}};import{createHash as Pt}from"node:crypto";var B=class{name="hash-fallback";dimensions=128;async embed(e){let t=new Array(this.dimensions).fill(0),o=e.toLowerCase().split(/\s+/).filter(Boolean);for(let n of o){let a=Pt("sha256").update(n).digest().readUInt32BE(0)%this.dimensions;t[a]+=1}return It(t)}async embedMany(e){return Promise.all(e.map(t=>this.embed(t)))}};function It(r){let e=Math.sqrt(r.reduce((t,o)=>t+o*o,0));return e===0?r:r.map(t=>t/e)}var F=class{constructor(e){this.options=e;this.dimensions=e.dimensions}options;name="openai-compatible";dimensions;async embed(e){let[t]=await this.embedMany([e]);return t}async embedMany(e){if(e.length===0)return[];let t=this.options.baseUrl.replace(/\/+$/,""),o=await fetch(`${t}/embeddings`,{method:"POST",headers:{"content-type":"application/json",...this.options.apiKey?{authorization:`Bearer ${this.options.apiKey}`}:{}},body:JSON.stringify({model:this.options.model,input:e})});if(!o.ok)throw new Error(`Embedding failed: ${o.status} ${await o.text()}`);let s=(await o.json()).data?.sort((a,c)=>(a.index??0)-(c.index??0)).map(a=>a.embedding);if(!s||s.length!==e.length||s.some(a=>!Array.isArray(a)))throw new Error("Embedding provider returned invalid response");return s}};var H=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`,o=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(!o.ok)throw new Error(`HF embedding failed: ${o.status} ${await o.text()}`);let n=await o.json();if(!Array.isArray(n))throw new Error("HF embedding returned invalid response");return n.map(s=>Ot(s))}};function Ot(r){if(!Array.isArray(r))throw new Error("Invalid embedding");if(r.length>0&&typeof r[0]=="number")return r;if(r.length>0&&Array.isArray(r[0])){let e=r;if(e.length===0)return[];let t=e[0].length,o=new Array(t).fill(0);for(let n of e)for(let s=0;s<t;s++)o[s]+=n[s]??0;for(let n=0;n<t;n++)o[n]/=e.length;return o}throw new Error("Unsupported embedding shape")}var te=class{constructor(e,t=new B){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 xt(r){return`https://api.cloudflare.com/client/v4/accounts/${r}/ai/v1`}function Ct(){let r=P().embedding;if(r.provider==="local")return null;if(r.provider==="huggingface")return!r.apiKey||!r.model?null:new H({token:r.apiKey,model:r.model});if(r.provider==="gemini")return!r.apiKey||!r.model?null:new U({apiKey:r.apiKey,model:r.model,baseUrl:r.baseUrl});let e=r.baseUrl;return r.provider==="cloudflare"&&!e&&r.accountId&&(e=xt(r.accountId)),e&&r.model?new F({apiKey:r.apiKey,baseUrl:e,model:r.model}):null}function Ee(){return new te(Ct())}var re=class extends Error{status;constructor(e,t){super(e),this.name="AiHttpError",this.status=t}};async function v(r,e,t=3e4){let o=new AbortController,n=setTimeout(()=>o.abort(),t);n.unref?.();try{let s=await fetch(r,{...e,signal:o.signal}),a=await s.text();if(!s.ok){let c=a;try{let d=JSON.parse(a);c=d.error?.message??d.message??a}catch{}throw new re(c||`HTTP ${s.status}`,s.status)}return a.trim()?JSON.parse(a):{}}finally{clearTimeout(n)}}function G(r,e){return`${r.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`}var V=class{constructor(e){this.config=e}config;id="cloudflare";model(){let e=this.config.model?.trim();if(!e)throw new Error("cloudflare: MODEL is not configured");return e}async generate(e){let t=this.config.accountId?.trim(),o=this.config.apiKey?.trim();if(!t)throw new Error("cloudflare: ACCOUNT ID is not configured");if(!o)throw new Error("cloudflare: API TOKEN is not configured");let n=this.model(),a=`${(this.config.baseUrl?.trim()||`https://api.cloudflare.com/client/v4/accounts/${t}/ai/run`).replace(/\/+$/,"")}/${n}`,c=await v(a,{method:"POST",headers:{authorization:`Bearer ${o}`,"content-type":"application/json",...this.config.headers},body:JSON.stringify({messages:e.messages,temperature:e.temperature,max_tokens:e.maxTokens})}),d=c.result?.response?.trim();if(!d)throw new Error(c.errors?.[0]?.message??"cloudflare: empty model response");return{text:d,provider:"cloudflare",model:n}}async healthCheck(){let e=Date.now();try{return{ok:!0,provider:"cloudflare",model:(await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8})).model,message:"Provider reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:"cloudflare",model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}};var Y=class{constructor(e){this.config=e}config;id="gemini";model(){let e=this.config.model?.trim();if(!e)throw new Error("gemini: MODEL is not configured");return e.replace(/^models\//,"")}async generate(e){let t=this.config.apiKey?.trim();if(!t)throw new Error("gemini: API KEY is not configured");let o=this.config.baseUrl?.trim()||"https://generativelanguage.googleapis.com/v1beta",n=this.model(),s=e.messages.filter(u=>u.role==="system"),a=e.messages.filter(u=>u.role!=="system").map(u=>({role:u.role==="assistant"?"model":"user",parts:[{text:u.content}]})),c=`${G(o,`models/${encodeURIComponent(n)}:generateContent`)}?key=${encodeURIComponent(t)}`,d=await v(c,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({...s.length?{systemInstruction:{parts:[{text:s.map(u=>u.content).join(`
5
-
6
- `)}]}}:{},contents:a,generationConfig:{temperature:e.temperature,maxOutputTokens:e.maxTokens}})}),m=d.candidates?.[0]?.content?.parts?.map(u=>u.text??"").join("").trim();if(!m)throw new Error("gemini: empty model response");return{text:m,provider:"gemini",model:n,usage:d.usageMetadata?{inputTokens:d.usageMetadata.promptTokenCount,outputTokens:d.usageMetadata.candidatesTokenCount,totalTokens:d.usageMetadata.totalTokenCount}:void 0}}async healthCheck(){let e=Date.now();try{return{ok:!0,provider:"gemini",model:(await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8})).model,message:"Provider reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:"gemini",model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}};var z=class{constructor(e){this.config=e;this.id=e.id}config;id;baseUrl(){let e=this.config.baseUrl?.trim();if(!e)throw new Error(`${this.id}: BASE URL is not configured`);return e}model(){let e=this.config.model?.trim();if(!e)throw new Error(`${this.id}: MODEL is not configured`);return e}headers(){let e={"content-type":"application/json",...this.config.headers};return this.config.apiKey&&(e.authorization=`Bearer ${this.config.apiKey}`),e}async generate(e){let t=this.model(),o=await v(G(this.baseUrl(),"chat/completions"),{method:"POST",headers:this.headers(),body:JSON.stringify({model:t,messages:e.messages,temperature:e.temperature,max_tokens:e.maxTokens,...this.id==="alibaba"?{enable_thinking:!1}:{}})}),n=o.choices?.[0]?.message?.content?.trim();if(!n)throw new Error(`${this.id}: empty model response`);return{text:n,provider:this.id,model:t,usage:o.usage?{inputTokens:o.usage.prompt_tokens,outputTokens:o.usage.completion_tokens,totalTokens:o.usage.total_tokens}:void 0}}async healthCheck(){let e=Date.now();try{let t=await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8});return{ok:!0,provider:this.id,model:t.model,message:"Provider reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:this.id,model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}};function ke(r=Se()){switch(b(r.id).transport){case"gemini":return new Y(r);case"cloudflare":return new V(r);default:return new z(r)}}var Mt=["memories/current.json","vectors/current.json","graph/current.json","graph/manifest.json","graph/resolution/current.json","code/chunks/current.json","code/vectors/current.json","code/architecture/current.json","code/architecture/entry-points.json","code/architecture/hotspots.json","code/architecture/layers.json","code/architecture/clusters.json","code/analysis/current.json","code/analysis/dead-code.json","code/analysis/dependencies.json","code/visualization/graph.json"],W=class{constructor(e){this.storage=e}storage;projectPrefix(e){return`projects/${e}`}snapshotPrefix(e,t){return[this.projectPrefix(e),"snapshots",t].join("/")}async create(e,t="automatic",o){let n=new Date().toISOString(),s=n.replace(/[:.]/g,"-"),a=this.projectPrefix(e),c=this.snapshotPrefix(e,s),d=[];for(let u of Mt){let l=`${a}/${u}`,f=await this.storage.get(l);f&&(await this.storage.put(`${c}/${u}`,f,"application/json"),d.push(u))}if(d.length===0)return null;let m={version:1,id:s,projectId:e,createdAt:n,reason:t,objects:d,metadata:o};return await this.storage.put(`${c}/snapshot.json`,JSON.stringify(m,null,2),"application/json"),m}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),n=(await this.storage.list(t)).filter(a=>a.key.endsWith("/snapshot.json")),s=[];for(let a of n){let c=await this.storage.getText(a.key);if(c)try{s.push(JSON.parse(c))}catch{}}return s.sort((a,c)=>c.createdAt.localeCompare(a.createdAt))}async restore(e,t){let o=this.snapshotPrefix(e,t),n=await this.storage.getText(`${o}/snapshot.json`);if(!n)throw new Error(`Snapshot not found: ${t}`);let s=JSON.parse(n);if(s.projectId!==e)throw new Error("Snapshot project mismatch");let a=this.projectPrefix(e),c=[];for(let d of s.objects){let m=await this.storage.get(`${o}/${d}`);m&&(await this.storage.put(`${a}/${d}`,m,"application/json"),c.push(d))}return{restored:c}}async remove(e,t){let o=this.snapshotPrefix(e,t),n=await this.storage.list(o);for(let s of n)await this.storage.delete(s.key);return n.length}async prune(e,t=10){let n=(await this.list(e)).slice(t),s=0;for(let a of n)await this.remove(e,a.id),s++;return s}};function oe(r,e){for(let t of r)process.env[t]||e.push(`Missing ${t}`)}function we(){let r=[],e=[],t=process.env.MEMORY_STORAGE_PROVIDER??"local";return t==="r2"?oe(["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"],r):t==="s3"?oe(["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"],r):t==="huggingface"?oe(["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"],r):t!=="local"&&r.push(`Unsupported MEMORY_STORAGE_PROVIDER: ${t}`),process.env.HF_TOKEN||e.push("HF_TOKEN missing: semantic embedding will use fallback"),process.env.HF_EMBEDDING_MODEL||e.push("HF_EMBEDDING_MODEL not set; default model will be used"),{ok:r.length===0,errors:r,warnings:e}}import{randomUUID as jt}from"node:crypto";var J=class{constructor(e,t){this.storage=e;this.embeddings=t}storage;embeddings;async run(){let e=`_health/production-${jt()}.txt`,t=`toolnet-memory:${Date.now()}`,o=!1;try{await this.storage.put(e,t,"text/plain"),o=await this.storage.getText(e)===t}finally{try{await this.storage.delete(e)}catch{}}let n=null;if(this.embeddings)try{let s=await this.embeddings.embed("ToolNet Memory production health check");n={ok:s.length>0,dimensions:s.length,mode:s.length===384?"huggingface":"fallback-or-custom"}}catch{n={ok:!1,dimensions:0,mode:"failed"}}return{ok:o&&(n===null||n.ok),storage:{ok:o,provider:this.storage.name},embedding:n}}};import{existsSync as Oe,readFileSync as Dt,readdirSync as Pe}from"node:fs";import{join as q}from"node:path";import{existsSync as Rt,mkdirSync as In,readFileSync as Tt,renameSync as On,writeFileSync as xn}from"node:fs";import{dirname as Mn,join as $t}from"node:path";function Lt(r){return $t(r.rootPath,".toolnet","work","current.json")}function _e(r){let e=Lt(r);if(!Rt(e))return null;try{let t=JSON.parse(Tt(e,"utf8"));return t.version!==1||t.projectId!==r.id?null:t}catch{return null}}function xe(r){if(!Oe(r))return null;try{return JSON.parse(Dt(r,"utf8"))}catch{return null}}function Nt(r){let e=q(r.rootPath,".toolnet","sessions");if(!Oe(e))return[];let t=[];for(let o of Pe(e,{withFileTypes:!0})){if(!o.isDirectory())continue;let n=q(e,o.name);for(let s of Pe(n,{withFileTypes:!0})){if(!s.isDirectory())continue;let a=xe(q(n,s.name,"state.json"));!a||a.version!==1||a.projectId!==r.id||t.push(a)}}return t}function Ie(r){return r.filter(e=>!!e).sort().at(-1)}function Kt(r){let e=xe(q(r.rootPath,".toolnet","runtime","opencode-status.json"));return!e||e.projectRoot&&e.projectRoot!==r.rootPath?null:e}function Ce(r){let e=Nt(r),o=[...e].sort((l,f)=>(l.lastLocalEventAt??l.updatedAt).localeCompare(f.lastLocalEventAt??f.updatedAt)).at(-1),n=Array.from(new Set(e.map(l=>l.agent))).sort(),s=e.reduce((l,f)=>l+Math.max(0,f.lastSequence-f.lastRemoteSequence),0),a=Kt(r),c=a?.state==="capture-failed",d=a?.state==="sync-failed",m;c?m="degraded":s>0||d?m="pending":e.length>0?m="healthy":m="unknown";let u=_e(r);return{ok:m!=="degraded",agents:n,sessions:e.length,latestAgent:o?.agent,latestSessionId:o?.nativeSessionId,lastCaptureAt:Ie(e.map(l=>l.lastLocalEventAt??l.updatedAt)),lastFlushAt:Ie(e.map(l=>l.lastRemoteAt)),pendingWal:s,currentTask:u?.currentTask?.title,currentFile:u?.activeFiles?.at(-1)??u?.filesTouched.at(-1),syncHealth:m,opencode:a?{state:a.state,reason:a.reason,timestamp:a.timestamp,error:a.error}:void 0}}function je(){return process.argv.includes("--json")}function Ut(r){let e=process.stdout.isTTY===!0&&process.env.TERM!=="dumb"&&process.env.NO_COLOR===void 0,t=(p,y)=>e?`\x1B[${p}m${y}\x1B[0m`:y,o=p=>t("38;5;82",p),n=p=>t("38;5;51",p),s=p=>t("38;5;214",p),a=p=>t("38;5;196",p),c=p=>t("38;5;255",p),d=p=>t("2",p),m=p=>t("1",p),u=d("\u2502"),l=d("\u251C");if(console.log(""),console.log(`${n("\u25C7")} ${m(c("ToolNet Memory Doctor"))}`),console.log(""),r.project&&console.log(`${o("\u25C6")} Project ${c(r.project)}`),console.log(u),r.storage){let p=typeof r.storage.provider=="string"?` \u2014 ${r.storage.provider}`:"";r.storage.ok?console.log(`${l} ${o("\u25C6")} Storage${p}`):console.log(`${l} ${a("\u2717")} Storage${p}`)}if(r.llm){let p=[];if(r.llm.provider&&p.push(r.llm.provider),r.llm.model&&p.push(r.llm.model),typeof r.llm.latencyMs=="number"&&p.push(`${r.llm.latencyMs}ms`),r.llm.ok)console.log(`${l} ${o("\u25C6")} LLM${p.length?` \u2014 ${p.join(" \xB7 ")}`:""}`);else{let y=r.llm.error?` \u2014 ${r.llm.error}`:"";console.log(`${l} ${a("\u2717")} LLM${y}`)}}if(r.embedding){let p=[];typeof r.embedding.mode=="string"&&p.push(r.embedding.mode),typeof r.embedding.dimensions=="number"&&p.push(`${r.embedding.dimensions}d`),r.embedding.ok?console.log(`${l} ${o("\u25C6")} Embedding${p.length?` \u2014 ${p.join(" \xB7 ")}`:""}`):console.log(`${l} ${a("\u2717")} Embedding`)}if((r.memory!==void 0||r.graphSymbols!==void 0||r.codeChunks!==void 0)&&(console.log(u),console.log(`${n("\u25C7")} ${c("Project data")}`),r.memory!==void 0&&console.log(`${l} ${d("Memories")} ${c(String(r.memory))}`),r.graphSymbols!==void 0&&console.log(`${l} ${d("Graph symbols")} ${c(String(r.graphSymbols))}`),r.graphEdges!==void 0&&console.log(`${l} ${d("Graph edges")} ${c(String(r.graphEdges))}`),r.memoryVectors!==void 0&&console.log(`${l} ${d("Memory vectors")} ${c(String(r.memoryVectors))}`),r.codeChunks!==void 0&&console.log(`${l} ${d("Code chunks")} ${c(String(r.codeChunks))}`),r.codeVectors!==void 0&&console.log(`${l} ${d("Code vectors")} ${c(String(r.codeVectors))}`),r.snapshots!==void 0&&console.log(`${l} ${d("Snapshots")} ${c(String(r.snapshots))}`)),r.capture){console.log(u),console.log(`${n("\u25C7")} ${c("Session continuity")}`),console.log(`${l} ${d("Agents")} ${c(r.capture.agents.length?r.capture.agents.join(", "):"none")}`),r.capture.latestAgent&&console.log(`${l} ${d("Latest agent")} ${c(String(r.capture.latestAgent))}`),r.capture.currentTask&&console.log(`${l} ${d("Current task")} ${c(r.capture.currentTask)}`),r.capture.currentFile&&console.log(`${l} ${d("Current file")} ${c(r.capture.currentFile)}`),r.capture.lastCaptureAt&&console.log(`${l} ${d("Last capture")} ${c(r.capture.lastCaptureAt)}`),r.capture.lastFlushAt&&console.log(`${l} ${d("Last flush")} ${c(r.capture.lastFlushAt)}`),console.log(`${l} ${d("Pending WAL")} ${c(String(r.capture.pendingWal))}`);let p=r.capture.syncHealth==="healthy"?o(r.capture.syncHealth):r.capture.syncHealth==="degraded"?a(r.capture.syncHealth):s(r.capture.syncHealth);console.log(`${l} ${d("Sync health")} ${p}`)}let f=r.config?.errors??[];if(f.length>0){console.log(u),console.log(`${a("\u2717")} ${c("Configuration required")}`);for(let p of f)console.log(`${l} ${a("\u2022")} ${p}`)}let Q=[...r.config?.warnings??[],...r.warnings??[]],I=[...new Set(Q)];if(I.length>0){console.log(u),console.log(`${s("\u25C7")} ${c("Warnings")}`);for(let p of I)console.log(`${l} ${s("!")} ${p}`)}console.log(u),r.ok?console.log(`${o("\u2514 \u25C6")} ${m(c("ToolNet Memory is ready"))}`):(console.log(`${a("\u2514 \u2717")} ${m(c("ToolNet Memory requires attention"))}`),f.length>0&&console.log(d(" Run: toolnet-memory setup"))),console.log("")}function Me(r){je()?console.log(JSON.stringify(r,null,2)):Ut(r)}async function Bt(){let r=we();if(!r.ok){let h={ok:!1,config:r,warnings:r.warnings};Me(h),process.exitCode=1;return}let e=se(),t=new x().detect(),o=fe(me({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),n=new D(o,t.id,t.name,t.remote??t.name),s=Ee(),a=P(),c;try{let A=await ke({id:a.llm.provider,apiKey:a.llm.apiKey,baseUrl:a.llm.baseUrl,model:a.llm.model,accountId:a.llm.accountId}).healthCheck();c={ok:A.ok,provider:a.llm.provider,model:A.model??a.llm.model,latencyMs:A.latencyMs,error:A.ok?void 0:A.message}}catch(h){c={ok:!1,provider:a.llm.provider,model:a.llm.model,error:h instanceof Error?h.message:String(h)}}let d=await new J(o,s).run(),m=await new j(n).load(t.id),u=await new T(n).load(t.id),l=await new R(n).load(t.id),f=await new $(n).load(t.id),Q=await new L(n).load(t.id),I=await new W(n).list(t.id),p=Ce(t),y=p.syncHealth==="degraded"?[`Session capture degraded${p.opencode?.error?`: ${p.opencode.error}`:""}`]:[],ne={ok:d.ok&&!!c?.ok&&p.ok,project:t.name,storage:d.storage,llm:c,embedding:d.embedding,memory:m.length,graphSymbols:u?.symbols.length??0,graphEdges:u?.edges.length??0,memoryVectors:l?.records.length??0,codeChunks:f?.chunks.length??0,codeVectors:Q?.records.length??0,snapshots:I.length,capture:p,warnings:[...r.warnings,...y]};Me(ne),process.exitCode=ne.ok?0:1}Bt().catch(r=>{let e=r instanceof Error?r.message:String(r);je()?console.log(JSON.stringify({ok:!1,error:e},null,2)):(console.error(""),console.error("ToolNet Memory Doctor"),console.error("====================="),console.error(""),console.error(`\u2717 ${e}`),console.error("")),process.exitCode=1});
1
+ import"dotenv/config";import{existsSync as ae,readFileSync as ce}from"node:fs";import{homedir as pe}from"node:os";import{join as de}from"node:path";function ue(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function le(){let r=process.env.TOOLNET_GLOBAL_ENV??de(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 n=o.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=ue(o.slice(s+1)))}}le();function f(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function y(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function F(){return{memory:{autoCapture:f(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:f(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:f(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:f(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:y(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:y(process.env.MEMORY_RERANK_TOP,10),finalContext:y(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:y(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:f(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:y(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as ge}from"node:crypto";import{existsSync as O,mkdirSync as me,readFileSync as fe,renameSync as ye,writeFileSync as he}from"node:fs";import{basename as Se,dirname as w,join as j,parse as V,resolve as h}from"node:path";var W=".toolnet",ke="project.json";function we(r){return ge("sha256").update(r).digest("hex").slice(0,16)}function _(r){return j(r,W,ke)}function ve(r){return O(_(r))}function je(r,e){let t=h(r),o=V(t).root;for(;;){if(ve(t))return t;if(t===o||e&&t===h(e))break;let s=w(t);if(s===t)break;t=s}return null}function be(r){let e=h(r),t=V(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(n=>O(j(e,n))))return e;if(e===t)break;let s=w(e);if(s===e)break;e=s}return h(r)}function xe(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:w(w(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=j(r,W);me(t,{recursive:!0});let o=_(r),s=`${o}.tmp-${process.pid}`;he(s,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),ye(s,o)}function z(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 v=class{detect(e=process.cwd()){let t=h(e),o=be(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(l=>O(j(o,l))),i=je(t,n?o:void 0);if(i){let l=_(i),c=xe(l);return c.rootPath!==i&&(c.rootPath=i,c.updatedAt=new Date().toISOString(),L(i,c)),z(c,i)}let a=new Date().toISOString(),p=Se(o),d={version:1,id:we(o),name:p,remote:p,rootPath:o,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return L(o,d),z(d,o)}};import{homedir as Je}from"node:os";import{join as Ye}from"node:path";import{DeleteObjectCommand as Ae,GetObjectCommand as Ce,HeadObjectCommand as Pe,ListObjectsV2Command as Re,PutObjectCommand as Me,S3Client as $e}from"@aws-sdk/client-s3";import{getSignedUrl as Ee}from"@aws-sdk/s3-request-presigner";var b=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new $e({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 Me({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Ee(this.client,new Ce({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 Pe({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Ae({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new Re({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};import{access as B,mkdir as Te,readFile as Ie,readdir as Oe,rm as _e,stat as H,writeFile as Ke}from"node:fs/promises";import{dirname as Ne,join as De,relative as U,resolve as Fe}from"node:path";var S=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Fe(this.root,t)}async put(e,t){let o=this.path(e);await Te(Ne(o),{recursive:!0}),await Ke(o,t)}async get(e){try{return await Ie(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 B(this.path(e)),!0}catch{return!1}}async delete(e){await _e(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await B(t)}catch{return o}let s=async i=>{let a=await Oe(i,{withFileTypes:!0});for(let p of a){let d=De(i,p.name);if(p.isDirectory()){await s(d);continue}let l=await H(d);o.push({key:U(this.root,d),size:l.size,updatedAt:l.mtime.toISOString()})}},n=await H(t);return n.isDirectory()?await s(t):o.push({key:U(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),o}};import{DeleteObjectCommand as Le,GetObjectCommand as ze,HeadObjectCommand as Ve,ListObjectsV2Command as We,PutObjectCommand as Be,S3Client as He}from"@aws-sdk/client-s3";import{getSignedUrl as Ue}from"@aws-sdk/s3-request-presigner";var k=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new He({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 Be({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Ue(this.client,new ze({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 Le({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 n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};function K(r,e){return console.warn(e),new S(r)}function J(r){let e=r.localRoot??Ye(Je(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new k({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(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new k({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(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new b({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):K(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new S(e)}var x=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"memories","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return[];let o=JSON.parse(t);if(!Array.isArray(o))throw new Error("Invalid memory snapshot");return o}async save(e,t){let o=JSON.stringify(t,null,2);await this.storage.put(this.key(e),o,"application/json")}};var A=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return null;let o=JSON.parse(t);if(o.version!==1||o.projectId!==e||!Array.isArray(o.records))throw new Error("Invalid vector snapshot");return o}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var C=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 P=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 R=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 Ge(r){return new Promise(e=>setTimeout(e,r))}async function Y(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,s=e.maxDelayMs??2e3,n;for(let i=1;i<=t;i++)try{return await r()}catch(a){if(n=a,i>=t)break;let p=Math.min(s,o*2**(i-1)),d=Math.floor(Math.random()*Math.max(1,p*.2));await Ge(p+d)}throw n}var Qe=new Set(["put","get","getText","delete","list"]);function G(r,e={}){return new Proxy(r,{get(t,o){let s=Reflect.get(t,o,t);return typeof s!="function"?s:Qe.has(o)?(...n)=>Y(()=>Promise.resolve(s.apply(t,n)),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 q(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 M=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 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"&&(o=i.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:o,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=q(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))}};var qe=["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"],$=class{constructor(e){this.storage=e}storage;projectPrefix(e){return`projects/${e}`}snapshotPrefix(e,t){return[this.projectPrefix(e),"snapshots",t].join("/")}async create(e,t="automatic",o){let s=new Date().toISOString(),n=s.replace(/[:.]/g,"-"),i=this.projectPrefix(e),a=this.snapshotPrefix(e,n),p=[];for(let l of qe){let c=`${i}/${l}`,g=await this.storage.get(c);g&&(await this.storage.put(`${a}/${l}`,g,"application/json"),p.push(l))}if(p.length===0)return null;let d={version:1,id:n,projectId:e,createdAt:s,reason:t,objects:p,metadata:o};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(d,null,2),"application/json"),d}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),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 o=this.snapshotPrefix(e,t),s=await this.storage.getText(`${o}/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 p of n.objects){let d=await this.storage.get(`${o}/${p}`);d&&(await this.storage.put(`${i}/${p}`,d,"application/json"),a.push(p))}return{restored:a}}async remove(e,t){let o=this.snapshotPrefix(e,t),s=await this.storage.list(o);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}};function N(r,e){for(let t of r)process.env[t]||e.push(`Missing ${t}`)}function Z(){let r=[],e=[],t=process.env.MEMORY_STORAGE_PROVIDER??"local";return t==="r2"?N(["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"],r):t==="s3"?N(["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"],r):t==="huggingface"?N(["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"],r):t!=="local"&&r.push(`Unsupported MEMORY_STORAGE_PROVIDER: ${t}`),process.env.HF_TOKEN||e.push("HF_TOKEN missing: Hugging Face S3 storage credentials incomplete"),{ok:r.length===0,errors:r,warnings:e}}import{randomUUID as Ze}from"node:crypto";var E=class{constructor(e){this.storage=e}storage;async run(){let e=`_health/production-${Ze()}.txt`,t=`toolnet-memory:${Date.now()}`,o=!1;try{await this.storage.put(e,t,"text/plain"),o=await this.storage.getText(e)===t}finally{try{await this.storage.delete(e)}catch{}}return{ok:o,storage:{ok:o,provider:this.storage.name},embedding:null}}};import{existsSync as re,readFileSync as ot,readdirSync as ee}from"node:fs";import{join as T}from"node:path";import{existsSync as Xe,mkdirSync as lo,readFileSync as et,renameSync as go,writeFileSync as mo}from"node:fs";import{dirname as yo,join as tt}from"node:path";function rt(r){return tt(r.rootPath,".toolnet","work","current.json")}function X(r){let e=rt(r);if(!Xe(e))return null;try{let t=JSON.parse(et(e,"utf8"));return t.version!==1||t.projectId!==r.id?null:t}catch{return null}}function oe(r){if(!re(r))return null;try{return JSON.parse(ot(r,"utf8"))}catch{return null}}function st(r){let e=T(r.rootPath,".toolnet","sessions");if(!re(e))return[];let t=[];for(let o of ee(e,{withFileTypes:!0})){if(!o.isDirectory())continue;let s=T(e,o.name);for(let n of ee(s,{withFileTypes:!0})){if(!n.isDirectory())continue;let i=oe(T(s,n.name,"state.json"));!i||i.version!==1||i.projectId!==r.id||t.push(i)}}return t}function te(r){return r.filter(e=>!!e).sort().at(-1)}function nt(r){let e=oe(T(r.rootPath,".toolnet","runtime","opencode-status.json"));return!e||e.projectRoot&&e.projectRoot!==r.rootPath?null:e}function se(r){let e=st(r),o=[...e].sort((c,g)=>(c.lastLocalEventAt??c.updatedAt).localeCompare(g.lastLocalEventAt??g.updatedAt)).at(-1),s=Array.from(new Set(e.map(c=>c.agent))).sort(),n=e.reduce((c,g)=>c+Math.max(0,g.lastSequence-g.lastRemoteSequence),0),i=nt(r),a=i?.state==="capture-failed",p=i?.state==="sync-failed",d;a?d="degraded":n>0||p?d="pending":e.length>0?d="healthy":d="unknown";let l=X(r);return{ok:d!=="degraded",agents:s,sessions:e.length,latestAgent:o?.agent,latestSessionId:o?.nativeSessionId,lastCaptureAt:te(e.map(c=>c.lastLocalEventAt??c.updatedAt)),lastFlushAt:te(e.map(c=>c.lastRemoteAt)),pendingWal:n,currentTask:l?.currentTask?.title,currentFile:l?.activeFiles?.at(-1)??l?.filesTouched.at(-1),syncHealth:d,opencode:i?{state:i.state,reason:i.reason,timestamp:i.timestamp,error:i.error}:void 0}}function ie(){return process.argv.includes("--json")}function it(r){let e=process.stdout.isTTY===!0&&process.env.TERM!=="dumb"&&process.env.NO_COLOR===void 0,t=(u,D)=>e?`\x1B[${u}m${D}\x1B[0m`:D,o=u=>t("38;5;82",u),s=u=>t("38;5;51",u),n=u=>t("38;5;214",u),i=u=>t("38;5;196",u),a=u=>t("38;5;255",u),p=u=>t("2",u),d=u=>t("1",u),l=p("\u2502"),c=p("\u251C");if(console.log(""),console.log(`${s("\u25C7")} ${d(a("ToolNet Memory Doctor"))}`),console.log(""),r.project&&console.log(`${o("\u25C6")} Project ${a(r.project)}`),console.log(l),r.storage){let u=typeof r.storage.provider=="string"?` \u2014 ${r.storage.provider}`:"";r.storage.ok?console.log(`${c} ${o("\u25C6")} Storage${u}`):console.log(`${c} ${i("\u2717")} Storage${u}`)}if((r.memory!==void 0||r.graphSymbols!==void 0||r.codeChunks!==void 0)&&(console.log(l),console.log(`${s("\u25C7")} ${a("Project data")}`),r.memory!==void 0&&console.log(`${c} ${p("Memories")} ${a(String(r.memory))}`),r.graphSymbols!==void 0&&console.log(`${c} ${p("Graph symbols")} ${a(String(r.graphSymbols))}`),r.graphEdges!==void 0&&console.log(`${c} ${p("Graph edges")} ${a(String(r.graphEdges))}`),r.memoryVectors!==void 0&&console.log(`${c} ${p("Memory vectors")} ${a(String(r.memoryVectors))}`),r.codeChunks!==void 0&&console.log(`${c} ${p("Code chunks")} ${a(String(r.codeChunks))}`),r.codeVectors!==void 0&&console.log(`${c} ${p("Code vectors")} ${a(String(r.codeVectors))}`),r.snapshots!==void 0&&console.log(`${c} ${p("Snapshots")} ${a(String(r.snapshots))}`)),r.capture){console.log(l),console.log(`${s("\u25C7")} ${a("Session continuity")}`),console.log(`${c} ${p("Agents")} ${a(r.capture.agents.length?r.capture.agents.join(", "):"none")}`),r.capture.latestAgent&&console.log(`${c} ${p("Latest agent")} ${a(String(r.capture.latestAgent))}`),r.capture.currentTask&&console.log(`${c} ${p("Current task")} ${a(r.capture.currentTask)}`),r.capture.currentFile&&console.log(`${c} ${p("Current file")} ${a(r.capture.currentFile)}`),r.capture.lastCaptureAt&&console.log(`${c} ${p("Last capture")} ${a(r.capture.lastCaptureAt)}`),r.capture.lastFlushAt&&console.log(`${c} ${p("Last flush")} ${a(r.capture.lastFlushAt)}`),console.log(`${c} ${p("Pending WAL")} ${a(String(r.capture.pendingWal))}`);let u=r.capture.syncHealth==="healthy"?o(r.capture.syncHealth):r.capture.syncHealth==="degraded"?i(r.capture.syncHealth):n(r.capture.syncHealth);console.log(`${c} ${p("Sync health")} ${u}`)}let g=r.config?.errors??[];if(g.length>0){console.log(l),console.log(`${i("\u2717")} ${a("Configuration required")}`);for(let u of g)console.log(`${c} ${i("\u2022")} ${u}`)}let I=[...r.config?.warnings??[],...r.warnings??[]],m=[...new Set(I)];if(m.length>0){console.log(l),console.log(`${n("\u25C7")} ${a("Warnings")}`);for(let u of m)console.log(`${c} ${n("!")} ${u}`)}console.log(l),r.ok?console.log(`${o("\u2514 \u25C6")} ${d(a("ToolNet Memory is ready"))}`):(console.log(`${i("\u2514 \u2717")} ${d(a("ToolNet Memory requires attention"))}`),g.length>0&&console.log(p(" Run: toolnet-memory setup"))),console.log("")}function ne(r){ie()?console.log(JSON.stringify(r,null,2)):it(r)}async function at(){let r=Z();if(!r.ok){let u={ok:!1,config:r,warnings:r.warnings};ne(u),process.exitCode=1;return}let e=F(),t=new v().detect(),o=G(J({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),s=new M(o,t.id,t.name,t.remote??t.name),n=await new E(o).run(),i=await new x(s).load(t.id),a=await new C(s).load(t.id),p=await new A(s).load(t.id),d=await new P(s).load(t.id),l=await new R(s).load(t.id),c=await new $(s).list(t.id),g=se(t),I=g.syncHealth==="degraded"?[`Session capture degraded${g.opencode?.error?`: ${g.opencode.error}`:""}`]:[],m={ok:n.ok&&g.ok,project:t.name,storage:n.storage,memory:i.length,graphSymbols:a?.symbols.length??0,graphEdges:a?.edges.length??0,memoryVectors:p?.records.length??0,codeChunks:d?.chunks.length??0,codeVectors:l?.records.length??0,snapshots:c.length,capture:g,warnings:[...r.warnings,...I]};ne(m),process.exitCode=m.ok?0:1}at().catch(r=>{let e=r instanceof Error?r.message:String(r);ie()?console.log(JSON.stringify({ok:!1,error:e},null,2)):(console.error(""),console.error("ToolNet Memory Doctor"),console.error("====================="),console.error(""),console.error(`\u2717 ${e}`),console.error("")),process.exitCode=1});