11 lines
5.8 KiB
JavaScript
Executable File
11 lines
5.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
import{stdin as P}from"process";function $(n,t,e){return n==="PreCompact"?t?{continue:!0,suppressOutput:!0}:{continue:!1,stopReason:e.reason||"Pre-compact operation failed",suppressOutput:!0}:n==="SessionStart"?t&&e.context?{continue:!0,suppressOutput:!0,hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{continue:!0,suppressOutput:!0}:n==="UserPromptSubmit"||n==="PostToolUse"?{continue:!0,suppressOutput:!0}:n==="Stop"?{continue:!0,suppressOutput:!0}:{continue:t,suppressOutput:!0,...e.reason&&!t?{stopReason:e.reason}:{}}}function d(n,t,e={}){let o=$(n,t,e);return JSON.stringify(o)}var S=(s=>(s[s.DEBUG=0]="DEBUG",s[s.INFO=1]="INFO",s[s.WARN=2]="WARN",s[s.ERROR=3]="ERROR",s[s.SILENT=4]="SILENT",s))(S||{}),h=class{level;useColor;constructor(){let t=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=S[t]??1,this.useColor=process.stdout.isTTY??!1}correlationId(t,e){return`obs-${t}-${e}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.level===0?`${t.message}
|
|
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let e=Object.keys(t);return e.length===0?"{}":e.length<=3?JSON.stringify(t):`{${e.length} keys: ${e.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,e){if(!e)return t;try{let o=typeof e=="string"?JSON.parse(e):e;if(t==="Bash"&&o.command){let r=o.command.length>50?o.command.substring(0,50)+"...":o.command;return`${t}(${r})`}if(t==="Read"&&o.file_path){let r=o.file_path.split("/").pop()||o.file_path;return`${t}(${r})`}if(t==="Edit"&&o.file_path){let r=o.file_path.split("/").pop()||o.file_path;return`${t}(${r})`}if(t==="Write"&&o.file_path){let r=o.file_path.split("/").pop()||o.file_path;return`${t}(${r})`}return t}catch{return t}}log(t,e,o,r,s){if(t<this.level)return;let c=new Date().toISOString().replace("T"," ").substring(0,23),l=S[t].padEnd(5),a=e.padEnd(6),u="";r?.correlationId?u=`[${r.correlationId}] `:r?.sessionId&&(u=`[session-${r.sessionId}] `);let g="";s!=null&&(this.level===0&&typeof s=="object"?g=`
|
|
`+JSON.stringify(s,null,2):g=" "+this.formatData(s));let C="";if(r){let{sessionId:J,sdkSessionId:V,correlationId:q,...x}=r;Object.keys(x).length>0&&(C=` {${Object.entries(x).map(([D,I])=>`${D}=${I}`).join(", ")}}`)}let k=`[${c}] [${l}] [${a}] ${u}${o}${C}${g}`;t===3?console.error(k):console.log(k)}debug(t,e,o,r){this.log(0,t,e,o,r)}info(t,e,o,r){this.log(1,t,e,o,r)}warn(t,e,o,r){this.log(2,t,e,o,r)}error(t,e,o,r){this.log(3,t,e,o,r)}dataIn(t,e,o,r){this.info(t,`\u2192 ${e}`,o,r)}dataOut(t,e,o,r){this.info(t,`\u2190 ${e}`,o,r)}success(t,e,o,r){this.info(t,`\u2713 ${e}`,o,r)}failure(t,e,o,r){this.error(t,`\u2717 ${e}`,o,r)}timing(t,e,o,r){this.info(t,`\u23F1 ${e}`,r,{duration:`${o}ms`})}},m=new h;import _ from"path";import{homedir as H}from"os";import{existsSync as O,readFileSync as j}from"fs";import{spawnSync as M}from"child_process";import{join as i,dirname as b,basename as Z}from"path";import{homedir as T}from"os";import{fileURLToPath as A}from"url";function L(){return typeof __dirname<"u"?__dirname:b(A(import.meta.url))}var U=L(),p=process.env.CLAUDE_MEM_DATA_DIR||i(T(),".claude-mem"),y=process.env.CLAUDE_CONFIG_DIR||i(T(),".claude"),rt=i(p,"archives"),nt=i(p,"logs"),st=i(p,"trash"),it=i(p,"backups"),at=i(p,"settings.json"),ct=i(p,"claude-mem.db"),pt=i(p,"vector-db"),ut=i(y,"settings.json"),lt=i(y,"commands"),mt=i(y,"CLAUDE.md");function R(){return i(U,"..","..")}var N=100,W=500,K=10;function f(){try{let n=_.join(H(),".claude-mem","settings.json");if(O(n)){let t=JSON.parse(j(n,"utf-8")),e=parseInt(t.env?.CLAUDE_MEM_WORKER_PORT,10);if(!isNaN(e))return e}}catch{}return parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10)}async function w(){try{let n=f();return(await fetch(`http://127.0.0.1:${n}/health`,{signal:AbortSignal.timeout(N)})).ok}catch{return!1}}async function B(){try{let n=R(),t=_.join(n,"ecosystem.config.cjs");if(!O(t))throw new Error(`Ecosystem config not found at ${t}`);let e=_.join(n,"node_modules",".bin","pm2"),o=process.platform==="win32"?e+".cmd":e,r=O(o)?o:"pm2",s=M(r,["start",t],{cwd:n,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(s.status!==0)throw new Error(s.stderr||"PM2 start failed");for(let c=0;c<K;c++)if(await new Promise(l=>setTimeout(l,W)),await w())return!0;return!1}catch{return!1}}async function v(){if(await w())return;if(!await B()){let t=f(),e=R();throw new Error(`Worker service failed to start on port ${t}.
|
|
|
|
To start manually, run:
|
|
cd ${e}
|
|
npx pm2 start ecosystem.config.cjs
|
|
|
|
If already running, try: npx pm2 restart claude-mem-worker`)}}var F=new Set(["ListMcpResourcesTool","SlashCommand","Skill","TodoWrite","AskUserQuestion"]);async function G(n){if(!n)throw new Error("saveHook requires input");let{session_id:t,cwd:e,tool_name:o,tool_input:r,tool_response:s}=n;if(F.has(o)){console.log(d("PostToolUse",!0));return}await v();let c=f(),l=m.formatTool(o,r);m.dataIn("HOOK",`PostToolUse: ${l}`,{workerPort:c});try{let a=await fetch(`http://127.0.0.1:${c}/api/sessions/observations`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,tool_name:o,tool_input:r,tool_response:s,cwd:e||""}),signal:AbortSignal.timeout(2e3)});if(!a.ok){let u=await a.text();throw m.failure("HOOK","Failed to send observation",{status:a.status},u),new Error(`Failed to send observation to worker: ${a.status} ${u}`)}m.debug("HOOK","Observation sent successfully",{toolName:o})}catch(a){throw a.cause?.code==="ECONNREFUSED"||a.name==="TimeoutError"||a.message.includes("fetch failed")?new Error("There's a problem with the worker. If you just updated, type `pm2 restart claude-mem-worker` in your terminal to continue"):a}console.log(d("PostToolUse",!0))}var E="";P.on("data",n=>E+=n);P.on("end",async()=>{let n=E?JSON.parse(E):void 0;await G(n)});
|