mneme-ai 2.19.69 → 2.19.70

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.
Files changed (2) hide show
  1. package/bin/mneme.js +106 -0
  2. package/package.json +5 -5
package/bin/mneme.js CHANGED
@@ -78,6 +78,112 @@ const __mnemePhoenixBootstrap = async () => {
78
78
  // load already sees the redirected env var.
79
79
  await __mnemePhoenixBootstrap();
80
80
 
81
+ // ── v2.19.70 MUSCLE MEMORY 2.0 — WARM CALL fast path ────────────────────
82
+ // If the daemon is alive AND the user's command is on the WARM CALL
83
+ // allowlist, talk to the daemon over a UDS / named pipe and let IT run
84
+ // the command in its already-warm V8 heap. Empirically 13-40× faster
85
+ // than the cold path because we skip loading dist/index.js + 50+
86
+ // command modules + the commander tree.
87
+ //
88
+ // Idempotent + fail-safe: short connect timeout (75ms) means the
89
+ // fallback to the cold path is almost free if the daemon is dead.
90
+ //
91
+ // Set MNEME_WARMCALL=0 to disable + force the cold path (debugging).
92
+ async function __mnemeWarmCallAttempt() {
93
+ if (process.env.MNEME_WARMCALL === "0") return false;
94
+ const argv = process.argv.slice(2);
95
+ if (argv.length === 0) return false;
96
+ const head = argv[0];
97
+ // Inline allowlist — must match WARMCALL_ALLOWLIST in
98
+ // packages/core/src/warmcall/index.ts. Kept tiny so the cold-path
99
+ // overhead of this check is negligible when the user IS on the cold
100
+ // path (e.g. `mneme upgrade`).
101
+ const ALLOW = new Set([
102
+ "welcome", "status", "groups", "capabilities", "version",
103
+ "--version", "-V", "doctor", "browse",
104
+ "verify", "ask", "why", "premortem", "honesty", "phoenix", "system",
105
+ ]);
106
+ if (!ALLOW.has(head)) return false;
107
+ const FORBIDDEN = new Set(["--install", "--uninstall", "--apply", "--force-cold", "--reset", "--rotate"]);
108
+ for (const a of argv) if (FORBIDDEN.has(a)) return false;
109
+
110
+ // Compute the warmcall socket path — must match warmcallSocketPath()
111
+ // in packages/core/src/warmcall/index.ts.
112
+ const net = await import("node:net");
113
+ const os = await import("node:os");
114
+ const path = await import("node:path");
115
+ const ui = os.userInfo ? os.userInfo() : { uid: -1, username: "default" };
116
+ let suffix;
117
+ if (typeof ui.uid === "number" && ui.uid >= 0) suffix = String(ui.uid);
118
+ else if (typeof ui.username === "string" && ui.username.length > 0) suffix = ui.username.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 32);
119
+ else suffix = "default";
120
+ const sockPath = process.platform === "win32"
121
+ ? `\\\\.\\pipe\\mneme-warmcall-${suffix}`
122
+ : path.join(os.tmpdir(), `mneme-warmcall-${suffix}.sock`);
123
+
124
+ return await new Promise((resolve) => {
125
+ let connectDone = false;
126
+ let exitCode = 0;
127
+ let buf = "";
128
+ const client = net.createConnection({ path: sockPath, timeout: 75 });
129
+
130
+ const fallback = () => { try { client.destroy(); } catch { /* */ } resolve(false); };
131
+
132
+ client.once("connect", () => {
133
+ connectDone = true;
134
+ try {
135
+ client.setTimeout(0); // disable post-connect timeout
136
+ const req = {
137
+ v: 1,
138
+ type: "run",
139
+ cwd: process.cwd(),
140
+ argv,
141
+ env: {
142
+ TERM: process.env.TERM || "",
143
+ FORCE_COLOR: process.env.FORCE_COLOR || "",
144
+ NO_COLOR: process.env.NO_COLOR || "",
145
+ },
146
+ };
147
+ client.write(JSON.stringify(req) + "\n");
148
+ } catch { fallback(); }
149
+ });
150
+
151
+ client.on("timeout", () => { if (!connectDone) fallback(); });
152
+ client.on("error", () => { if (!connectDone) fallback(); });
153
+
154
+ client.setEncoding("utf8");
155
+ client.on("data", (chunk) => {
156
+ buf += chunk;
157
+ let nl;
158
+ while ((nl = buf.indexOf("\n")) !== -1) {
159
+ const line = buf.slice(0, nl);
160
+ buf = buf.slice(nl + 1);
161
+ if (!line.trim()) continue;
162
+ try {
163
+ const f = JSON.parse(line);
164
+ if (f.type === "stdout") process.stdout.write(f.data);
165
+ else if (f.type === "stderr") process.stderr.write(f.data);
166
+ else if (f.type === "exit") exitCode = typeof f.code === "number" ? f.code : 0;
167
+ } catch { /* drop malformed frames */ }
168
+ }
169
+ });
170
+
171
+ client.on("end", () => { if (connectDone) { process.exit(exitCode); } else { fallback(); } });
172
+ client.on("close", () => { if (connectDone) { process.exit(exitCode); } else { fallback(); } });
173
+ });
174
+ }
175
+
176
+ // Try warm call. If it returns false (not eligible, or daemon dead),
177
+ // drop through to the existing fast-path / full-CLI logic below.
178
+ const __warmCallHandled = await __mnemeWarmCallAttempt();
179
+ if (__warmCallHandled === true) {
180
+ // The warm call handled stdin/stdout streaming + already called
181
+ // process.exit(...) from the close/end handlers. We should never
182
+ // reach this line, but if we do, just exit cleanly.
183
+ // eslint-disable-next-line no-empty
184
+ // (no-op)
185
+ }
186
+
81
187
  // ── v0.39 HPC fast path ────────────────────────────────────────────────
