toolnet-memory 0.2.13 → 0.2.15
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 +119 -26
- package/bundle/auto-integrate.js +600 -127
- package/bundle/doctor.js +3 -3
- package/bundle/full-index.js +8 -8
- package/bundle/mcp.js +7 -7
- package/bundle/opencode.js +637 -161
- package/bundle/runtime.js +11 -11
- package/bundle/semantic.js +6 -6
- package/bundle/setup.js +613 -187
- package/package.json +1 -1
package/bundle/setup.js
CHANGED
|
@@ -1,25 +1,171 @@
|
|
|
1
|
-
import m from"node:fs";import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import m from"node:fs";import j from"node:os";import R from"node:path";import ze from"node:readline/promises";import{stdin as f,stdout as I}from"node:process";import{HeadBucketCommand as Ze,S3Client as Qe}from"@aws-sdk/client-s3";var W=[{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 A(e){let t=W.find(o=>o.id===e);if(!t)throw new Error(`Unsupported AI provider: ${e}`);return t}function N(e){return W.some(t=>t.id===e)}function i(e){return process.env[e]?.trim()||void 0}function g(...e){return e.find(t=>!!t?.trim())}function X(){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 z(){let e=i("TOOLNET_LLM_PROVIDER");return e&&N(e)?e:X()??"openai-compatible"}function h(e){let t=A(e);switch(e){case"alibaba":return{provider:e,apiKey:g(i("ALIBABA_API_KEY"),i("DASHSCOPE_API_KEY")),baseUrl:g(i("ALIBABA_BASE_URL"),i("DASHSCOPE_BASE_URL"),t.defaultBaseUrl),model:g(i("ALIBABA_MODEL"),i("DASHSCOPE_MODEL"))};case"openrouter":return{provider:e,apiKey:i("OPENROUTER_API_KEY"),baseUrl:g(i("OPENROUTER_BASE_URL"),t.defaultBaseUrl),model:i("OPENROUTER_MODEL")};case"groq":return{provider:e,apiKey:i("GROQ_API_KEY"),baseUrl:g(i("GROQ_BASE_URL"),t.defaultBaseUrl),model:i("GROQ_MODEL")};case"deepseek":return{provider:e,apiKey:i("DEEPSEEK_API_KEY"),baseUrl:g(i("DEEPSEEK_BASE_URL"),t.defaultBaseUrl),model:g(i("DEEPSEEK_MODEL"),t.defaultModel)};case"nvidia":return{provider:e,apiKey:g(i("NVIDIA_API_KEY"),i("NVIDIA_NIM_API_KEY")),baseUrl:g(i("NVIDIA_BASE_URL"),i("NVIDIA_NIM_BASE_URL"),t.defaultBaseUrl),model:g(i("NVIDIA_MODEL"),i("NVIDIA_NIM_MODEL"),t.defaultModel)};case"gemini":return{provider:e,apiKey:g(i("GEMINI_API_KEY"),i("GOOGLE_API_KEY")),baseUrl:g(i("GEMINI_BASE_URL"),t.defaultBaseUrl),model:i("GEMINI_MODEL")};case"huggingface":return{provider:e,apiKey:i("HF_TOKEN"),baseUrl:g(i("HF_INFERENCE_BASE_URL"),t.defaultBaseUrl),model:g(i("HF_LLM_MODEL"),i("HF_MODEL"))};case"ollama":return{provider:e,apiKey:i("OLLAMA_API_KEY"),baseUrl:g(i("OLLAMA_BASE_URL"),t.defaultBaseUrl),model:i("OLLAMA_MODEL")};case"cloudflare":return{provider:e,accountId:i("CLOUDFLARE_ACCOUNT_ID"),apiKey:i("CLOUDFLARE_API_TOKEN"),baseUrl:i("CLOUDFLARE_AI_BASE_URL"),model:i("CLOUDFLARE_MODEL")};case"custom":return{provider:e,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 Te(){let e=i("TOOLNET_LLM_PROVIDER"),t=e&&N(e)?e:z(),o=A(t),n=h(t);return{provider:t,apiKey:g(i("TOOLNET_LLM_API_KEY"),n.apiKey),baseUrl:g(i("TOOLNET_LLM_BASE_URL"),n.baseUrl,o.defaultBaseUrl),model:g(i("TOOLNET_LLM_MODEL"),n.model,o.defaultModel),accountId:g(i("TOOLNET_LLM_ACCOUNT_ID"),n.accountId)}}function Ae(){let e=i("TOOLNET_EMBEDDING_PROVIDER");return e==="local"?"local":e&&N(e)?e:i("HF_TOKEN")||i("HF_EMBEDDING_MODEL")?"huggingface":"local"}function Se(){let e=Ae();if(e==="local")return{provider:"local",model:g(i("TOOLNET_EMBEDDING_MODEL"),i("LOCAL_EMBEDDING_MODEL"))};let t=A(e),o,n,r,s;switch(e){case"huggingface":o=i("HF_TOKEN"),n=i("HF_INFERENCE_BASE_URL"),r=i("HF_EMBEDDING_MODEL");break;case"openai-compatible":o=i("OPENAI_API_KEY"),n=i("OPENAI_BASE_URL"),r=i("OPENAI_EMBEDDING_MODEL");break;case"cloudflare":o=i("CLOUDFLARE_API_TOKEN"),n=i("CLOUDFLARE_AI_BASE_URL"),r=i("CLOUDFLARE_EMBEDDING_MODEL"),s=i("CLOUDFLARE_ACCOUNT_ID");break;default:o=h(e).apiKey,n=h(e).baseUrl,r=i(`${e.toUpperCase().replace(/-/g,"_")}_EMBEDDING_MODEL`)}return{provider:e,apiKey:g(i("TOOLNET_EMBEDDING_API_KEY"),o),baseUrl:g(i("TOOLNET_EMBEDDING_BASE_URL"),n,t.defaultBaseUrl),model:g(i("TOOLNET_EMBEDDING_MODEL"),r),accountId:g(i("TOOLNET_EMBEDDING_ACCOUNT_ID"),s)}}function Ce(){let e=Te(),t=Se();return{llm:e,embedding:t,legacy:{llm:!i("TOOLNET_LLM_PROVIDER")&&!!X(),embedding:!i("TOOLNET_EMBEDDING_PROVIDER")&&!!(i("HF_TOKEN")||i("HF_EMBEDDING_MODEL"))}}}function Z(e=z()){let t=Ce().llm;if(e===t.provider)return{id:e,apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,accountId:t.accountId};let o=h(e);return{id:e,apiKey:o.apiKey,baseUrl:o.baseUrl,model:o.model,accountId:o.accountId}}var w=class extends Error{status;constructor(t,o){super(t),this.name="AiHttpError",this.status=o}};async function S(e,t,o=3e4){let n=new AbortController,r=setTimeout(()=>n.abort(),o);r.unref?.();try{let s=await fetch(e,{...t,signal:n.signal}),a=await s.text();if(!s.ok){let l=a;try{let c=JSON.parse(a);l=c.error?.message??c.message??a}catch{}throw new w(l||`HTTP ${s.status}`,s.status)}return a.trim()?JSON.parse(a):{}}finally{clearTimeout(r)}}function D(e,t){return`${e.replace(/\/+$/,"")}/${t.replace(/^\/+/,"")}`}var P=class{constructor(t){this.config=t}config;id="cloudflare";model(){let t=this.config.model?.trim();if(!t)throw new Error("cloudflare: MODEL is not configured");return t}async generate(t){let o=this.config.accountId?.trim(),n=this.config.apiKey?.trim();if(!o)throw new Error("cloudflare: ACCOUNT ID is not configured");if(!n)throw new Error("cloudflare: API TOKEN is not configured");let r=this.model(),a=`${(this.config.baseUrl?.trim()||`https://api.cloudflare.com/client/v4/accounts/${o}/ai/run`).replace(/\/+$/,"")}/${r}`,l=await S(a,{method:"POST",headers:{authorization:`Bearer ${n}`,"content-type":"application/json",...this.config.headers},body:JSON.stringify({messages:t.messages,temperature:t.temperature,max_tokens:t.maxTokens})}),c=l.result?.response?.trim();if(!c)throw new Error(l.errors?.[0]?.message??"cloudflare: empty model response");return{text:c,provider:"cloudflare",model:r}}async healthCheck(){let t=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()-t}}catch(o){return{ok:!1,provider:"cloudflare",model:this.config.model,message:o instanceof Error?o.message:String(o),latencyMs:Date.now()-t}}}};var U=class{constructor(t){this.config=t}config;id="gemini";model(){let t=this.config.model?.trim();if(!t)throw new Error("gemini: MODEL is not configured");return t.replace(/^models\//,"")}async generate(t){let o=this.config.apiKey?.trim();if(!o)throw new Error("gemini: API KEY is not configured");let n=this.config.baseUrl?.trim()||"https://generativelanguage.googleapis.com/v1beta",r=this.model(),s=t.messages.filter(E=>E.role==="system"),a=t.messages.filter(E=>E.role!=="system").map(E=>({role:E.role==="assistant"?"model":"user",parts:[{text:E.content}]})),l=`${D(n,`models/${encodeURIComponent(r)}:generateContent`)}?key=${encodeURIComponent(o)}`,c=await S(l,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({...s.length?{systemInstruction:{parts:[{text:s.map(E=>E.content).join(`
|
|
2
|
+
|
|
3
|
+
`)}]}}:{},contents:a,generationConfig:{temperature:t.temperature,maxOutputTokens:t.maxTokens}})}),d=c.candidates?.[0]?.content?.parts?.map(E=>E.text??"").join("").trim();if(!d)throw new Error("gemini: empty model response");return{text:d,provider:"gemini",model:r,usage:c.usageMetadata?{inputTokens:c.usageMetadata.promptTokenCount,outputTokens:c.usageMetadata.candidatesTokenCount,totalTokens:c.usageMetadata.totalTokenCount}:void 0}}async healthCheck(){let t=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()-t}}catch(o){return{ok:!1,provider:"gemini",model:this.config.model,message:o instanceof Error?o.message:String(o),latencyMs:Date.now()-t}}}};var K=class{constructor(t){this.config=t;this.id=t.id}config;id;baseUrl(){let t=this.config.baseUrl?.trim();if(!t)throw new Error(`${this.id}: BASE URL is not configured`);return t}model(){let t=this.config.model?.trim();if(!t)throw new Error(`${this.id}: MODEL is not configured`);return t}headers(){let t={"content-type":"application/json",...this.config.headers};return this.config.apiKey&&(t.authorization=`Bearer ${this.config.apiKey}`),t}async generate(t){let o=this.model(),n=await S(D(this.baseUrl(),"chat/completions"),{method:"POST",headers:this.headers(),body:JSON.stringify({model:o,messages:t.messages,temperature:t.temperature,max_tokens:t.maxTokens})}),r=n.choices?.[0]?.message?.content?.trim();if(!r)throw new Error(`${this.id}: empty model response`);return{text:r,provider:this.id,model:o,usage:n.usage?{inputTokens:n.usage.prompt_tokens,outputTokens:n.usage.completion_tokens,totalTokens:n.usage.total_tokens}:void 0}}async healthCheck(){let t=Date.now();try{let o=await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8});return{ok:!0,provider:this.id,model:o.model,message:"Provider reachable",latencyMs:Date.now()-t}}catch(o){return{ok:!1,provider:this.id,model:this.config.model,message:o instanceof Error?o.message:String(o),latencyMs:Date.now()-t}}}};function Q(e=Z()){switch(A(e.id).transport){case"gemini":return new U(e);case"cloudflare":return new P(e);default:return new K(e)}}import{existsSync as $}from"node:fs";import{homedir as F}from"node:os";import{join as v}from"node:path";import{spawnSync as Ge}from"node:child_process";import{existsSync as ye,mkdirSync as Ie,readFileSync as Re,writeFileSync as Me}from"node:fs";import{homedir as Le}from"node:os";import{dirname as Ne,join as he}from"node:path";function De(e){return`'${e.replace(/'/g,"'\\''")}'`}function ee(e={}){let t=e.hooksFile??he(Le(),".gemini","config","hooks.json");Ie(Ne(t),{recursive:!0});let o={};if(ye(t))try{o=JSON.parse(Re(t,"utf8"))}catch(s){throw new Error(`Invalid existing Agy hooks.json: ${s instanceof Error?s.message:String(s)}`)}let n=e.binary??"toolnet-memory",r=`${De(n)} session:agy-hook`;return o["toolnet-memory"]={enabled:!0,PreInvocation:[{type:"command",command:`${r} pre`,timeout:15}],PostInvocation:[{type:"command",command:`${r} post`,timeout:15}],Stop:[{type:"command",command:`${r} stop`,timeout:30}]},Me(t,JSON.stringify(o,null,2)+`
|
|
4
|
+
`,{encoding:"utf8",mode:384}),t}import{existsSync as Pe,mkdirSync as oe,readFileSync as Ue,writeFileSync as ne}from"node:fs";import{homedir as B}from"node:os";import{join as b}from"node:path";var te="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",k="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function Ke(){let e=b(B(),".config","opencode","AGENTS.md");oe(b(B(),".config","opencode"),{recursive:!0});let t=`${te}
|
|
5
|
+
## ToolNet Memory
|
|
6
|
+
|
|
7
|
+
ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
|
|
8
|
+
|
|
9
|
+
Rules:
|
|
10
|
+
|
|
11
|
+
1. Do not create a ToolNet project automatically.
|
|
12
|
+
2. If no valid \`.toolnet/project.json\` exists in the current project or an ancestor, ignore ToolNet Memory.
|
|
13
|
+
3. Normal startup context must stay small and selective.
|
|
14
|
+
4. Do not automatically run deep recovery commands.
|
|
15
|
+
5. Use ToolNet MCP or selective retrieval when older project knowledge is actually needed.
|
|
16
|
+
6. Raw transcripts must not be injected into prompts.
|
|
17
|
+
7. Current repository evidence has priority over stale memory when they conflict.
|
|
18
|
+
|
|
19
|
+
${k}`,o=Pe(e)?Ue(e,"utf8"):"",n=o.indexOf(te),r=o.indexOf(k);return n>=0&&r>=n?o=o.slice(0,n)+t+o.slice(r+k.length):(o=o.trimEnd(),o&&(o+=`
|
|
20
|
+
|
|
21
|
+
`),o+=t),ne(e,o.trimEnd()+`
|
|
22
|
+
`,{encoding:"utf8",mode:384}),e}function re(e={}){let t=e.directory??b(B(),".config","opencode","plugins");oe(t,{recursive:!0}),Ke();let o=b(t,"toolnet-memory.js"),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",r=`
|
|
23
|
+
// Generated by ToolNet Memory.
|
|
24
|
+
// OpenCode integration:
|
|
25
|
+
// - project gated
|
|
26
|
+
// - selective context injection
|
|
27
|
+
// - incremental session checkpoints
|
|
28
|
+
// - periodic durability sync
|
|
29
|
+
// - best-effort shutdown flush
|
|
30
|
+
|
|
31
|
+
import fs from "node:fs"
|
|
32
|
+
import path from "node:path"
|
|
33
|
+
import os from "node:os"
|
|
34
|
+
|
|
35
|
+
const TOOLNET_BINARY =
|
|
36
|
+
${JSON.stringify(n)}
|
|
37
|
+
|
|
38
|
+
const CONTEXT_MAX_TOKENS = 700
|
|
39
|
+
|
|
40
|
+
const CONTEXT_CACHE_MS = 5000
|
|
41
|
+
|
|
42
|
+
const PERIODIC_SYNC_MS = 30000
|
|
43
|
+
|
|
44
|
+
const SYNC_TIMEOUT_MS = 15000
|
|
45
|
+
|
|
46
|
+
const STATUS_FILE = path.join(
|
|
47
|
+
os.homedir(),
|
|
48
|
+
".config",
|
|
49
|
+
"toolnet-memory",
|
|
50
|
+
"opencode-sync-status.json"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
function writeStatus(data) {
|
|
54
|
+
try {
|
|
55
|
+
fs.mkdirSync(
|
|
56
|
+
path.dirname(
|
|
57
|
+
STATUS_FILE
|
|
58
|
+
),
|
|
59
|
+
{
|
|
60
|
+
recursive: true,
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
fs.writeFileSync(
|
|
65
|
+
STATUS_FILE,
|
|
66
|
+
JSON.stringify(
|
|
67
|
+
{
|
|
68
|
+
timestamp:
|
|
69
|
+
new Date()
|
|
70
|
+
.toISOString(),
|
|
71
|
+
...data,
|
|
72
|
+
},
|
|
73
|
+
null,
|
|
74
|
+
2
|
|
75
|
+
) + "\\n"
|
|
76
|
+
)
|
|
77
|
+
} catch {
|
|
78
|
+
// Status reporting must
|
|
79
|
+
// never break OpenCode.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function validProjectManifest(
|
|
84
|
+
file
|
|
85
|
+
) {
|
|
86
|
+
try {
|
|
87
|
+
const parsed =
|
|
88
|
+
JSON.parse(
|
|
89
|
+
fs.readFileSync(
|
|
90
|
+
file,
|
|
91
|
+
"utf8"
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return Boolean(
|
|
96
|
+
parsed &&
|
|
97
|
+
typeof parsed ===
|
|
98
|
+
"object" &&
|
|
99
|
+
typeof parsed.id ===
|
|
100
|
+
"string" &&
|
|
101
|
+
parsed.id.length > 0
|
|
102
|
+
)
|
|
103
|
+
} catch {
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function findProjectRoot(
|
|
109
|
+
inputDirectory
|
|
110
|
+
) {
|
|
111
|
+
let current =
|
|
112
|
+
path.resolve(
|
|
113
|
+
inputDirectory
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const filesystemRoot =
|
|
117
|
+
path.parse(
|
|
118
|
+
current
|
|
119
|
+
).root
|
|
120
|
+
|
|
121
|
+
while (true) {
|
|
122
|
+
const manifest =
|
|
123
|
+
path.join(
|
|
124
|
+
current,
|
|
125
|
+
".toolnet",
|
|
126
|
+
"project.json"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if (
|
|
130
|
+
fs.existsSync(
|
|
131
|
+
manifest
|
|
132
|
+
) &&
|
|
133
|
+
validProjectManifest(
|
|
134
|
+
manifest
|
|
135
|
+
)
|
|
136
|
+
) {
|
|
137
|
+
return current
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (
|
|
141
|
+
current ===
|
|
142
|
+
filesystemRoot
|
|
143
|
+
) {
|
|
144
|
+
break
|
|
145
|
+
}
|
|
4
146
|
|
|
5
|
-
|
|
147
|
+
const parent =
|
|
148
|
+
path.dirname(
|
|
149
|
+
current
|
|
150
|
+
)
|
|
6
151
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
6. Never invent missing rationale. If ToolNet says a reason was not explicitly recorded, inspect the implementation or ask rather than guessing.
|
|
152
|
+
if (
|
|
153
|
+
parent === current
|
|
154
|
+
) {
|
|
155
|
+
break
|
|
156
|
+
}
|
|
13
157
|
|
|
14
|
-
|
|
15
|
-
|
|
158
|
+
current = parent
|
|
159
|
+
}
|
|
16
160
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// OpenCode session capture + continuity bootstrap.
|
|
161
|
+
return null
|
|
162
|
+
}
|
|
20
163
|
|
|
21
|
-
function getSessionId(
|
|
22
|
-
|
|
164
|
+
function getSessionId(
|
|
165
|
+
event
|
|
166
|
+
) {
|
|
167
|
+
const p =
|
|
168
|
+
event?.properties ?? {}
|
|
23
169
|
|
|
24
170
|
const candidates = [
|
|
25
171
|
p.sessionID,
|
|
@@ -28,210 +174,490 @@ function getSessionId(event) {
|
|
|
28
174
|
p.session?.id,
|
|
29
175
|
]
|
|
30
176
|
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
177
|
+
return (
|
|
178
|
+
candidates.find(
|
|
179
|
+
value =>
|
|
180
|
+
typeof value ===
|
|
181
|
+
"string" &&
|
|
182
|
+
value.length > 0
|
|
183
|
+
) ?? null
|
|
184
|
+
)
|
|
34
185
|
}
|
|
35
186
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
187
|
+
async function runWithTimeout(
|
|
188
|
+
args,
|
|
189
|
+
{
|
|
190
|
+
stdout = "ignore",
|
|
191
|
+
timeout =
|
|
192
|
+
SYNC_TIMEOUT_MS,
|
|
193
|
+
} = {}
|
|
194
|
+
) {
|
|
195
|
+
const child =
|
|
196
|
+
Bun.spawn(
|
|
197
|
+
args,
|
|
198
|
+
{
|
|
199
|
+
stdin:
|
|
200
|
+
"ignore",
|
|
201
|
+
stdout,
|
|
202
|
+
stderr:
|
|
203
|
+
"ignore",
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
let timer
|
|
208
|
+
|
|
209
|
+
const timeoutPromise =
|
|
210
|
+
new Promise(
|
|
211
|
+
(_, reject) => {
|
|
212
|
+
timer =
|
|
213
|
+
setTimeout(
|
|
214
|
+
() => {
|
|
215
|
+
try {
|
|
216
|
+
child.kill()
|
|
217
|
+
} catch {}
|
|
218
|
+
|
|
219
|
+
reject(
|
|
220
|
+
new Error(
|
|
221
|
+
"ToolNet command timeout"
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
},
|
|
225
|
+
timeout
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
const exitCode =
|
|
232
|
+
await Promise.race([
|
|
233
|
+
child.exited,
|
|
234
|
+
timeoutPromise,
|
|
235
|
+
])
|
|
236
|
+
|
|
237
|
+
if (
|
|
238
|
+
typeof exitCode ===
|
|
239
|
+
"number" &&
|
|
240
|
+
exitCode !== 0
|
|
241
|
+
) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
\`ToolNet exited with code \${exitCode}\`
|
|
53
244
|
)
|
|
245
|
+
}
|
|
54
246
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
247
|
+
return child
|
|
248
|
+
} finally {
|
|
249
|
+
if (timer) {
|
|
250
|
+
clearTimeout(
|
|
251
|
+
timer
|
|
252
|
+
)
|
|
58
253
|
}
|
|
59
254
|
}
|
|
255
|
+
}
|
|
60
256
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
stderr: "ignore",
|
|
76
|
-
}
|
|
257
|
+
export const ToolNetMemoryPlugin =
|
|
258
|
+
async ({
|
|
259
|
+
directory,
|
|
260
|
+
}) => {
|
|
261
|
+
/*
|
|
262
|
+
* CRITICAL:
|
|
263
|
+
* Never auto-create a ToolNet
|
|
264
|
+
* project just because OpenCode
|
|
265
|
+
* happens to start in /root,
|
|
266
|
+
* /tmp, $HOME, etc.
|
|
267
|
+
*/
|
|
268
|
+
const projectRoot =
|
|
269
|
+
findProjectRoot(
|
|
270
|
+
directory
|
|
77
271
|
)
|
|
78
272
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
273
|
+
if (!projectRoot) {
|
|
274
|
+
writeStatus({
|
|
275
|
+
active: false,
|
|
276
|
+
reason:
|
|
277
|
+
"no-toolnet-project",
|
|
278
|
+
directory,
|
|
279
|
+
})
|
|
85
280
|
|
|
86
|
-
return
|
|
87
|
-
} catch {
|
|
88
|
-
return ""
|
|
281
|
+
return {}
|
|
89
282
|
}
|
|
90
|
-
}
|
|
91
283
|
|
|
92
|
-
|
|
93
|
-
|
|
284
|
+
writeStatus({
|
|
285
|
+
active: true,
|
|
286
|
+
projectRoot,
|
|
287
|
+
state:
|
|
288
|
+
"plugin-loaded",
|
|
289
|
+
})
|
|
94
290
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
binary,
|
|
98
|
-
"session:opencode-sync",
|
|
99
|
-
sessionId,
|
|
100
|
-
"--project",
|
|
101
|
-
directory,
|
|
102
|
-
]
|
|
291
|
+
let lastSessionId =
|
|
292
|
+
null
|
|
103
293
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
294
|
+
let syncChain =
|
|
295
|
+
Promise.resolve()
|
|
107
296
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
297
|
+
let contextCache = {
|
|
298
|
+
value: "",
|
|
299
|
+
expiresAt: 0,
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function readContext() {
|
|
303
|
+
const now =
|
|
304
|
+
Date.now()
|
|
113
305
|
|
|
114
306
|
if (
|
|
115
|
-
|
|
307
|
+
contextCache.value &&
|
|
308
|
+
now <
|
|
309
|
+
contextCache.expiresAt
|
|
116
310
|
) {
|
|
117
|
-
|
|
311
|
+
return (
|
|
312
|
+
contextCache.value
|
|
313
|
+
)
|
|
118
314
|
}
|
|
119
|
-
} catch {
|
|
120
|
-
// Memory capture must never break OpenCode.
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
315
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
316
|
+
try {
|
|
317
|
+
const child =
|
|
318
|
+
Bun.spawn(
|
|
319
|
+
[
|
|
320
|
+
TOOLNET_BINARY,
|
|
321
|
+
"context:print",
|
|
322
|
+
"--project",
|
|
323
|
+
projectRoot,
|
|
324
|
+
"--tokens",
|
|
325
|
+
String(
|
|
326
|
+
CONTEXT_MAX_TOKENS
|
|
327
|
+
),
|
|
328
|
+
],
|
|
329
|
+
{
|
|
330
|
+
stdin:
|
|
331
|
+
"ignore",
|
|
332
|
+
stdout:
|
|
333
|
+
"pipe",
|
|
334
|
+
stderr:
|
|
335
|
+
"ignore",
|
|
336
|
+
}
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
const textPromise =
|
|
340
|
+
new Response(
|
|
341
|
+
child.stdout
|
|
342
|
+
).text()
|
|
343
|
+
|
|
344
|
+
const exitCode =
|
|
345
|
+
await child.exited
|
|
346
|
+
|
|
347
|
+
if (
|
|
348
|
+
exitCode !== 0
|
|
349
|
+
) {
|
|
350
|
+
return ""
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const text =
|
|
354
|
+
(
|
|
355
|
+
await textPromise
|
|
356
|
+
).trim()
|
|
129
357
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
358
|
+
contextCache = {
|
|
359
|
+
value: text,
|
|
360
|
+
expiresAt:
|
|
361
|
+
now +
|
|
362
|
+
CONTEXT_CACHE_MS,
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return text
|
|
366
|
+
} catch {
|
|
367
|
+
return ""
|
|
368
|
+
}
|
|
369
|
+
}
|
|
134
370
|
|
|
371
|
+
async function syncNow(
|
|
372
|
+
sessionId,
|
|
373
|
+
flag,
|
|
374
|
+
reason
|
|
375
|
+
) {
|
|
135
376
|
if (!sessionId) {
|
|
136
377
|
return
|
|
137
378
|
}
|
|
138
379
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
380
|
+
const args = [
|
|
381
|
+
TOOLNET_BINARY,
|
|
382
|
+
"session:opencode-sync",
|
|
383
|
+
sessionId,
|
|
384
|
+
"--project",
|
|
385
|
+
projectRoot,
|
|
386
|
+
]
|
|
387
|
+
|
|
388
|
+
if (flag) {
|
|
389
|
+
args.push(
|
|
390
|
+
flag
|
|
391
|
+
)
|
|
142
392
|
}
|
|
143
393
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
394
|
+
try {
|
|
395
|
+
await runWithTimeout(
|
|
396
|
+
args
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
writeStatus({
|
|
400
|
+
active: true,
|
|
401
|
+
projectRoot,
|
|
402
|
+
sessionId,
|
|
403
|
+
reason,
|
|
404
|
+
state:
|
|
405
|
+
"sync-success",
|
|
406
|
+
})
|
|
407
|
+
} catch (error) {
|
|
408
|
+
writeStatus({
|
|
409
|
+
active: true,
|
|
410
|
+
projectRoot,
|
|
411
|
+
sessionId,
|
|
412
|
+
reason,
|
|
413
|
+
state:
|
|
414
|
+
"sync-failed",
|
|
415
|
+
error:
|
|
416
|
+
error instanceof
|
|
417
|
+
Error
|
|
418
|
+
? error.message
|
|
419
|
+
: String(
|
|
420
|
+
error
|
|
421
|
+
),
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
throw error
|
|
147
425
|
}
|
|
426
|
+
}
|
|
148
427
|
|
|
149
|
-
|
|
150
|
-
|
|
428
|
+
function queueSync(
|
|
429
|
+
sessionId,
|
|
430
|
+
flag,
|
|
431
|
+
reason
|
|
432
|
+
) {
|
|
433
|
+
if (!sessionId) {
|
|
434
|
+
return syncChain
|
|
151
435
|
}
|
|
152
|
-
|
|
436
|
+
|
|
437
|
+
lastSessionId =
|
|
438
|
+
sessionId
|
|
439
|
+
|
|
440
|
+
syncChain =
|
|
441
|
+
syncChain
|
|
442
|
+
.catch(
|
|
443
|
+
() =>
|
|
444
|
+
undefined
|
|
445
|
+
)
|
|
446
|
+
.then(
|
|
447
|
+
() =>
|
|
448
|
+
syncNow(
|
|
449
|
+
sessionId,
|
|
450
|
+
flag,
|
|
451
|
+
reason
|
|
452
|
+
)
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
return syncChain
|
|
456
|
+
}
|
|
153
457
|
|
|
154
458
|
/*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
459
|
+
* Periodic durability checkpoint.
|
|
460
|
+
*
|
|
461
|
+
* This is intentionally independent
|
|
462
|
+
* from session.idle/dispose because
|
|
463
|
+
* abrupt SSH/process termination may
|
|
464
|
+
* skip shutdown hooks.
|
|
465
|
+
*
|
|
466
|
+
* unref() prevents the timer itself
|
|
467
|
+
* from keeping OpenCode alive.
|
|
157
468
|
*/
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
469
|
+
const periodic =
|
|
470
|
+
setInterval(
|
|
471
|
+
() => {
|
|
472
|
+
if (
|
|
473
|
+
lastSessionId
|
|
474
|
+
) {
|
|
475
|
+
void queueSync(
|
|
476
|
+
lastSessionId,
|
|
477
|
+
null,
|
|
478
|
+
"periodic"
|
|
479
|
+
)
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
PERIODIC_SYNC_MS
|
|
483
|
+
)
|
|
161
484
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
485
|
+
if (
|
|
486
|
+
typeof periodic.unref ===
|
|
487
|
+
"function"
|
|
488
|
+
) {
|
|
489
|
+
periodic.unref()
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return {
|
|
493
|
+
event: async ({
|
|
494
|
+
event,
|
|
495
|
+
}) => {
|
|
496
|
+
const sessionId =
|
|
497
|
+
getSessionId(
|
|
498
|
+
event
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
if (sessionId) {
|
|
502
|
+
lastSessionId =
|
|
503
|
+
sessionId
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (
|
|
507
|
+
event.type ===
|
|
508
|
+
"session.idle"
|
|
509
|
+
) {
|
|
510
|
+
await queueSync(
|
|
511
|
+
sessionId,
|
|
512
|
+
"--idle",
|
|
513
|
+
"session.idle"
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
return
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (
|
|
520
|
+
event.type ===
|
|
521
|
+
"session.compacted"
|
|
522
|
+
) {
|
|
523
|
+
await queueSync(
|
|
524
|
+
sessionId,
|
|
525
|
+
"--compacted",
|
|
526
|
+
"session.compacted"
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
contextCache = {
|
|
530
|
+
value: "",
|
|
531
|
+
expiresAt: 0,
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (
|
|
538
|
+
event.type ===
|
|
539
|
+
"session.error"
|
|
540
|
+
) {
|
|
541
|
+
await queueSync(
|
|
542
|
+
sessionId,
|
|
543
|
+
"--error",
|
|
544
|
+
"session.error"
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
return
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
/*
|
|
552
|
+
* VERIFIED on OpenCode 1.18.14:
|
|
553
|
+
* this hook fires on real model turns.
|
|
554
|
+
*
|
|
555
|
+
* It remains experimental, therefore
|
|
556
|
+
* AGENTS.md + MCP stay as fallbacks.
|
|
557
|
+
*/
|
|
558
|
+
"experimental.chat.system.transform":
|
|
559
|
+
async (
|
|
560
|
+
_input,
|
|
561
|
+
output
|
|
562
|
+
) => {
|
|
563
|
+
const context =
|
|
564
|
+
await readContext()
|
|
565
|
+
|
|
566
|
+
if (!context) {
|
|
567
|
+
return
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (
|
|
571
|
+
Array.isArray(
|
|
572
|
+
output?.system
|
|
573
|
+
)
|
|
574
|
+
) {
|
|
575
|
+
output.system.push(
|
|
576
|
+
context
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
return
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (
|
|
583
|
+
typeof output?.system ===
|
|
584
|
+
"string"
|
|
585
|
+
) {
|
|
586
|
+
output.system =
|
|
587
|
+
output.system
|
|
588
|
+
? \`\${output.system}\\n\\n\${context}\`
|
|
589
|
+
: context
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
|
|
593
|
+
/*
|
|
594
|
+
* Optional compaction survival hook.
|
|
595
|
+
* We keep it as a supplementary path,
|
|
596
|
+
* never as the only continuity path.
|
|
597
|
+
*/
|
|
598
|
+
"experimental.session.compacting":
|
|
599
|
+
async (
|
|
600
|
+
_input,
|
|
601
|
+
output
|
|
602
|
+
) => {
|
|
603
|
+
const context =
|
|
604
|
+
await readContext()
|
|
605
|
+
|
|
606
|
+
if (
|
|
607
|
+
context &&
|
|
608
|
+
Array.isArray(
|
|
609
|
+
output?.context
|
|
610
|
+
)
|
|
611
|
+
) {
|
|
612
|
+
output.context.push(
|
|
613
|
+
context
|
|
614
|
+
)
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
|
|
618
|
+
/*
|
|
619
|
+
* VERIFIED for clean OpenCode exits.
|
|
620
|
+
* Best effort only: kill -9 / crash /
|
|
621
|
+
* machine loss cannot guarantee this.
|
|
622
|
+
*/
|
|
623
|
+
dispose: async () => {
|
|
624
|
+
clearInterval(
|
|
625
|
+
periodic
|
|
170
626
|
)
|
|
171
|
-
|
|
172
|
-
|
|
627
|
+
|
|
628
|
+
if (
|
|
629
|
+
lastSessionId
|
|
630
|
+
) {
|
|
631
|
+
try {
|
|
632
|
+
await queueSync(
|
|
633
|
+
lastSessionId,
|
|
634
|
+
"--idle",
|
|
635
|
+
"dispose"
|
|
636
|
+
)
|
|
637
|
+
} catch {
|
|
638
|
+
// Status already recorded.
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
try {
|
|
643
|
+
await syncChain
|
|
644
|
+
} catch {
|
|
645
|
+
// Do not block OpenCode shutdown.
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
}
|
|
173
649
|
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
`),a=
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
`)
|
|
180
|
-
`)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
# Cloudflare R2
|
|
189
|
-
R2_ACCOUNT_ID=${e.get("R2_ACCOUNT_ID")??""}
|
|
190
|
-
R2_BUCKET=${e.get("R2_BUCKET")??"toolnet-memory"}
|
|
191
|
-
R2_ACCESS_KEY_ID=${e.get("R2_ACCESS_KEY_ID")??""}
|
|
192
|
-
R2_SECRET_ACCESS_KEY=${e.get("R2_SECRET_ACCESS_KEY")??""}
|
|
193
|
-
|
|
194
|
-
# Generic S3 / S3-compatible storage
|
|
195
|
-
# Leave S3_ENDPOINT empty for AWS S3.
|
|
196
|
-
S3_ENDPOINT=${e.get("S3_ENDPOINT")??""}
|
|
197
|
-
S3_REGION=${e.get("S3_REGION")??"us-east-1"}
|
|
198
|
-
S3_BUCKET=${e.get("S3_BUCKET")??"toolnet-memory"}
|
|
199
|
-
S3_ACCESS_KEY_ID=${e.get("S3_ACCESS_KEY_ID")??""}
|
|
200
|
-
S3_SECRET_ACCESS_KEY=${e.get("S3_SECRET_ACCESS_KEY")??""}
|
|
201
|
-
S3_FORCE_PATH_STYLE=${e.get("S3_FORCE_PATH_STYLE")??"false"}
|
|
202
|
-
|
|
203
|
-
# Hugging Face S3 (legacy compatibility)
|
|
204
|
-
HF_NAMESPACE=${e.get("HF_NAMESPACE")??""}
|
|
205
|
-
HF_BUCKET=${e.get("HF_BUCKET")??"toolnet-memory"}
|
|
206
|
-
HF_S3_ACCESS_KEY_ID=${e.get("HF_S3_ACCESS_KEY_ID")??""}
|
|
207
|
-
HF_S3_SECRET_ACCESS_KEY=${e.get("HF_S3_SECRET_ACCESS_KEY")??""}
|
|
208
|
-
|
|
209
|
-
# Embedding
|
|
210
|
-
HF_TOKEN=${e.get("HF_TOKEN")??""}
|
|
211
|
-
HF_EMBEDDING_MODEL=${e.get("HF_EMBEDDING_MODEL")??"sentence-transformers/all-MiniLM-L6-v2"}
|
|
212
|
-
|
|
213
|
-
# Local cache
|
|
214
|
-
MEMORY_LOCAL_STORAGE_PATH=${e.get("MEMORY_LOCAL_STORAGE_PATH")??""}
|
|
215
|
-
MEMORY_LOCAL_CACHE_MB=${e.get("MEMORY_LOCAL_CACHE_MB")??"200"}
|
|
216
|
-
|
|
217
|
-
# Automation
|
|
218
|
-
MEMORY_AUTO_CAPTURE=${e.get("MEMORY_AUTO_CAPTURE")??"true"}
|
|
219
|
-
MEMORY_AUTO_RETRIEVE=${e.get("MEMORY_AUTO_RETRIEVE")??"true"}
|
|
220
|
-
MEMORY_AUTO_SUMMARIZE=${e.get("MEMORY_AUTO_SUMMARIZE")??"true"}
|
|
221
|
-
MEMORY_AUTO_SYNC=${e.get("MEMORY_AUTO_SYNC")??"true"}
|
|
222
|
-
|
|
223
|
-
# Retrieval
|
|
224
|
-
MEMORY_MAX_CANDIDATES=${e.get("MEMORY_MAX_CANDIDATES")??"50"}
|
|
225
|
-
MEMORY_RERANK_TOP=${e.get("MEMORY_RERANK_TOP")??"10"}
|
|
226
|
-
MEMORY_FINAL_CONTEXT=${e.get("MEMORY_FINAL_CONTEXT")??"5"}
|
|
227
|
-
MEMORY_TOKEN_BUDGET=${e.get("MEMORY_TOKEN_BUDGET")??"2000"}
|
|
228
|
-
|
|
229
|
-
# Automatic Session Memory
|
|
230
|
-
TOOLNET_SESSION_LEARNING=${e.get("TOOLNET_SESSION_LEARNING")??"1"}
|
|
231
|
-
TOOLNET_WORK_CONTINUITY=${e.get("TOOLNET_WORK_CONTINUITY")??"1"}
|
|
232
|
-
TOOLNET_SEMANTIC_CONTINUITY=${e.get("TOOLNET_SEMANTIC_CONTINUITY")??"1"}
|
|
233
|
-
TOOLNET_SMART_HANDOFF=${e.get("TOOLNET_SMART_HANDOFF")??"1"}
|
|
234
|
-
`;m.writeFileSync(_,o,{encoding:"utf8",mode:384}),m.chmodSync(x,448),m.chmodSync(_,384)}function G(e,t=!0){let o=e.trim().toLowerCase();return o?o==="y"||o==="yes":t}async function Ne(e){return d.isTTY?(S.write(e),new Promise(t=>{let o="",r=()=>{d.off("data",n),d.setRawMode?.(!1),d.pause(),S.write(`
|
|
235
|
-
`),t(o)},n=s=>{for(let i of s.toString("utf8")){if(i==="\r"||i===`
|
|
236
|
-
`){r();return}if(i===""&&(d.setRawMode?.(!1),S.write(`
|
|
237
|
-
`),process.exit(130)),i==="\x7F"){o=o.slice(0,-1);continue}o+=i}};d.resume(),d.setRawMode?.(!0),d.on("data",n)})):""}function M(){try{let t=I().filter(o=>o.installed);if(t.length>0){console.log(""),console.log("Automatic AI memory:");for(let o of t){let r=o.agent==="agy"?"Agy / Antigravity":o.agent==="opencode"?"OpenCode":"Codex";console.log(` \u2713 ${r}`)}}}catch{}}function u(e,t,o,r){o.trim()?e.set(t,o.trim()):!e.get(t)&&r!==void 0&&e.set(t,r)}async function he(e,t){console.log("Storage provider:"),console.log(" 1. Cloudflare R2 (recommended)"),console.log(" 2. S3 / S3-compatible"),console.log(" 3. Local"),console.log(" 4. Hugging Face S3 (legacy)"),console.log("");let o=t==="s3"?"2":t==="local"?"3":t==="huggingface"?"4":"1",n=(await e.question(`Choose [${o}]: `)).trim()||o;return n==="2"?"s3":n==="3"?"local":n==="4"?"huggingface":"r2"}async function Ie(){let e=m.existsSync(_),t=e?Re(m.readFileSync(_,"utf8")):new Map,o=J(t),r=B(o),n=r.every(c=>!!t.get(c)?.trim());if(console.log(""),console.log("TOOLNET MEMORY SETUP"),console.log("===================="),console.log(""),console.log(`Config: ${_}`),console.log(""),!d.isTTY||!S.isTTY){e||(t.set("MEMORY_STORAGE_PROVIDER",o),j(t)),n?console.log(`\u2713 ${y(o)} already configured`):(console.log("Configuration pending."),console.log("Run:"),console.log(" toolnet-memory setup")),M();return}let s=Ae.createInterface({input:d,output:S});if(n){console.log(`\u2713 ${y(o)} already configured`),console.log("");let c=await s.question("Use existing storage configuration? (Y/n) [Y]: ");if(G(c)){s.close(),console.log(""),console.log(`\u2713 Existing ${y(o)} configuration kept`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor");return}console.log("")}o=await he(s,o),t.set("MEMORY_STORAGE_PROVIDER",o),console.log("");let i="",a="";if(o==="r2"){let c=await s.question(t.get("R2_ACCOUNT_ID")?`Cloudflare Account ID [${t.get("R2_ACCOUNT_ID")}]: `:"Cloudflare Account ID: "),g=await s.question(`R2 Bucket [${t.get("R2_BUCKET")||"toolnet-memory"}]: `),E=await s.question(t.get("R2_ACCESS_KEY_ID")?"R2 Access Key ID [configured]: ":"R2 Access Key ID: ");u(t,"R2_ACCOUNT_ID",c),u(t,"R2_BUCKET",g,"toolnet-memory"),u(t,"R2_ACCESS_KEY_ID",E),i="R2_SECRET_ACCESS_KEY",a=t.get(i)?"R2 Secret Access Key [configured]: ":"R2 Secret Access Key: "}else if(o==="s3"){let c=await s.question(t.get("S3_ENDPOINT")?`S3 Endpoint [${t.get("S3_ENDPOINT")}]: `:"S3 Endpoint [blank = AWS S3]: "),g=await s.question(`S3 Region [${t.get("S3_REGION")||"us-east-1"}]: `),E=await s.question(`S3 Bucket [${t.get("S3_BUCKET")||"toolnet-memory"}]: `),l=await s.question(t.get("S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: "),F=await s.question(`Force path-style? (y/N) [${t.get("S3_FORCE_PATH_STYLE")==="true"?"Y":"N"}]: `);c.trim()&&t.set("S3_ENDPOINT",c.trim()),u(t,"S3_REGION",g,"us-east-1"),u(t,"S3_BUCKET",E,"toolnet-memory"),u(t,"S3_ACCESS_KEY_ID",l),F.trim()?t.set("S3_FORCE_PATH_STYLE",G(F,!1)?"true":"false"):t.get("S3_FORCE_PATH_STYLE")||t.set("S3_FORCE_PATH_STYLE","false"),i="S3_SECRET_ACCESS_KEY",a=t.get(i)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}else if(o==="huggingface"){let c=await s.question(t.get("HF_NAMESPACE")?`Hugging Face namespace [${t.get("HF_NAMESPACE")}]: `:"Hugging Face namespace: "),g=await s.question(`Bucket [${t.get("HF_BUCKET")||"toolnet-memory"}]: `),E=await s.question(t.get("HF_S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: ");u(t,"HF_NAMESPACE",c),u(t,"HF_BUCKET",g,"toolnet-memory"),u(t,"HF_S3_ACCESS_KEY_ID",E),i="HF_S3_SECRET_ACCESS_KEY",a=t.get(i)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}if(s.close(),i){let c=await Ne(a);c.trim()&&t.set(i,c.trim())}j(t),r=B(o);let f=r.filter(c=>!t.get(c)?.trim());if(console.log(""),console.log("\u2713 Configuration saved"),console.log(` ${_}`),console.log(""),f.length){console.log("Missing configuration:");for(let c of f)console.log(` - ${c}`);console.log(""),console.log("Run setup again:"),console.log(" toolnet-memory setup");return}console.log(`\u2713 ${y(o)} configuration complete`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor")}Ie().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1)});
|
|
650
|
+
`;return ne(o,r.trimStart(),{encoding:"utf8",mode:384}),o}import{existsSync as be,mkdirSync as ie,readFileSync as Ye,writeFileSync as se}from"node:fs";import{homedir as ae}from"node:os";import{dirname as ce,join as x}from"node:path";function we(e){let t=[],o=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,n;for(;n=o.exec(e);){let r=n[1]??n[2]??"";try{t.push(n[1]!==void 0?JSON.parse(`"${r}"`):r)}catch{t.push(r)}}return t}function le(e={}){let t=e.configFile??x(process.env.CODEX_HOME??x(ae(),".codex"),"config.toml"),o=e.previousFile??x(ae(),".config","toolnet-memory","codex-notify-previous.json");ie(ce(t),{recursive:!0}),ie(ce(o),{recursive:!0});let n=be(t)?Ye(t,"utf8"):"",r=e.binary??"toolnet-memory",s=`notify = [${JSON.stringify(r)}, "session:codex-notify"]`,a=n.split(`
|
|
651
|
+
`),l=a.findIndex(u=>/^\s*\[/.test(u));l<0&&(l=a.length);let c=-1,d=-1;for(let u=0;u<l;u+=1)if(/^\s*notify\s*=/.test(a[u])){if(c=u,d=u,a[u].includes("[")&&!a[u].includes("]"))for(;d+1<l&&(d+=1,!a[d].includes("]")););break}let E=[];if(c>=0){let u=a.slice(c,d+1).join(`
|
|
652
|
+
`);E=we(u),a.splice(c,d-c+1,s)}else l=a.findIndex(u=>/^\s*\[/.test(u)),l<0&&(l=a.length),a.splice(l,0,s);let y=E.length>=2&&E[E.length-1]==="session:codex-notify";return E.length>0&&!y&&se(o,JSON.stringify(E,null,2)+`
|
|
653
|
+
`,{encoding:"utf8",mode:384}),n=a.join(`
|
|
654
|
+
`),n.endsWith(`
|
|
655
|
+
`)||(n+=`
|
|
656
|
+
`),se(t,n,{encoding:"utf8",mode:384}),{configFile:t,previousFile:o,preservedPrevious:E.length>0&&!y}}import{existsSync as ke,mkdirSync as Be,readFileSync as xe,writeFileSync as $e}from"node:fs";import{homedir as Fe}from"node:os";import{dirname as ve,join as de}from"node:path";function He(e){return`'${e.replace(/'/g,"'\\''")}'`}function ge(e={}){let t=e.hooksFile??de(process.env.CODEX_HOME??de(Fe(),".codex"),"hooks.json");Be(ve(t),{recursive:!0});let o={};if(ke(t))try{o=JSON.parse(xe(t,"utf8"))}catch(l){throw new Error(`Invalid existing Codex hooks.json: ${l instanceof Error?l.message:String(l)}`)}let n=o.hooks&&typeof o.hooks=="object"&&!Array.isArray(o.hooks)?o.hooks:{};o.hooks=n;let s=(Array.isArray(n.SessionStart)?n.SessionStart:[]).filter(l=>{try{return!JSON.stringify(l).includes("session:codex-context")}catch{return!0}}),a=e.binary??"toolnet-memory";return s.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${He(a)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),n.SessionStart=s,$e(t,JSON.stringify(o,null,2)+`
|
|
657
|
+
`,{encoding:"utf8",mode:384}),t}function H(e){return Ge("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function qe(){return H("agy")||$(v(F(),".gemini"))}function je(){return H("opencode")||$(v(F(),".config","opencode"))}function Ve(){return H("codex")||$(process.env.CODEX_HOME??v(F(),".codex"))}function G(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=[];if(!(e.force===!0||qe()))o.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let r=ee({binary:t});o.push({agent:"agy",detected:!0,installed:!0,targets:[r]})}catch(r){o.push({agent:"agy",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||je()))o.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let r=re({binary:t});o.push({agent:"opencode",detected:!0,installed:!0,targets:[r]})}catch(r){o.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||Ve()))o.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let r=le({binary:t}),s=ge({binary:t}),a=[r.configFile,s];r.preservedPrevious&&a.push(r.previousFile),o.push({agent:"codex",detected:!0,installed:!0,targets:a})}catch(r){o.push({agent:"codex",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}return o}function Je(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let t of e){let o=t.agent==="agy"?"Agy / Antigravity":t.agent==="opencode"?"OpenCode":"Codex";if(!t.detected){console.log(`- ${o}: not detected`);continue}if(t.installed){console.log(`\u2713 ${o}: automatic memory enabled`);continue}console.log(`\u2717 ${o}: integration failed`),t.error&&console.log(` ${t.error}`)}console.log("")}async function We(){let e=process.argv.slice(2),t=e.includes("--all"),o=e.includes("--json"),n=G({force:t});if(o){console.log(JSON.stringify(n,null,2));return}Je(n)}var Xe=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Xe&&We().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});var q=R.join(j.homedir(),".config","toolnet-memory"),_=R.join(q,".env"),et=new Set(["MEMORY_STORAGE_PROVIDER","R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY","S3_ENDPOINT","S3_REGION","S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY","S3_FORCE_PATH_STYLE","HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY","HF_URL","HF_TOKEN","HF_EMBEDDING_MODEL","TOOLNET_LLM_PROVIDER","TOOLNET_LLM_API_KEY","TOOLNET_LLM_BASE_URL","TOOLNET_LLM_MODEL","TOOLNET_LLM_ACCOUNT_ID","TOOLNET_EMBEDDING_PROVIDER","TOOLNET_EMBEDDING_API_KEY","TOOLNET_EMBEDDING_BASE_URL","TOOLNET_EMBEDDING_MODEL","TOOLNET_EMBEDDING_ACCOUNT_ID","MEMORY_LOCAL_STORAGE_PATH","MEMORY_LOCAL_CACHE_MB","MEMORY_AUTO_CAPTURE","MEMORY_AUTO_RETRIEVE","MEMORY_AUTO_SUMMARIZE","MEMORY_AUTO_SYNC","MEMORY_MAX_CANDIDATES","MEMORY_RERANK_TOP","MEMORY_FINAL_CONTEXT","MEMORY_TOKEN_BUDGET","TOOLNET_SESSION_LEARNING","TOOLNET_WORK_CONTINUITY","TOOLNET_SEMANTIC_CONTINUITY","TOOLNET_SMART_HANDOFF"]);function tt(e){let t=new Map;for(let o of e.split(/\r?\n/)){let n=o.trim();if(!n||n.startsWith("#"))continue;let r=n.indexOf("=");if(r===-1)continue;let s=n.slice(0,r).trim(),a=n.slice(r+1).trim();s&&t.set(s,a)}return t}function T(e){let t=e.get("MEMORY_STORAGE_PROVIDER")?.trim();return t==="r2"||t==="s3"||t==="local"||t==="huggingface"?t:e.get("R2_ACCOUNT_ID")&&e.get("R2_BUCKET")?"r2":e.get("S3_BUCKET")?"s3":e.get("HF_BUCKET")&&e.get("HF_S3_ACCESS_KEY_ID")?"huggingface":e.get("MEMORY_LOCAL_STORAGE_PATH")?"local":"r2"}function M(e){switch(e){case"r2":return"Cloudflare R2";case"s3":return"S3 / S3-compatible";case"huggingface":return"Hugging Face S3";case"local":return"Local";default:return e}}function Ee(e){switch(e){case"r2":return["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"];case"s3":return["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"];case"huggingface":return["HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"];case"local":return[];default:return[]}}function ue(e){let t=T(e);return t==="local"?!0:Ee(t).every(o=>!!e.get(o)?.trim())}function V(e){return e?e.length<=8?"configured":`${e.slice(0,4)}\u2022\u2022\u2022\u2022${e.slice(-3)}`:"not configured"}function ot(e){let o=["# ==========================================================","# TOOLNET MEMORY","# Generated by: toolnet-memory setup","# Do not commit this file.","# ==========================================================","",`MEMORY_STORAGE_PROVIDER=${T(e)}`,"","# ----------------------------------------------------------","# Cloudflare R2","# ----------------------------------------------------------",`R2_ACCOUNT_ID=${e.get("R2_ACCOUNT_ID")??""}`,`R2_BUCKET=${e.get("R2_BUCKET")??"toolnet-memory"}`,`R2_ACCESS_KEY_ID=${e.get("R2_ACCESS_KEY_ID")??""}`,`R2_SECRET_ACCESS_KEY=${e.get("R2_SECRET_ACCESS_KEY")??""}`,"","# ----------------------------------------------------------","# Generic S3 / S3-compatible","# ----------------------------------------------------------",`S3_ENDPOINT=${e.get("S3_ENDPOINT")??""}`,`S3_REGION=${e.get("S3_REGION")??"us-east-1"}`,`S3_BUCKET=${e.get("S3_BUCKET")??"toolnet-memory"}`,`S3_ACCESS_KEY_ID=${e.get("S3_ACCESS_KEY_ID")??""}`,`S3_SECRET_ACCESS_KEY=${e.get("S3_SECRET_ACCESS_KEY")??""}`,`S3_FORCE_PATH_STYLE=${e.get("S3_FORCE_PATH_STYLE")??"false"}`,"","# ----------------------------------------------------------","# Hugging Face S3","# ----------------------------------------------------------",`HF_NAMESPACE=${e.get("HF_NAMESPACE")??""}`,`HF_BUCKET=${e.get("HF_BUCKET")??"toolnet-memory"}`,`HF_S3_ACCESS_KEY_ID=${e.get("HF_S3_ACCESS_KEY_ID")??""}`,`HF_S3_SECRET_ACCESS_KEY=${e.get("HF_S3_SECRET_ACCESS_KEY")??""}`,`HF_URL=${e.get("HF_URL")??""}`,"","# ----------------------------------------------------------","# AI / LLM - canonical ToolNet configuration","# ----------------------------------------------------------",`TOOLNET_LLM_PROVIDER=${e.get("TOOLNET_LLM_PROVIDER")??""}`,`TOOLNET_LLM_API_KEY=${e.get("TOOLNET_LLM_API_KEY")??""}`,`TOOLNET_LLM_BASE_URL=${e.get("TOOLNET_LLM_BASE_URL")??""}`,`TOOLNET_LLM_MODEL=${e.get("TOOLNET_LLM_MODEL")??""}`,`TOOLNET_LLM_ACCOUNT_ID=${e.get("TOOLNET_LLM_ACCOUNT_ID")??""}`,"","# ----------------------------------------------------------","# Embedding - canonical ToolNet configuration","# ----------------------------------------------------------",`TOOLNET_EMBEDDING_PROVIDER=${e.get("TOOLNET_EMBEDDING_PROVIDER")??""}`,`TOOLNET_EMBEDDING_API_KEY=${e.get("TOOLNET_EMBEDDING_API_KEY")??""}`,`TOOLNET_EMBEDDING_BASE_URL=${e.get("TOOLNET_EMBEDDING_BASE_URL")??""}`,`TOOLNET_EMBEDDING_MODEL=${e.get("TOOLNET_EMBEDDING_MODEL")??""}`,`TOOLNET_EMBEDDING_ACCOUNT_ID=${e.get("TOOLNET_EMBEDDING_ACCOUNT_ID")??""}`,"","# ----------------------------------------------------------","# Embedding - legacy/current compatibility","# ----------------------------------------------------------",`HF_TOKEN=${e.get("HF_TOKEN")??""}`,`HF_EMBEDDING_MODEL=${e.get("HF_EMBEDDING_MODEL")??"sentence-transformers/all-MiniLM-L6-v2"}`,"","# ----------------------------------------------------------","# Local storage/cache","# ----------------------------------------------------------",`MEMORY_LOCAL_STORAGE_PATH=${e.get("MEMORY_LOCAL_STORAGE_PATH")??""}`,`MEMORY_LOCAL_CACHE_MB=${e.get("MEMORY_LOCAL_CACHE_MB")??"200"}`,"","# ----------------------------------------------------------","# Automation","# ----------------------------------------------------------",`MEMORY_AUTO_CAPTURE=${e.get("MEMORY_AUTO_CAPTURE")??"true"}`,`MEMORY_AUTO_RETRIEVE=${e.get("MEMORY_AUTO_RETRIEVE")??"true"}`,`MEMORY_AUTO_SUMMARIZE=${e.get("MEMORY_AUTO_SUMMARIZE")??"true"}`,`MEMORY_AUTO_SYNC=${e.get("MEMORY_AUTO_SYNC")??"true"}`,"","# ----------------------------------------------------------","# Retrieval","# ----------------------------------------------------------",`MEMORY_MAX_CANDIDATES=${e.get("MEMORY_MAX_CANDIDATES")??"50"}`,`MEMORY_RERANK_TOP=${e.get("MEMORY_RERANK_TOP")??"10"}`,`MEMORY_FINAL_CONTEXT=${e.get("MEMORY_FINAL_CONTEXT")??"5"}`,`MEMORY_TOKEN_BUDGET=${e.get("MEMORY_TOKEN_BUDGET")??"2000"}`,"","# ----------------------------------------------------------","# Automatic Session Memory","# ----------------------------------------------------------",`TOOLNET_SESSION_LEARNING=${e.get("TOOLNET_SESSION_LEARNING")??"1"}`,`TOOLNET_WORK_CONTINUITY=${e.get("TOOLNET_WORK_CONTINUITY")??"1"}`,`TOOLNET_SEMANTIC_CONTINUITY=${e.get("TOOLNET_SEMANTIC_CONTINUITY")??"1"}`,`TOOLNET_SMART_HANDOFF=${e.get("TOOLNET_SMART_HANDOFF")??"1"}`],n=[...e.entries()].filter(([r])=>!et.has(r));if(n.length>0){o.push("","# ----------------------------------------------------------","# Preserved settings","# ----------------------------------------------------------");for(let[r,s]of n)o.push(`${r}=${s}`)}return`${o.join(`
|
|
658
|
+
`)}
|
|
659
|
+
`}function O(e){m.mkdirSync(q,{recursive:!0,mode:448});let t=`${_}.tmp-${process.pid}`;m.writeFileSync(t,ot(e),{encoding:"utf8",mode:384}),m.renameSync(t,_),m.chmodSync(q,448),m.chmodSync(_,384)}function p(e,t,o,n){let r=o.trim();if(r){e.set(t,r);return}!e.get(t)&&n!==void 0&&e.set(t,n)}async function Y(e,t){return f.isTTY?(e.pause(),I.write(t),new Promise(o=>{let n="",r=!1,s=()=>{r||(r=!0,f.off("data",a),f.setRawMode?.(!1),f.pause(),I.write(`
|
|
660
|
+
`),e.resume(),o(n))},a=l=>{for(let c of l.toString("utf8")){if(c==="\r"||c===`
|
|
661
|
+
`){s();return}if(c===""&&(f.off("data",a),f.setRawMode?.(!1),I.write(`
|
|
662
|
+
`),process.exit(130)),c==="\x7F"){n=n.slice(0,-1);continue}n+=c}};f.resume(),f.setRawMode?.(!0),f.on("data",a)})):""}function L(e){return e.trim().replace(/\/+$/,"")}function _e(e){return`https://${e}.r2.cloudflarestorage.com`}async function J(e){let t=new AbortController,o=setTimeout(()=>t.abort(),15e3);o.unref?.();let n=new Qe({region:e.region,endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}});try{return await n.send(new Ze({Bucket:e.bucket}),{abortSignal:t.signal}),{ok:!0,message:`Bucket "${e.bucket}" reachable`}}catch(r){return{ok:!1,message:r instanceof Error?r.message:String(r)}}finally{clearTimeout(o),n.destroy()}}async function nt(e){let t=e.get("R2_ACCOUNT_ID")?.trim()??"",o=e.get("R2_BUCKET")?.trim()??"",n=e.get("R2_ACCESS_KEY_ID")?.trim()??"",r=e.get("R2_SECRET_ACCESS_KEY")?.trim()??"";return!t||!o||!n||!r?{ok:!1,message:"R2 configuration is incomplete"}:J({endpoint:_e(t),region:"auto",bucket:o,accessKeyId:n,secretAccessKey:r})}async function rt(e){let t=L(e.get("S3_ENDPOINT")??""),o=e.get("S3_REGION")?.trim()||"us-east-1",n=e.get("S3_BUCKET")?.trim()??"",r=e.get("S3_ACCESS_KEY_ID")?.trim()??"",s=e.get("S3_SECRET_ACCESS_KEY")?.trim()??"";return!n||!r||!s?{ok:!1,message:"S3 configuration is incomplete"}:J({endpoint:t||void 0,region:o,bucket:n,accessKeyId:r,secretAccessKey:s,forcePathStyle:e.get("S3_FORCE_PATH_STYLE")==="true"})}async function it(e){let t=L(e.get("HF_URL")??""),o=e.get("HF_BUCKET")?.trim()??"",n=e.get("HF_S3_ACCESS_KEY_ID")?.trim()??"",r=e.get("HF_S3_SECRET_ACCESS_KEY")?.trim()??"";return t?!o||!n||!r?{ok:!1,message:"Hugging Face S3 configuration is incomplete"}:J({endpoint:t,region:"us-east-1",bucket:o,accessKeyId:n,secretAccessKey:r,forcePathStyle:!0}):{ok:!1,message:"HF_URL / S3 endpoint is required for connection test"}}async function st(e){let t=e.get("MEMORY_LOCAL_STORAGE_PATH")?.trim()||R.join(j.homedir(),".local","share","toolnet-memory"),o=R.join(t,`.toolnet-test-${process.pid}-${Date.now()}`);try{return m.mkdirSync(t,{recursive:!0,mode:448}),m.writeFileSync(o,`toolnet-memory
|
|
663
|
+
`,{encoding:"utf8",mode:384}),m.unlinkSync(o),{ok:!0,message:`Writable: ${t}`}}catch(n){return{ok:!1,message:n instanceof Error?n.message:String(n)}}}async function at(e,t){switch(e){case"r2":return nt(t);case"s3":return rt(t);case"huggingface":return it(t);case"local":return st(t)}}async function ct(e,t){console.log(""),console.log(`Testing ${M(e)}...`);let o=await at(e,t);return o.ok?console.log(`\u2713 ${o.message}`):(console.log("\u2717 Connection test failed"),console.log(` ${o.message}`)),console.log(""),o}function lt(){console.log(""),console.log("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.log("\u2551 TOOLNET MEMORY SETUP \u2551"),console.log("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),console.log("")}function dt(e){let t=T(e);console.log("Current configuration"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(`Storage : ${M(t)} ${ue(e)?"\u2713":"\u26A0 incomplete"}`);let o=e.get("TOOLNET_LLM_PROVIDER"),n=e.get("TOOLNET_LLM_MODEL"),r=e.get("TOOLNET_EMBEDDING_PROVIDER");console.log(`LLM : ${o?`${fe(o)}${n?` / ${n}`:""}`:"not configured"}`),console.log(`Embedding: ${r||"legacy/default"}`),console.log("")}async function gt(e){console.log("Setup"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(" 1. Storage"),console.log(" 2. AI Model"),console.log(" 3. Finish & Save"),console.log(" 0. Exit without saving"),console.log("");let t=(await e.question("Choose [1-3]: ")).trim();return t==="1"?"storage":t==="2"?"ai":t==="0"?"exit":"finish"}async function Et(e,t){console.log(""),console.log("Storage"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(` 1. Cloudflare R2${t==="r2"?" \u2713 current":""}`),console.log(` 2. S3 / S3-compatible${t==="s3"?" \u2713 current":""}`),console.log(` 3. Hugging Face S3${t==="huggingface"?" \u2713 current":""}`),console.log(` 4. Local${t==="local"?" \u2713 current":""}`),console.log(""),console.log(" 0. Back"),console.log("");let o=(await e.question("Choose storage: ")).trim();return o==="0"?"back":o==="2"?"s3":o==="3"?"huggingface":o==="4"?"local":"r2"}async function ut(e,t){console.log(""),console.log("Cloudflare R2"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let o=await e.question(t.get("R2_ACCOUNT_ID")?`ACCOUNT ID [${t.get("R2_ACCOUNT_ID")}]: `:"ACCOUNT ID: "),n=await e.question(t.get("R2_ACCESS_KEY_ID")?`ACCESS KEY ID [${V(t.get("R2_ACCESS_KEY_ID"))}]: `:"ACCESS KEY ID: "),r=await e.question(`BUCKET [${t.get("R2_BUCKET")||"toolnet-memory"}]: `);p(t,"R2_ACCOUNT_ID",o),p(t,"R2_ACCESS_KEY_ID",n),p(t,"R2_BUCKET",r,"toolnet-memory");let s=t.get("R2_ACCOUNT_ID")?.trim();s&&console.log(`URL: ${_e(s)}`);let a=await Y(e,t.get("R2_SECRET_ACCESS_KEY")?"SECRET ACCESS KEY [configured, Enter = keep]: ":"SECRET ACCESS KEY: ");a.trim()&&t.set("R2_SECRET_ACCESS_KEY",a.trim())}async function _t(e,t){console.log(""),console.log("S3 / S3-compatible"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let o=await e.question(t.get("S3_ENDPOINT")?`URL / ENDPOINT [${t.get("S3_ENDPOINT")}]: `:"URL / ENDPOINT [blank = AWS S3]: "),n=await e.question(`REGION [${t.get("S3_REGION")||"us-east-1"}]: `),r=await e.question(t.get("S3_ACCESS_KEY_ID")?`ACCESS KEY ID [${V(t.get("S3_ACCESS_KEY_ID"))}]: `:"ACCESS KEY ID: "),s=await e.question(`BUCKET [${t.get("S3_BUCKET")||"toolnet-memory"}]: `),a=t.get("S3_FORCE_PATH_STYLE")==="true",l=await e.question(`FORCE PATH STYLE [${a?"Y":"N"}] (y/n): `);if(o.trim()&&t.set("S3_ENDPOINT",L(o)),p(t,"S3_REGION",n,"us-east-1"),p(t,"S3_ACCESS_KEY_ID",r),p(t,"S3_BUCKET",s,"toolnet-memory"),l.trim()){let d=l.trim().toLowerCase();t.set("S3_FORCE_PATH_STYLE",d==="y"||d==="yes"?"true":"false")}else t.has("S3_FORCE_PATH_STYLE")||t.set("S3_FORCE_PATH_STYLE","false");let c=await Y(e,t.get("S3_SECRET_ACCESS_KEY")?"SECRET ACCESS KEY [configured, Enter = keep]: ":"SECRET ACCESS KEY: ");c.trim()&&t.set("S3_SECRET_ACCESS_KEY",c.trim())}async function ft(e,t){console.log(""),console.log("Hugging Face S3"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let o=await e.question(t.get("HF_NAMESPACE")?`NAMESPACE [${t.get("HF_NAMESPACE")}]: `:"NAMESPACE [optional]: "),n=await e.question(t.get("HF_S3_ACCESS_KEY_ID")?`ACCESS KEY ID [${V(t.get("HF_S3_ACCESS_KEY_ID"))}]: `:"ACCESS KEY ID: "),r=await e.question(`BUCKET [${t.get("HF_BUCKET")||"toolnet-memory"}]: `),s=await e.question(t.get("HF_URL")?`URL [${t.get("HF_URL")}]: `:"URL / S3 ENDPOINT: ");p(t,"HF_NAMESPACE",o),p(t,"HF_S3_ACCESS_KEY_ID",n),p(t,"HF_BUCKET",r,"toolnet-memory"),s.trim()&&t.set("HF_URL",L(s));let a=await Y(e,t.get("HF_S3_SECRET_ACCESS_KEY")?"SECRET ACCESS KEY [configured, Enter = keep]: ":"SECRET ACCESS KEY: ");a.trim()&&t.set("HF_S3_SECRET_ACCESS_KEY",a.trim())}async function mt(e,t){console.log(""),console.log("Local Storage"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let o=t.get("MEMORY_LOCAL_STORAGE_PATH")??R.join(j.homedir(),".local","share","toolnet-memory"),n=await e.question(`LOCAL PATH [${o}]: `);p(t,"MEMORY_LOCAL_STORAGE_PATH",n,o)}async function pt(e,t){let o=new Map(t);for(;;){let n=await Et(e,T(t));if(n==="back")return;let r=new Map(t);t.set("MEMORY_STORAGE_PROVIDER",n),n==="r2"?await ut(e,t):n==="s3"?await _t(e,t):n==="huggingface"?await ft(e,t):await mt(e,t);let s=Ee(n).filter(c=>!t.get(c)?.trim());if(s.length){console.log(""),console.log("\u26A0 Missing required fields:");for(let d of s)console.log(` - ${d}`);console.log(""),console.log(" 1. Retry"),console.log(" 2. Save anyway"),console.log(" 3. Choose another provider"),console.log(" 4. Cancel"),console.log("");let c=(await e.question("Choose [1]: ")).trim()||"1";if(c==="2"){O(t),console.log(""),console.log("\u26A0 Saved with incomplete configuration"),console.log("");return}if(c==="3"){t.clear();for(let[d,E]of r)t.set(d,E);continue}if(c==="4"){t.clear();for(let[d,E]of o)t.set(d,E);console.log(""),console.log("Storage changes cancelled."),console.log("");return}continue}if((await ct(n,t)).ok){O(t),console.log(`\u2713 ${M(n)} configuration saved`),console.log(` ${_}`),console.log("");return}console.log(" 1. Retry"),console.log(" 2. Save anyway"),console.log(" 3. Choose another provider"),console.log(" 4. Cancel"),console.log("");let l=(await e.question("Choose [1]: ")).trim()||"1";if(l==="2"){O(t),console.log(""),console.log("\u26A0 Saved even though connection test failed"),console.log("");return}if(l==="3"){t.clear();for(let[c,d]of r)t.set(c,d);continue}if(l==="4"){t.clear();for(let[c,d]of o)t.set(c,d);console.log(""),console.log("Storage changes cancelled."),console.log("");return}}}var C=[{id:"openai-compatible",label:"OpenAI-compatible",apiKeyRequired:!0,baseUrlRequired:!0},{id:"alibaba",label:"Alibaba / DashScope",baseUrl:"https://dashscope-intl.aliyuncs.com/compatible-mode/v1",suggestedModel:"qwen3.6-flash",apiKeyRequired:!0},{id:"openrouter",label:"OpenRouter",baseUrl:"https://openrouter.ai/api/v1",apiKeyRequired:!0},{id:"groq",label:"Groq",baseUrl:"https://api.groq.com/openai/v1",apiKeyRequired:!0},{id:"deepseek",label:"DeepSeek",baseUrl:"https://api.deepseek.com",suggestedModel:"deepseek-v4-flash",apiKeyRequired:!0},{id:"nvidia",label:"NVIDIA NIM",baseUrl:"https://integrate.api.nvidia.com/v1",suggestedModel:"deepseek-ai/deepseek-v4-pro",apiKeyRequired:!0},{id:"gemini",label:"Gemini",baseUrl:"https://generativelanguage.googleapis.com/v1beta",suggestedModel:"gemini-3.6-flash",apiKeyRequired:!0},{id:"huggingface",label:"Hugging Face",baseUrl:"https://router.huggingface.co/v1",apiKeyRequired:!0},{id:"ollama",label:"Ollama / Local",baseUrl:"http://127.0.0.1:11434/v1",apiKeyRequired:!1},{id:"custom",label:"Custom endpoint",apiKeyRequired:!1,baseUrlRequired:!0},{id:"cloudflare",label:"Cloudflare Workers AI",suggestedModel:"@cf/meta/llama-3.1-8b-instruct",apiKeyRequired:!0,accountIdRequired:!0}];function Ot(e){return C.find(t=>t.id===e)}function fe(e){return Ot(e)?.label??e??"not configured"}function me(e){let t=e.get("TOOLNET_LLM_PROVIDER")?.trim();return C.some(o=>o.id===t)?t:void 0}async function pe(e,t){console.log(""),console.log("AI Model"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),C.forEach((r,s)=>{console.log(` ${s+1}. ${r.label}${t===r.id?" \u2713 current":""}`)}),console.log(""),console.log(" 0. Back"),console.log("");let o=(await e.question("Choose provider: ")).trim();if(o==="0")return"back";let n=Number(o)-1;return Number.isInteger(n)&&n>=0&&n<C.length?C[n].id:(console.log(""),console.log("\u26A0 Invalid provider selection"),pe(e,t))}function Tt(e,t){return!(!e.get("TOOLNET_LLM_MODEL")?.trim()||t.apiKeyRequired&&!e.get("TOOLNET_LLM_API_KEY")?.trim()||t.accountIdRequired&&!e.get("TOOLNET_LLM_ACCOUNT_ID")?.trim()||t.baseUrlRequired&&!e.get("TOOLNET_LLM_BASE_URL")?.trim())}function At(e){e.delete("TOOLNET_LLM_API_KEY"),e.delete("TOOLNET_LLM_BASE_URL"),e.delete("TOOLNET_LLM_MODEL"),e.delete("TOOLNET_LLM_ACCOUNT_ID")}async function St(e,t,o){console.log(""),console.log(o.label),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let n=t.get("TOOLNET_LLM_PROVIDER");if(n&&n!==o.id&&At(t),t.set("TOOLNET_LLM_PROVIDER",o.id),o.accountIdRequired){let c=t.get("TOOLNET_LLM_ACCOUNT_ID"),d=await e.question(c?`ACCOUNT ID [${c}]: `:"ACCOUNT ID: ");p(t,"TOOLNET_LLM_ACCOUNT_ID",d)}else t.delete("TOOLNET_LLM_ACCOUNT_ID");if(o.apiKeyRequired||o.id==="custom"){let c=t.get("TOOLNET_LLM_API_KEY"),d=await Y(e,c?"API KEY [configured, Enter = keep]: ":o.apiKeyRequired?"API KEY: ":"API KEY [optional]: ");d.trim()&&t.set("TOOLNET_LLM_API_KEY",d.trim())}else t.delete("TOOLNET_LLM_API_KEY");if(o.id!=="cloudflare"){let d=t.get("TOOLNET_LLM_BASE_URL")||o.baseUrl||"",y=(await e.question(d?`BASE URL [${d}]: `:o.baseUrlRequired?"BASE URL: ":"BASE URL [optional]: ")).trim()||d;y?t.set("TOOLNET_LLM_BASE_URL",L(y)):t.delete("TOOLNET_LLM_BASE_URL")}else t.delete("TOOLNET_LLM_BASE_URL");let s=t.get("TOOLNET_LLM_MODEL")||o.suggestedModel||"",l=(await e.question(s?`MODEL [${s}]: `:"MODEL: ")).trim()||s;l&&t.set("TOOLNET_LLM_MODEL",l)}function Ct(e){let t=me(e);if(!t)throw new Error("AI provider is not configured");return{id:t,apiKey:e.get("TOOLNET_LLM_API_KEY")?.trim()||void 0,baseUrl:e.get("TOOLNET_LLM_BASE_URL")?.trim()||void 0,model:e.get("TOOLNET_LLM_MODEL")?.trim()||void 0,accountId:e.get("TOOLNET_LLM_ACCOUNT_ID")?.trim()||void 0}}async function yt(e){let t=Ct(e);console.log(""),console.log(`Testing ${fe(t.id)}...`);try{let n=await Q(t).healthCheck();return n.ok?(console.log(`\u2713 Provider reachable${n.latencyMs?` (${n.latencyMs} ms)`:""}`),console.log(`\u2713 Model: ${n.model??t.model??"configured"}`),console.log(""),{ok:!0,message:n.message}):(console.log("\u2717 AI provider test failed"),console.log(` ${n.message}`),console.log(""),{ok:!1,message:n.message})}catch(o){let n=o instanceof Error?o.message:String(o);return console.log("\u2717 AI provider test failed"),console.log(` ${n}`),console.log(""),{ok:!1,message:n}}}async function It(e,t){let o=new Map(t);for(;;){let n=await pe(e,me(t));if(n==="back")return;let r=C.find(l=>l.id===n);if(!r)continue;if(await St(e,t,r),!Tt(t,r)){console.log(""),console.log("\u26A0 AI configuration is incomplete."),console.log(""),console.log(" 1. Retry"),console.log(" 2. Save anyway"),console.log(" 3. Choose another provider"),console.log(" 4. Cancel"),console.log("");let l=(await e.question("Choose [1]: ")).trim()||"1";if(l==="2"){O(t),console.log(""),console.log("\u26A0 AI configuration saved without validation"),console.log(` ${_}`),console.log("");return}if(l==="3")continue;if(l==="4"){t.clear();for(let[c,d]of o)t.set(c,d);console.log(""),console.log("AI changes cancelled."),console.log("");return}continue}if((await yt(t)).ok){O(t),console.log(`\u2713 ${r.label} configuration saved`),console.log(` ${_}`),console.log("");return}console.log(" 1. Retry"),console.log(" 2. Save anyway"),console.log(" 3. Choose another provider"),console.log(" 4. Cancel"),console.log("");let a=(await e.question("Choose [1]: ")).trim()||"1";if(a==="2"){O(t),console.log(""),console.log("\u26A0 AI configuration saved even though provider test failed"),console.log(` ${_}`),console.log("");return}if(a!=="3"&&a==="4"){t.clear();for(let[l,c]of o)t.set(l,c);console.log(""),console.log("AI changes cancelled."),console.log("");return}}}function Rt(){try{let t=G().filter(o=>o.installed);if(t.length===0)return;console.log(""),console.log("Automatic AI memory");for(let o of t){let n=o.agent==="agy"?"Agy / Antigravity":o.agent==="opencode"?"OpenCode":o.agent==="codex"?"Codex":o.agent;console.log(` \u2713 ${n}`)}}catch{}}function Oe(e){e.has("MEMORY_STORAGE_PROVIDER")||e.set("MEMORY_STORAGE_PROVIDER",T(e)),e.has("MEMORY_LOCAL_CACHE_MB")||e.set("MEMORY_LOCAL_CACHE_MB","200"),e.has("MEMORY_AUTO_CAPTURE")||e.set("MEMORY_AUTO_CAPTURE","true"),e.has("MEMORY_AUTO_RETRIEVE")||e.set("MEMORY_AUTO_RETRIEVE","true"),e.has("MEMORY_AUTO_SUMMARIZE")||e.set("MEMORY_AUTO_SUMMARIZE","true"),e.has("MEMORY_AUTO_SYNC")||e.set("MEMORY_AUTO_SYNC","true"),e.has("MEMORY_MAX_CANDIDATES")||e.set("MEMORY_MAX_CANDIDATES","50"),e.has("MEMORY_RERANK_TOP")||e.set("MEMORY_RERANK_TOP","10"),e.has("MEMORY_FINAL_CONTEXT")||e.set("MEMORY_FINAL_CONTEXT","5"),e.has("MEMORY_TOKEN_BUDGET")||e.set("MEMORY_TOKEN_BUDGET","2000"),e.has("TOOLNET_SESSION_LEARNING")||e.set("TOOLNET_SESSION_LEARNING","1"),e.has("TOOLNET_WORK_CONTINUITY")||e.set("TOOLNET_WORK_CONTINUITY","1"),e.has("TOOLNET_SEMANTIC_CONTINUITY")||e.set("TOOLNET_SEMANTIC_CONTINUITY","1"),e.has("TOOLNET_SMART_HANDOFF")||e.set("TOOLNET_SMART_HANDOFF","1")}async function Mt(e,t){Oe(e),t||O(e),console.log("TOOLNET MEMORY SETUP"),console.log(""),console.log("Interactive terminal not detected."),console.log(`Config: ${_}`),console.log(`Storage: ${M(T(e))}`),console.log(""),console.log("Run from an interactive terminal to configure providers:"),console.log(" toolnet-memory setup")}async function Lt(){let e=m.existsSync(_),t=e?tt(m.readFileSync(_,"utf8")):new Map,o=new Map(t);if(Oe(o),!f.isTTY||!I.isTTY){await Mt(o,e);return}let n=ze.createInterface({input:f,output:I}),r=!1;try{for(;;){lt(),dt(o);let s=await gt(n);if(s==="storage"){let a=JSON.stringify([...o.entries()]);await pt(n,o),r=r||a!==JSON.stringify([...o.entries()]);continue}if(s==="ai"){let a=JSON.stringify([...o.entries()]);await It(n,o),r=r||a!==JSON.stringify([...o.entries()]);continue}if(s==="exit"){if(!r){console.log(""),console.log("No changes made.");return}console.log("");let a=(await n.question("Discard unsaved changes? (y/N): ")).trim().toLowerCase();if(a==="y"||a==="yes"){console.log(""),console.log("Changes discarded.");return}continue}O(o),console.log(""),console.log("\u2713 Configuration saved"),console.log(` ${_}`),console.log(""),console.log(`\u2713 Permissions: ${m.statSync(_).mode.toString(8).slice(-3)}`),console.log(`\u2713 Storage: ${M(T(o))}`),ue(o)||console.log("\u26A0 Storage configuration is incomplete."),Rt(),console.log(""),console.log("Next:"),console.log(" toolnet-memory doctor"),console.log("");return}}finally{n.close()}}Lt().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1)});
|