UNPKG

grix-connector

Version:

Connect local AI coding agents (Claude, Codex, Gemini, Qwen, DeepSeek, Cursor, OpenCode, Pi, OpenHuman, Reasonix) to the Grix scheduling platform. Also serves as an OpenClaw plugin for Grix channel transport.

6 lines (5 loc) 3.37 kB
import{createReadStream as A,readFileSync as S,statSync as k}from"node:fs";import{createInterface as j}from"node:readline";import T from"node:path";import E from"node:os";import{log as _}from"../../core/log/index.js";function I(e){return e.replace(/[/\\]/g,"-").replace(/:/g,"-")}function m(e,r){const t=T.join(E.homedir(),".claude"),u=T.join(t,"projects",I(r));return T.join(u,`${e}.jsonl`)}function R(){return{inputTokens:0,outputTokens:0,cacheReadInputTokens:0,cacheCreationInputTokens:0}}function b(e,r){e.inputTokens+=r.input_tokens??0,e.outputTokens+=r.output_tokens??0,e.cacheReadInputTokens+=r.cache_read_input_tokens??0,e.cacheCreationInputTokens+=r.cache_creation_input_tokens??0}async function $(e,r){const t=m(e,r);_.info("usage-parser",`Parsing session usage from ${t}`);const u=new Map,a=R();let n=0;try{const c=j({input:A(t,"utf8"),crlfDelay:1/0});for await(const s of c)if(s.trim())try{const l=JSON.parse(s);if(l.type!=="assistant")continue;const o=l.message?.usage;if(!o)continue;const f=l.message?.model??"unknown";n++,b(a,o);let p=u.get(f);p||(p={turns:0,usage:R()},u.set(f,p)),p.turns++,b(p.usage,o)}catch{}}catch(c){return c.code==="ENOENT"?(_.info("usage-parser",`Session JSONL not found: ${t}`),null):(_.error("usage-parser",`Failed to parse session usage: ${c}`),null)}return n===0?null:{models:[...u.entries()].map(([c,s])=>({model:c,turns:s.turns,total:s.usage})),total:a,turns:n}}const h=64*1024,M=9e4;function N(e,r,t){const u=m(e,r);try{const a=S(u);let n;t!==void 0&&t>0?n=t<a.length?a.subarray(t):Buffer.alloc(0):n=a.length>h?a.subarray(a.length-h):a;const i=n.toString("utf8").split(` `);for(let c=i.length-1;c>=0;c--){const s=i[c].trim();if(!s)continue;let l;try{l=JSON.parse(s)}catch{continue}if(l.type!=="assistant")continue;const o=l.message;if(!o?.content)continue;const f=Array.isArray(o.content)?o.content:[{type:"text",text:String(o.content)}],p=[];for(const g of f){const y=g;y.type==="text"&&y.text?.trim()&&p.push(y.text)}if(p.length>0)return{text:p.join(` `),stopReason:o.stop_reason??null}}return null}catch{return null}}function B(e,r,t){return N(e,r,t)?.text??null}function F(e,r,t){const u=m(e,r);let a=null,n;try{const s=k(u);a=Date.now()-s.mtimeMs,n=S(u)}catch{return{lastStopReason:null,freshMs:null}}let i;t!==void 0&&t>0?i=t<n.length?n.subarray(t):Buffer.alloc(0):i=n.length>h?n.subarray(n.length-h):n;const c=i.toString("utf8").split(` `);for(let s=c.length-1;s>=0;s--){const l=c[s].trim();if(!l)continue;let o;try{o=JSON.parse(l)}catch{continue}if(o.type!=="assistant")continue;const f=o.message;if(f)return{lastStopReason:f.stop_reason??null,freshMs:a}}return{lastStopReason:null,freshMs:a}}function v(e,r,t,u=!1){const a=m(e,r);try{const n=k(a);if(u&&Date.now()-n.mtimeMs<M)return!1;const i=S(a),s=(t>0&&t<i.length?i.subarray(t):i.length>h?i.subarray(i.length-h):i).toString("utf8").split(` `);let l=!1,o=!1;for(const f of s){const p=f.trim();if(!p)continue;let g;try{g=JSON.parse(p)}catch{continue}if(g.type==="user"){const x=g.message?.content??g.content;(Array.isArray(x)?x:[]).some(d=>typeof d=="object"&&d!==null&&d.type==="tool_result")&&(l=!0,o=!1)}else g.type==="assistant"&&l&&(o=!0)}return l&&!o}catch{return!1}}export{N as extractLastAssistantEntry,B as extractLastAssistantText,v as hasTerminalToolResultAfterOffset,$ as parseClaudeSessionUsage,F as probeSessionTurnState,m as resolveSessionJsonlPath};