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