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.
2 lines (1 loc) • 3.45 kB
JavaScript
import{createReadStream as I,readFileSync as h,readdirSync as $,existsSync as T}from"node:fs";import{createInterface as C}from"node:readline";import p from"node:path";import g from"node:os";import{log as f}from"../../core/log/index.js";function m(){return{inputTokens:0,outputTokens:0,cacheReadInputTokens:0,cacheCreationInputTokens:0}}function d(n,e){n.inputTokens+=e.inputTokens,n.outputTokens+=e.outputTokens,n.cacheReadInputTokens+=e.cacheReadInputTokens,n.cacheCreationInputTokens+=e.cacheCreationInputTokens}function S(n,e){const i=p.join(g.homedir(),".gemini","tmp"),o=p.basename(e),s=n.slice(0,8),r=p.join(i,o,"chats");try{const l=$(r);for(const c of[".jsonl",".json"]){const a=l.find(t=>t.startsWith("session-")&&t.includes(s)&&t.endsWith(c));if(a)return p.join(r,a)}}catch{}return null}function y(n){if(n.type!=="gemini"||!n.tokens)return null;const e=n.tokens;return{model:n.model??"unknown",usage:{inputTokens:e.input??0,outputTokens:e.output??0,cacheReadInputTokens:e.cached??0,cacheCreationInputTokens:0}}}function F(n,e){const i=p.join(g.homedir(),".qwen","projects"),o=p.join(i,e.replace(/[/\\]/g,"-")),s=p.join(o,"chats",`${n}.jsonl`);return T(s)?s:null}function M(n){if(n.type!=="assistant"||!n.usageMetadata)return null;const e=n.usageMetadata;return{model:n.model??"unknown",usage:{inputTokens:e.promptTokenCount??0,outputTokens:e.candidatesTokenCount??0,cacheReadInputTokens:e.cachedContentTokenCount??0,cacheCreationInputTokens:0}}}async function w(n,e){const i=new Map,o=m();let s=0;const r=C({input:I(n,"utf8"),crlfDelay:1/0});for await(const c of r)if(c.trim())try{const a=JSON.parse(c),t=e(a);if(!t)continue;s++,d(o,t.usage);let u=i.get(t.model);u||(u={turns:0,usage:m()},i.set(t.model,u)),u.turns++,d(u.usage,t.usage)}catch{}return s===0?null:{models:[...i.entries()].map(([c,a])=>({model:c,turns:a.turns,total:a.usage})),total:o,turns:s}}function J(n){const e=h(n,"utf8"),i=JSON.parse(e),o=Array.isArray(i)?i:i.messages??[],s=new Map,r=m();let l=0;for(const a of o){const t=y(a);if(!t)continue;l++,d(r,t.usage);let u=s.get(t.model);u||(u={turns:0,usage:m()},s.set(t.model,u)),u.turns++,d(u.usage,t.usage)}return l===0?null:{models:[...s.entries()].map(([a,t])=>({model:a,turns:t.turns,total:t.usage})),total:r,turns:l}}function P(n){const e=p.join(g.homedir(),".kiro","sessions","cli",`${n}.json`);return T(e)?e:null}function R(n){const e=h(n,"utf8"),o=JSON.parse(e).session_state;if(!o)return null;const r=o.rts_model_state?.model_info?.model_id??"auto",c=o.conversation_metadata?.user_turn_metadatas??[];if(c.length===0)return null;const a=m();let t=0;for(const k of c){const j=k.input_token_count??0,_=k.output_token_count??0;t++,a.inputTokens+=j,a.outputTokens+=_}return{models:[{model:r,turns:t,total:{...a}}],total:a,turns:t}}async function b(n,e,i){if(!i||i==="kiro"){const r=P(n);if(r){f.info("acp-usage-parser",`Parsing Kiro session from ${r}`);try{return R(r)}catch(l){f.error("acp-usage-parser",`Kiro parse failed: ${l}`)}}}const o=S(n,e);if(o){f.info("acp-usage-parser",`Parsing Gemini session from ${o}`);try{return o.endsWith(".jsonl")?await w(o,y):J(o)}catch(r){f.error("acp-usage-parser",`Gemini parse failed: ${r}`)}}const s=F(n,e);if(s){f.info("acp-usage-parser",`Parsing Qwen session from ${s}`);try{return await w(s,M)}catch(r){f.error("acp-usage-parser",`Qwen parse failed: ${r}`)}}return f.info("acp-usage-parser",`No session file found for ${n} in ${e}`),null}export{b as parseAcpSessionUsage};