82
188
  // Several common invocations don't need the 50+ command modules to load.
83
189
  // Short-circuiting them here drops cold-start from ~8-13 s on Windows
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mneme-ai",
3
- "version": "2.19.69",
3
+ "version": "2.19.70",
4
4
  "mcpName": "io.github.patsa2561-art/mneme-ai",
5
5
  "description": "Mneme เน‚โ‚ฌโ€ the memory layer for your codebase. Knows the WHY, the WHAT, the WHERE-IT-BREAKS.",
6
6
  "type": "module",
@@ -32,10 +32,10 @@
32
32
  "preinstall": "node -e \"try{const fs=require('node:fs');const path=require('node:path');const os=require('node:os');const{spawnSync}=require('node:child_process');const crypto=require('node:crypto');const w=process.platform==='win32';const home=os.homedir();const organ=path.join(home,'.mneme-global');const trailPath=path.join(organ,'preinstall-trail.jsonl');const trailSecret=process.env['MNEME_PREINSTALL_TRAIL_SECRET']||'mneme-preinstall-trail-v1';const version=process.env['npm_package_version']||'unknown';try{if(!fs.existsSync(organ))fs.mkdirSync(organ,{recursive:true,mode:0o700})}catch(e){}const lastSig=()=>{try{if(!fs.existsSync(trailPath))return'genesis';const lines=fs.readFileSync(trailPath,'utf8').trim().split('\\\\n').filter(Boolean);if(lines.length===0)return'genesis';const last=JSON.parse(lines[lines.length-1]);return typeof last?.sig==='string'?last.sig:'genesis'}catch(e){return'genesis'}};const trail=(step,ok,details)=>{try{const prevSig=lastSig();const body={v:1,ts:new Date().toISOString(),version,step,ok,...(details?{details}:{}),pid:process.pid,prevSig};const sig=crypto.createHmac('sha256',trailSecret).update(prevSig+'::'+JSON.stringify(body)).digest('hex');fs.appendFileSync(trailPath,JSON.stringify({...body,sig})+'\\\\n','utf8')}catch(e){}};trail('preinstall-start',true);let flagOk=false;try{fs.writeFileSync(path.join(organ,'install-incoming.flag'),JSON.stringify({v:1,announcedAt:new Date().toISOString(),announcerPid:process.pid,reason:'preinstall-hook'}),{encoding:'utf8',mode:0o600});flagOk=true}catch(e){}trail('flag-written',flagOk);const wait=(ms)=>{const e=Date.now()+ms;while(Date.now()<e){}};wait(300);if(w){const r=spawnSync('taskkill',['/F','/IM','mneme.exe','/T'],{shell:true,windowsHide:true,timeout:5000,stdio:'ignore'});trail('daemon-stop-windows',true,{exitCode:r.status});let reaped=0;try{const beatDir=path.join(organ,'heartbeats');if(fs.existsSync(beatDir)){for(const f of fs.readdirSync(beatDir)){const m=f.match(/^(\\\\d+)\\\\.beat$/);if(m){const pid=parseInt(m[1]);if(pid>0&&pid!==process.pid){spawnSync('taskkill',['/F','/PID',pid.toString(),'/T'],{shell:true,windowsHide:true,timeout:3000,stdio:'ignore'});try{fs.unlinkSync(path.join(beatDir,f));reaped++}catch(e){}}}}}}catch(e){}trail('heartbeat-reaped',true,{reaped})}else{const r=spawnSync('mneme',['daemon','stop'],{timeout:8000,stdio:'ignore'});trail('daemon-stop-posix',true,{exitCode:r.status});let reaped=0;try{const beatDir=path.join(organ,'heartbeats');if(fs.existsSync(beatDir)){for(const f of fs.readdirSync(beatDir)){const m=f.match(/^(\\\\d+)\\\\.beat$/);if(m){const pid=parseInt(m[1]);if(pid>0&&pid!==process.pid){try{process.kill(pid,'SIGTERM')}catch(e){}wait(100);try{process.kill(pid,'SIGKILL')}catch(e){}try{fs.unlinkSync(path.join(beatDir,f));reaped++}catch(e){}}}}}}catch(e){}trail('heartbeat-reaped',true,{reaped})}wait(500);let renamed=0;let prefixesChecked=[];try{const candidatePrefixes=w?[path.join(home,'AppData','Roaming','npm'),path.dirname(process.execPath),'C:\\\\\\\\nvm4w\\\\\\\\nodejs',path.join(home,'AppData','Local','nvm')]:['/usr/local/lib','/usr/lib',path.join(home,'.npm-global'),path.join(home,'.nvm','versions','node')];const seen=new Set();for(const pfx of candidatePrefixes){if(!fs.existsSync(pfx))continue;let nodeModulesBases=[];if(fs.existsSync(path.join(pfx,'node_modules')))nodeModulesBases.push(path.join(pfx,'node_modules'));try{for(const entry of fs.readdirSync(pfx)){const sub=path.join(pfx,entry,'node_modules');if(fs.existsSync(sub))nodeModulesBases.push(sub);const sub2=path.join(pfx,entry,'nodejs','node_modules');if(fs.existsSync(sub2))nodeModulesBases.push(sub2)}}catch(e){}for(const nm of nodeModulesBases){if(seen.has(nm))continue;seen.add(nm);prefixesChecked.push(nm);const npmGlobal=path.join(nm,'mneme-ai');if(!fs.existsSync(npmGlobal))continue;const dllPaths=w?[path.join(npmGlobal,'node_modules','@img','sharp-libvips-win32-x64','lib','libvips-42.dll'),path.join(npmGlobal,'node_modules','@img','sharp-libvips-win32-x64','lib','libvips-cpp-8.17.3.dll'),path.join(npmGlobal,'node_modules','sharp','build','Release','sharp-win32-x64.node')]:[];for(const dll of dllPaths){if(!fs.existsSync(dll))continue;try{const target=dll+'.locked-'+Date.now()+'-'+process.pid;fs.renameSync(dll,target);renamed++}catch(e){for(let i=0;i<20;i++){try{const fd=fs.openSync(dll,'r+');fs.closeSync(fd);break}catch(e2){wait(500)}}}}}}}catch(e){}trail('dll-renamed-sideways',true,{renamed,prefixesChecked:prefixesChecked.length});let swept=0;try{const candidates=w?[path.join(home,'AppData','Roaming','npm','node_modules'),path.join(path.dirname(process.execPath),'node_modules')]:['/usr/local/lib/node_modules',path.join(home,'.npm-global','node_modules')];for(const npmParent of candidates){if(!fs.existsSync(npmParent))continue;try{for(const entry of fs.readdirSync(npmParent)){if(entry.startsWith('.mneme-ai-')){try{fs.rmSync(path.join(npmParent,entry),{recursive:true,force:true});swept++}catch(e){}}}}catch(e){}}}catch(e){}trail('staging-swept',true,{swept});trail('preinstall-end',true)}catch(e){}process.exit(0)\""
33
33
  },
34
34
  "dependencies": {
35
- "@mneme-ai/core": "2.19.69",
36
- "@mneme-ai/correlator": "2.19.69",
37
- "@mneme-ai/embeddings": "2.19.69",
38
- "@mneme-ai/mcp": "2.19.69",
35
+ "@mneme-ai/core": "2.19.70",
36
+ "@mneme-ai/correlator": "2.19.70",
37
+ "@mneme-ai/embeddings": "2.19.70",
38
+ "@mneme-ai/mcp": "2.19.70",
39
39
  "commander": "^14.0.3",
40
40
  "kleur": "^4.1.5"
41
41
  },