toolnet-memory 0.2.14 → 0.2.16
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/README.md +74 -0
- package/bin/toolnet-memory +18 -0
- package/bundle/doctor.js +5 -3
- package/bundle/full-index.js +8 -8
- package/bundle/mcp.js +7 -7
- package/bundle/provider.js +3 -0
- package/bundle/runtime.js +11 -11
- package/bundle/semantic.js +6 -6
- package/bundle/setup.js +22 -69
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -546,3 +546,77 @@ See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
|
546
546
|
## License
|
|
547
547
|
|
|
548
548
|
MIT © 2026 LBT-AI. See [LICENSE](LICENSE).
|
|
549
|
+
|
|
550
|
+
## Multi-provider AI setup
|
|
551
|
+
|
|
552
|
+
Run:
|
|
553
|
+
|
|
554
|
+
```bash
|
|
555
|
+
toolnet-memory setup
|
|
556
|
+
|
|
557
|
+
ToolNet Memory separates AI configuration into independent roles:
|
|
558
|
+
|
|
559
|
+
* LLM — reasoning, summarization, classification, and memory intelligence.
|
|
560
|
+
* Embedding — semantic indexing and retrieval.
|
|
561
|
+
* LLM Fallbacks — optional secondary providers for transient failures.
|
|
562
|
+
|
|
563
|
+
Supported LLM providers include:
|
|
564
|
+
|
|
565
|
+
* OpenAI-compatible
|
|
566
|
+
* Alibaba / DashScope
|
|
567
|
+
* OpenRouter
|
|
568
|
+
* Groq
|
|
569
|
+
* DeepSeek
|
|
570
|
+
* NVIDIA NIM
|
|
571
|
+
* Gemini
|
|
572
|
+
* Hugging Face
|
|
573
|
+
* Ollama / Local
|
|
574
|
+
* Cloudflare Workers AI
|
|
575
|
+
* Custom endpoints
|
|
576
|
+
|
|
577
|
+
Canonical configuration:
|
|
578
|
+
|
|
579
|
+
TOOLNET_LLM_PROVIDER=
|
|
580
|
+
TOOLNET_LLM_API_KEY=
|
|
581
|
+
TOOLNET_LLM_BASE_URL=
|
|
582
|
+
TOOLNET_LLM_MODEL=
|
|
583
|
+
TOOLNET_EMBEDDING_PROVIDER=
|
|
584
|
+
TOOLNET_EMBEDDING_API_KEY=
|
|
585
|
+
TOOLNET_EMBEDDING_BASE_URL=
|
|
586
|
+
TOOLNET_EMBEDDING_MODEL=
|
|
587
|
+
|
|
588
|
+
Optional resilient LLM chain:
|
|
589
|
+
|
|
590
|
+
TOOLNET_LLM_FALLBACK_1_PROVIDER=
|
|
591
|
+
TOOLNET_LLM_FALLBACK_1_API_KEY=
|
|
592
|
+
TOOLNET_LLM_FALLBACK_1_BASE_URL=
|
|
593
|
+
TOOLNET_LLM_FALLBACK_1_MODEL=
|
|
594
|
+
TOOLNET_LLM_FALLBACK_2_PROVIDER=
|
|
595
|
+
TOOLNET_LLM_FALLBACK_2_API_KEY=
|
|
596
|
+
TOOLNET_LLM_FALLBACK_2_BASE_URL=
|
|
597
|
+
TOOLNET_LLM_FALLBACK_2_MODEL=
|
|
598
|
+
TOOLNET_LLM_FALLBACK_COOLDOWN_MS=60000
|
|
599
|
+
TOOLNET_LLM_MAX_RETRIES=1
|
|
600
|
+
|
|
601
|
+
Fallback is used only for transient failures such as:
|
|
602
|
+
|
|
603
|
+
* timeout / network failure
|
|
604
|
+
* HTTP 408
|
|
605
|
+
* HTTP 429
|
|
606
|
+
* HTTP 5xx
|
|
607
|
+
|
|
608
|
+
HTTP 400, 401, and 403 are surfaced instead of silently switching providers.
|
|
609
|
+
|
|
610
|
+
Legacy provider environment variables remain readable for backward compatibility. The setup wizard can migrate recognized legacy values into canonical TOOLNET_* configuration without deleting the original variables.
|
|
611
|
+
|
|
612
|
+
Diagnostics:
|
|
613
|
+
|
|
614
|
+
toolnet-memory provider:list
|
|
615
|
+
toolnet-memory provider:status
|
|
616
|
+
toolnet-memory provider:test
|
|
617
|
+
toolnet-memory provider:test llm
|
|
618
|
+
toolnet-memory provider:test embedding
|
|
619
|
+
toolnet-memory doctor
|
|
620
|
+
|
|
621
|
+
Provider status masks API keys and secrets.
|
|
622
|
+
```
|
package/bin/toolnet-memory
CHANGED
|
@@ -68,6 +68,21 @@ case "$COMMAND" in
|
|
|
68
68
|
run bundle/doctor.js src/production/doctor.ts "$@"
|
|
69
69
|
;;
|
|
70
70
|
|
|
71
|
+
provider:list)
|
|
72
|
+
shift
|
|
73
|
+
run bundle/provider.js src/ai/provider-cli.ts list "$@"
|
|
74
|
+
;;
|
|
75
|
+
|
|
76
|
+
provider:status)
|
|
77
|
+
shift
|
|
78
|
+
run bundle/provider.js src/ai/provider-cli.ts status "$@"
|
|
79
|
+
;;
|
|
80
|
+
|
|
81
|
+
provider:test)
|
|
82
|
+
shift
|
|
83
|
+
run bundle/provider.js src/ai/provider-cli.ts test "$@"
|
|
84
|
+
;;
|
|
85
|
+
|
|
71
86
|
run)
|
|
72
87
|
run bundle/runtime.js src/index.ts
|
|
73
88
|
;;
|
|
@@ -521,6 +536,9 @@ CODE INTELLIGENCE:
|
|
|
521
536
|
SETUP & INTEGRATION:
|
|
522
537
|
toolnet-memory --version
|
|
523
538
|
toolnet-memory doctor
|
|
539
|
+
toolnet-memory provider:list
|
|
540
|
+
toolnet-memory provider:status
|
|
541
|
+
toolnet-memory provider:test [llm|embedding]
|
|
524
542
|
toolnet-memory setup
|
|
525
543
|
toolnet-memory update
|
|
526
544
|
toolnet-memory config get KEY
|
package/bundle/doctor.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
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}),ae(s,o)}function D(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var b=class{detect(e=process.cwd()){let t=f(e),o=ue(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>_(v(o,d))),i=le(t,n?o:void 0);if(i){let d=T(i),m=fe(d);return m.rootPath!==i&&(m.rootPath=i,m.updatedAt=new Date().toISOString(),N(i,m)),D(m,i)}let a=new Date().toISOString(),c=pe(o),p={version:1,id:me(o),name:c,remote:c,rootPath:o,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return N(o,p),D(p,o)}};import{homedir as De}from"node:os";import{join as Ve}from"node:path";import{DeleteObjectCommand as ye,GetObjectCommand as he,HeadObjectCommand as Se,ListObjectsV2Command as be,PutObjectCommand as ve,S3Client as we}from"@aws-sdk/client-s3";import{getSignedUrl as je}from"@aws-sdk/s3-request-presigner";var w=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new we({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,o="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ve({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await je(this.client,new he({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 Se({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 ye({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new be({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 z,mkdir as xe,readFile as ke,readdir as Pe,rm as Ee,stat as L,writeFile as Ae}from"node:fs/promises";import{dirname as Ce,join as Re,relative as U,resolve as Me}from"node:path";var y=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Me(this.root,t)}async put(e,t){let o=this.path(e);await xe(Ce(o),{recursive:!0}),await Ae(o,t)}async get(e){try{return await ke(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await z(this.path(e)),!0}catch{return!1}}async delete(e){await Ee(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await z(t)}catch{return o}let s=async i=>{let a=await Pe(i,{withFileTypes:!0});for(let c of a){let p=Re(i,c.name);if(c.isDirectory()){await s(p);continue}let d=await L(p);o.push({key:U(this.root,p),size:d.size,updatedAt:d.mtime.toISOString()})}},n=await L(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 _e,GetObjectCommand as Te,HeadObjectCommand as Oe,ListObjectsV2Command as Ie,PutObjectCommand as $e,S3Client as Ke}from"@aws-sdk/client-s3";import{getSignedUrl as Ne}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,o="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new $e({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Ne(this.client,new Te({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 Oe({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 _e({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new Ie({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 O(r,e){return console.warn(e),new y(r)}function F(r){let e=r.localRoot??Ve(De(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.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}):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 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}):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 w({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new y(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 x=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 k=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 E=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 Be(r){return new Promise(e=>setTimeout(e,r))}async function H(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 c=Math.min(s,o*2**(i-1)),p=Math.floor(Math.random()*Math.max(1,c*.2));await Be(c+p)}throw n}var ze=new Set(["put","get","getText","delete","list"]);function J(r,e={}){return new Proxy(r,{get(t,o){let s=Reflect.get(t,o,t);return typeof s!="function"?s:ze.has(o)?(...n)=>H(()=>Promise.resolve(s.apply(t,n)),e):s.bind(t)}})}var A=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 s=await o.json();if(!Array.isArray(s))throw new Error("HF embedding returned invalid response");return s.map(n=>Le(n))}};function Le(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 s of e)for(let n=0;n<t;n++)o[n]+=s[n]??0;for(let s=0;s<t;s++)o[s]/=e.length;return o}throw new Error("Unsupported embedding shape")}import{createHash as Ue}from"node:crypto";var C=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 s of o){let i=Ue("sha256").update(s).digest().readUInt32BE(0)%this.dimensions;t[i]+=1}return Fe(t)}async embedMany(e){return Promise.all(e.map(t=>this.embed(t)))}};function Fe(r){let e=Math.sqrt(r.reduce((t,o)=>t+o*o,0));return e===0?r:r.map(t=>t/e)}var I=class{constructor(e,t=new C){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 Y(){let r=process.env.HF_TOKEN,e=process.env.HF_EMBEDDING_MODEL??"sentence-transformers/all-MiniLM-L6-v2",t=r?new A({token:r,model:e}):null;return new I(t)}var He=["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"],R=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),c=[];for(let d of He){let m=`${i}/${d}`,g=await this.storage.get(m);g&&(await this.storage.put(`${a}/${d}`,g,"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:o};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 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 c of n.objects){let p=await this.storage.get(`${o}/${c}`);p&&(await this.storage.put(`${i}/${c}`,p,"application/json"),a.push(c))}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 $(r,e){for(let t of r)process.env[t]||e.push(`Missing ${t}`)}function G(){let r=[],e=[],t=process.env.MEMORY_STORAGE_PROVIDER??"local";return t==="r2"?$(["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"],r):t==="s3"?$(["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"],r):t==="huggingface"?$(["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 Je}from"node:crypto";var M=class{constructor(e,t){this.storage=e;this.embeddings=t}storage;embeddings;async run(){let e=`_health/production-${Je()}.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 s=null;if(this.embeddings)try{let n=await this.embeddings.embed("ToolNet Memory production health check");s={ok:n.length>0,dimensions:n.length,mode:n.length===384?"huggingface":"fallback-or-custom"}}catch{s={ok:!1,dimensions:0,mode:"failed"}}return{ok:o&&(s===null||s.ok),storage:{ok:o,provider:this.storage.name},embedding:s}}};function Z(){return process.argv.includes("--json")}function Ye(r){if(console.log(""),console.log("ToolNet Memory Doctor"),console.log("====================="),console.log(""),r.project&&(console.log(`Project: ${r.project}`),console.log("")),r.storage)if(r.storage.ok){let s=typeof r.storage.provider=="string"?` (${r.storage.provider})`:"";console.log(`\u2713 Storage${s}`)}else console.log("\u2717 Storage");if(r.embedding)if(r.embedding.ok){let s=[];typeof r.embedding.mode=="string"&&s.push(r.embedding.mode),typeof r.embedding.dimensions=="number"&&s.push(`${r.embedding.dimensions} dimensions`),console.log(`\u2713 Embedding${s.length?` (${s.join(", ")})`:""}`)}else console.log("\u2717 Embedding");(r.memory!==void 0||r.graphSymbols!==void 0||r.codeChunks!==void 0)&&(console.log(""),console.log("Project data:"),r.memory!==void 0&&console.log(` Memories ${r.memory}`),r.graphSymbols!==void 0&&console.log(` Graph symbols ${r.graphSymbols}`),r.graphEdges!==void 0&&console.log(` Graph edges ${r.graphEdges}`),r.memoryVectors!==void 0&&console.log(` Memory vectors ${r.memoryVectors}`),r.codeChunks!==void 0&&console.log(` Code chunks ${r.codeChunks}`),r.codeVectors!==void 0&&console.log(` Code vectors ${r.codeVectors}`),r.snapshots!==void 0&&console.log(` Snapshots ${r.snapshots}`));let e=r.config?.errors??[];if(e.length>0){console.log(""),console.log("Configuration required:");for(let s of e)console.log(` - ${s}`)}let t=[...r.config?.warnings??[],...r.warnings??[]],o=[...new Set(t)];if(o.length>0){console.log(""),console.log("Warnings:");for(let s of o)console.log(` - ${s}`)}console.log(""),r.ok?console.log("\u2713 ToolNet Memory is ready"):(console.log("\u2717 ToolNet Memory requires attention"),e.length>0&&(console.log(""),console.log("Run:"),console.log(" toolnet-memory setup"))),console.log("")}function W(r){Z()?console.log(JSON.stringify(r,null,2)):Ye(r)}async function Ge(){let r=G();if(!r.ok){let Q={ok:!1,config:r,warnings:r.warnings};W(Q),process.exitCode=1;return}let e=K(),t=new b().detect(),o=J(F({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),s=Y(),n=await new M(o,s).run(),i=await new j(o).load(t.id),a=await new k(o).load(t.id),c=await new x(o).load(t.id),p=await new P(o).load(t.id),d=await new E(o).load(t.id),m=await new R(o).list(t.id),g={ok:n.ok,project:t.name,storage:n.storage,embedding:n.embedding,memory:i.length,graphSymbols:a?.symbols.length??0,graphEdges:a?.edges.length??0,memoryVectors:c?.records.length??0,codeChunks:p?.chunks.length??0,codeVectors:d?.records.length??0,snapshots:m.length,warnings:r.warnings};W(g),process.exitCode=g.ok?0:1}Ge().catch(r=>{let e=r instanceof Error?r.message:String(r);Z()?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 ve,readFileSync as Ae}from"node:fs";import{homedir as Se}from"node:os";import{join as we}from"node:path";function _e(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 Pe(){let r=process.env.TOOLNET_GLOBAL_ENV??we(Se(),".config","toolnet-memory",".env");if(!ve(r))return;let e=Ae(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 i=o.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=_e(o.slice(n+1)))}}Pe();function b(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 X(){return{memory:{autoCapture:b(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:b(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:b(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:b(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates: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:b(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:E(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Oe}from"node:crypto";import{existsSync as Y,mkdirSync as Ie,readFileSync as ke,renameSync as je,writeFileSync as xe}from"node:fs";import{basename as Re,dirname as P,join as I,parse as re,resolve as v}from"node:path";var oe=".toolnet",Ce="project.json";function Me(r){return Oe("sha256").update(r).digest("hex").slice(0,16)}function z(r){return I(r,oe,Ce)}function Te(r){return Y(z(r))}function Le(r,e){let t=v(r),o=re(t).root;for(;;){if(Te(t))return t;if(t===o||e&&t===v(e))break;let n=P(t);if(n===t)break;t=n}return null}function De(r){let e=v(r),t=re(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(i=>Y(I(e,i))))return e;if(e===t)break;let n=P(e);if(n===e)break;e=n}return v(r)}function Ke(r){let e;try{e=JSON.parse(ke(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:P(P(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 ee(r,e){let t=I(r,oe);Ie(t,{recursive:!0});let o=z(r),n=`${o}.tmp-${process.pid}`;xe(n,JSON.stringify(e,null,2)+`
|
|
3
|
+
`,{encoding:"utf8",mode:384}),je(n,o)}function te(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 O=class{detect(e=process.cwd()){let t=v(e),o=De(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(p=>Y(I(o,p))),a=Le(t,i?o:void 0);if(a){let p=z(a),u=Ke(p);return u.rootPath!==a&&(u.rootPath=a,u.updatedAt=new Date().toISOString(),ee(a,u)),te(u,a)}let c=new Date().toISOString(),d=Re(o),m={version:1,id:Me(o),name:d,remote:d,rootPath:o,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return ee(o,m),te(m,o)}};import{homedir as it}from"node:os";import{join as at}from"node:path";import{DeleteObjectCommand as Ne,GetObjectCommand as Ue,HeadObjectCommand as $e,ListObjectsV2Command as Be,PutObjectCommand as Fe,S3Client as Ge}from"@aws-sdk/client-s3";import{getSignedUrl as Ve}from"@aws-sdk/s3-request-presigner";var k=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ge({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 Fe({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await Ve(this.client,new Ue({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 $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 Ne({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new Be({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let i of n.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return t}};import{access as ne,mkdir as He,readFile as Ye,readdir as ze,rm as Je,stat as se,writeFile as qe}from"node:fs/promises";import{dirname as We,join as Ze,relative as ie,resolve as Qe}from"node:path";var A=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 o=this.path(e);await He(We(o),{recursive:!0}),await qe(o,t)}async get(e){try{return await Ye(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 Je(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await ne(t)}catch{return o}let n=async a=>{let c=await ze(a,{withFileTypes:!0});for(let d of c){let m=Ze(a,d.name);if(d.isDirectory()){await n(m);continue}let p=await se(m);o.push({key:ie(this.root,m),size:p.size,updatedAt:p.mtime.toISOString()})}},i=await se(t);return i.isDirectory()?await n(t):o.push({key:ie(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),o}};import{DeleteObjectCommand as Xe,GetObjectCommand as et,HeadObjectCommand as tt,ListObjectsV2Command as rt,PutObjectCommand as ot,S3Client as nt}from"@aws-sdk/client-s3";import{getSignedUrl as st}from"@aws-sdk/s3-request-presigner";var S=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new nt({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,o="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ot({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await st(this.client,new et({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 tt({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 Xe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new rt({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let i of n.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return t}};function J(r,e){return console.warn(e),new A(r)}function ae(r){let e=r.localRoot??at(it(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new S({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):J(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 S({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):J(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 k({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):J(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new A(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 x=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 R=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 C=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 M=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 ct(r){return new Promise(e=>setTimeout(e,r))}async function ce(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,n=e.maxDelayMs??2e3,i;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(i=c,a>=t)break;let d=Math.min(n,o*2**(a-1)),m=Math.floor(Math.random()*Math.max(1,d*.2));await ct(d+m)}throw i}var dt=new Set(["put","get","getText","delete","list"]);function de(r,e={}){return new Proxy(r,{get(t,o){let n=Reflect.get(t,o,t);return typeof n!="function"?n:dt.has(o)?(...i)=>ce(()=>Promise.resolve(n.apply(t,i)),e):n.bind(t)}})}var le=[{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 f(r){let e=le.find(t=>t.id===r);if(!e)throw new Error(`Unsupported AI provider: ${r}`);return e}function T(r){return le.some(e=>e.id===r)}function s(r){return process.env[r]?.trim()||void 0}function l(...r){return r.find(e=>!!e?.trim())}function pe(){if(s("GROQ_API_KEY"))return"groq";if(s("DEEPSEEK_API_KEY"))return"deepseek";if(s("NVIDIA_API_KEY")||s("NVIDIA_NIM_API_KEY"))return"nvidia";if(s("OPENROUTER_API_KEY"))return"openrouter";if(s("ALIBABA_API_KEY")||s("DASHSCOPE_API_KEY"))return"alibaba";if(s("GEMINI_API_KEY")||s("GOOGLE_API_KEY"))return"gemini";if(s("CLOUDFLARE_API_TOKEN")&&s("CLOUDFLARE_ACCOUNT_ID"))return"cloudflare";if(s("HF_TOKEN"))return"huggingface";if(s("OLLAMA_MODEL")||s("OLLAMA_BASE_URL"))return"ollama"}function me(){let r=s("TOOLNET_LLM_PROVIDER");return r&&T(r)?r:pe()??"openai-compatible"}function L(r){let e=f(r);switch(r){case"alibaba":return{provider:r,apiKey:l(s("ALIBABA_API_KEY"),s("DASHSCOPE_API_KEY")),baseUrl:l(s("ALIBABA_BASE_URL"),s("DASHSCOPE_BASE_URL"),e.defaultBaseUrl),model:l(s("ALIBABA_MODEL"),s("DASHSCOPE_MODEL"))};case"openrouter":return{provider:r,apiKey:s("OPENROUTER_API_KEY"),baseUrl:l(s("OPENROUTER_BASE_URL"),e.defaultBaseUrl),model:s("OPENROUTER_MODEL")};case"groq":return{provider:r,apiKey:s("GROQ_API_KEY"),baseUrl:l(s("GROQ_BASE_URL"),e.defaultBaseUrl),model:s("GROQ_MODEL")};case"deepseek":return{provider:r,apiKey:s("DEEPSEEK_API_KEY"),baseUrl:l(s("DEEPSEEK_BASE_URL"),e.defaultBaseUrl),model:l(s("DEEPSEEK_MODEL"),e.defaultModel)};case"nvidia":return{provider:r,apiKey:l(s("NVIDIA_API_KEY"),s("NVIDIA_NIM_API_KEY")),baseUrl:l(s("NVIDIA_BASE_URL"),s("NVIDIA_NIM_BASE_URL"),e.defaultBaseUrl),model:l(s("NVIDIA_MODEL"),s("NVIDIA_NIM_MODEL"),e.defaultModel)};case"gemini":return{provider:r,apiKey:l(s("GEMINI_API_KEY"),s("GOOGLE_API_KEY")),baseUrl:l(s("GEMINI_BASE_URL"),e.defaultBaseUrl),model:s("GEMINI_MODEL")};case"huggingface":return{provider:r,apiKey:s("HF_TOKEN"),baseUrl:l(s("HF_INFERENCE_BASE_URL"),e.defaultBaseUrl),model:l(s("HF_LLM_MODEL"),s("HF_MODEL"))};case"ollama":return{provider:r,apiKey:s("OLLAMA_API_KEY"),baseUrl:l(s("OLLAMA_BASE_URL"),e.defaultBaseUrl),model:s("OLLAMA_MODEL")};case"cloudflare":return{provider:r,accountId:s("CLOUDFLARE_ACCOUNT_ID"),apiKey:s("CLOUDFLARE_API_TOKEN"),baseUrl:s("CLOUDFLARE_AI_BASE_URL"),model:s("CLOUDFLARE_MODEL")};case"custom":return{provider:r,apiKey:s("CUSTOM_AI_API_KEY"),baseUrl:s("CUSTOM_AI_BASE_URL"),model:s("CUSTOM_AI_MODEL")};default:return{provider:"openai-compatible",apiKey:l(s("OPENAI_API_KEY"),s("MODEL_API_KEY")),baseUrl:l(s("OPENAI_BASE_URL"),s("MODEL_BASE_URL")),model:l(s("OPENAI_MODEL"),s("MODEL_NAME"))}}}function lt(){let r=s("TOOLNET_LLM_PROVIDER"),e=r&&T(r)?r:me(),t=f(e),o=L(e);return{provider:e,apiKey:l(s("TOOLNET_LLM_API_KEY"),o.apiKey),baseUrl:l(s("TOOLNET_LLM_BASE_URL"),o.baseUrl,t.defaultBaseUrl),model:l(s("TOOLNET_LLM_MODEL"),o.model,t.defaultModel),accountId:l(s("TOOLNET_LLM_ACCOUNT_ID"),o.accountId)}}function pt(){let r=s("TOOLNET_EMBEDDING_PROVIDER");return r==="local"?"local":r&&T(r)?r:s("HF_TOKEN")||s("HF_EMBEDDING_MODEL")?"huggingface":"local"}function mt(){let r=pt();if(r==="local")return{provider:"local",model:l(s("TOOLNET_EMBEDDING_MODEL"),s("LOCAL_EMBEDDING_MODEL"))};let e=f(r),t,o,n,i;switch(r){case"huggingface":t=s("HF_TOKEN"),o=s("HF_INFERENCE_BASE_URL"),n=s("HF_EMBEDDING_MODEL");break;case"openai-compatible":t=s("OPENAI_API_KEY"),o=s("OPENAI_BASE_URL"),n=s("OPENAI_EMBEDDING_MODEL");break;case"cloudflare":t=s("CLOUDFLARE_API_TOKEN"),o=s("CLOUDFLARE_AI_BASE_URL"),n=s("CLOUDFLARE_EMBEDDING_MODEL"),i=s("CLOUDFLARE_ACCOUNT_ID");break;default:t=L(r).apiKey,o=L(r).baseUrl,n=s(`${r.toUpperCase().replace(/-/g,"_")}_EMBEDDING_MODEL`)}let a=l(s("TOOLNET_EMBEDDING_ACCOUNT_ID"),i),c=l(s("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:l(s("TOOLNET_EMBEDDING_API_KEY"),t),baseUrl:d,model:l(s("TOOLNET_EMBEDDING_MODEL"),n),accountId:a}}function w(){let r=lt(),e=mt();return{llm:r,embedding:e,legacy:{llm:!s("TOOLNET_LLM_PROVIDER")&&!!pe(),embedding:!s("TOOLNET_EMBEDDING_PROVIDER")&&!!(s("HF_TOKEN")||s("HF_EMBEDDING_MODEL"))}}}function ue(r=me()){let e=w().llm;if(r===e.provider)return{id:r,apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model,accountId:e.accountId};let t=L(r);return{id:r,apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,accountId:t.accountId}}var D=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)}`,i=await fetch(n,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({model:`models/${t}`,content:{parts:[{text:e}]}})});if(!i.ok)throw new Error(`Gemini embedding failed: ${i.status} ${await i.text()}`);let c=(await i.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 ut}from"node:crypto";var K=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=ut("sha256").update(n).digest().readUInt32BE(0)%this.dimensions;t[a]+=1}return gt(t)}async embedMany(e){return Promise.all(e.map(t=>this.embed(t)))}};function gt(r){let e=Math.sqrt(r.reduce((t,o)=>t+o*o,0));return e===0?r:r.map(t=>t/e)}var N=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 i=(await o.json()).data?.sort((a,c)=>(a.index??0)-(c.index??0)).map(a=>a.embedding);if(!i||i.length!==e.length||i.some(a=>!Array.isArray(a)))throw new Error("Embedding provider returned invalid response");return i}};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`,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(i=>ft(i))}};function ft(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 i=0;i<t;i++)o[i]+=n[i]??0;for(let n=0;n<t;n++)o[n]/=e.length;return o}throw new Error("Unsupported embedding shape")}var q=class{constructor(e,t=new K){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 yt(r){return`https://api.cloudflare.com/client/v4/accounts/${r}/ai/v1`}function ht(){let r=w().embedding;if(r.provider==="local")return null;if(r.provider==="huggingface")return!r.apiKey||!r.model?null:new U({token:r.apiKey,model:r.model});if(r.provider==="gemini")return!r.apiKey||!r.model?null:new D({apiKey:r.apiKey,model:r.model,baseUrl:r.baseUrl});let e=r.baseUrl;return r.provider==="cloudflare"&&!e&&r.accountId&&(e=yt(r.accountId)),e&&r.model?new N({apiKey:r.apiKey,baseUrl:e,model:r.model}):null}function ge(){return new q(ht())}var W=class extends Error{status;constructor(e,t){super(e),this.name="AiHttpError",this.status=t}};async function y(r,e,t=3e4){let o=new AbortController,n=setTimeout(()=>o.abort(),t);n.unref?.();try{let i=await fetch(r,{...e,signal:o.signal}),a=await i.text();if(!i.ok){let c=a;try{let d=JSON.parse(a);c=d.error?.message??d.message??a}catch{}throw new W(c||`HTTP ${i.status}`,i.status)}return a.trim()?JSON.parse(a):{}}finally{clearTimeout(n)}}function $(r,e){return`${r.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`}var B=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 y(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 F=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(),i=e.messages.filter(p=>p.role==="system"),a=e.messages.filter(p=>p.role!=="system").map(p=>({role:p.role==="assistant"?"model":"user",parts:[{text:p.content}]})),c=`${$(o,`models/${encodeURIComponent(n)}:generateContent`)}?key=${encodeURIComponent(t)}`,d=await y(c,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({...i.length?{systemInstruction:{parts:[{text:i.map(p=>p.content).join(`
|
|
4
|
+
|
|
5
|
+
`)}]}}:{},contents:a,generationConfig:{temperature:e.temperature,maxOutputTokens:e.maxTokens}})}),m=d.candidates?.[0]?.content?.parts?.map(p=>p.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 G=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 y($(this.baseUrl(),"chat/completions"),{method:"POST",headers:this.headers(),body:JSON.stringify({model:t,messages:e.messages,temperature:e.temperature,max_tokens:e.maxTokens})}),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 fe(r=ue()){switch(f(r.id).transport){case"gemini":return new F(r);case"cloudflare":return new B(r);default:return new G(r)}}var bt=["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"],V=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(),i=n.replace(/[:.]/g,"-"),a=this.projectPrefix(e),c=this.snapshotPrefix(e,i),d=[];for(let p of bt){let u=`${a}/${p}`,_=await this.storage.get(u);_&&(await this.storage.put(`${c}/${p}`,_,"application/json"),d.push(p))}if(d.length===0)return null;let m={version:1,id:i,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")),i=[];for(let a of n){let c=await this.storage.getText(a.key);if(c)try{i.push(JSON.parse(c))}catch{}}return i.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 i=JSON.parse(n);if(i.projectId!==e)throw new Error("Snapshot project mismatch");let a=this.projectPrefix(e),c=[];for(let d of i.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 i of n)await this.storage.delete(i.key);return n.length}async prune(e,t=10){let n=(await this.list(e)).slice(t),i=0;for(let a of n)await this.remove(e,a.id),i++;return i}};function Z(r,e){for(let t of r)process.env[t]||e.push(`Missing ${t}`)}function ye(){let r=[],e=[],t=process.env.MEMORY_STORAGE_PROVIDER??"local";return t==="r2"?Z(["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"],r):t==="s3"?Z(["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"],r):t==="huggingface"?Z(["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 Et}from"node:crypto";var H=class{constructor(e,t){this.storage=e;this.embeddings=t}storage;embeddings;async run(){let e=`_health/production-${Et()}.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 i=await this.embeddings.embed("ToolNet Memory production health check");n={ok:i.length>0,dimensions:i.length,mode:i.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}}};function be(){return process.argv.includes("--json")}function vt(r){if(console.log(""),console.log("ToolNet Memory Doctor"),console.log("====================="),console.log(""),r.project&&(console.log(`Project: ${r.project}`),console.log("")),r.storage)if(r.storage.ok){let n=typeof r.storage.provider=="string"?` (${r.storage.provider})`:"";console.log(`\u2713 Storage${n}`)}else console.log("\u2717 Storage");if(r.llm)if(r.llm.ok){let n=[];r.llm.provider&&n.push(r.llm.provider),r.llm.model&&n.push(r.llm.model),typeof r.llm.latencyMs=="number"&&n.push(`${r.llm.latencyMs} ms`),console.log(`\u2713 LLM${n.length?` (${n.join(", ")})`:""}`)}else console.log(`\u2717 LLM${r.llm.error?` (${r.llm.error})`:""}`);if(r.embedding)if(r.embedding.ok){let n=[];typeof r.embedding.mode=="string"&&n.push(r.embedding.mode),typeof r.embedding.dimensions=="number"&&n.push(`${r.embedding.dimensions} dimensions`),console.log(`\u2713 Embedding${n.length?` (${n.join(", ")})`:""}`)}else console.log("\u2717 Embedding");(r.memory!==void 0||r.graphSymbols!==void 0||r.codeChunks!==void 0)&&(console.log(""),console.log("Project data:"),r.memory!==void 0&&console.log(` Memories ${r.memory}`),r.graphSymbols!==void 0&&console.log(` Graph symbols ${r.graphSymbols}`),r.graphEdges!==void 0&&console.log(` Graph edges ${r.graphEdges}`),r.memoryVectors!==void 0&&console.log(` Memory vectors ${r.memoryVectors}`),r.codeChunks!==void 0&&console.log(` Code chunks ${r.codeChunks}`),r.codeVectors!==void 0&&console.log(` Code vectors ${r.codeVectors}`),r.snapshots!==void 0&&console.log(` Snapshots ${r.snapshots}`));let e=r.config?.errors??[];if(e.length>0){console.log(""),console.log("Configuration required:");for(let n of e)console.log(` - ${n}`)}let t=[...r.config?.warnings??[],...r.warnings??[]],o=[...new Set(t)];if(o.length>0){console.log(""),console.log("Warnings:");for(let n of o)console.log(` - ${n}`)}console.log(""),r.ok?console.log("\u2713 ToolNet Memory is ready"):(console.log("\u2717 ToolNet Memory requires attention"),e.length>0&&(console.log(""),console.log("Run:"),console.log(" toolnet-memory setup"))),console.log("")}function he(r){be()?console.log(JSON.stringify(r,null,2)):vt(r)}async function At(){let r=ye();if(!r.ok){let g={ok:!1,config:r,warnings:r.warnings};he(g),process.exitCode=1;return}let e=X(),t=new O().detect(),o=de(ae({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),n=ge(),i=w(),a;try{let h=await fe({id:i.llm.provider,apiKey:i.llm.apiKey,baseUrl:i.llm.baseUrl,model:i.llm.model,accountId:i.llm.accountId}).healthCheck();a={ok:h.ok,provider:i.llm.provider,model:h.model??i.llm.model,latencyMs:h.latencyMs,error:h.ok?void 0:h.message}}catch(g){a={ok:!1,provider:i.llm.provider,model:i.llm.model,error:g instanceof Error?g.message:String(g)}}let c=await new H(o,n).run(),d=await new j(o).load(t.id),m=await new R(o).load(t.id),p=await new x(o).load(t.id),u=await new C(o).load(t.id),_=await new M(o).load(t.id),Ee=await new V(o).list(t.id),Q={ok:c.ok&&!!a?.ok,project:t.name,storage:c.storage,llm:a,embedding:c.embedding,memory:d.length,graphSymbols:m?.symbols.length??0,graphEdges:m?.edges.length??0,memoryVectors:p?.records.length??0,codeChunks:u?.chunks.length??0,codeVectors:_?.records.length??0,snapshots:Ee.length,warnings:r.warnings};he(Q),process.exitCode=Q.ok?0:1}At().catch(r=>{let e=r instanceof Error?r.message:String(r);be()?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});
|