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) • 4.87 kB
JavaScript
import{readFile as _}from"node:fs/promises";import{existsSync as m}from"node:fs";import{homedir as h}from"node:os";import{join as u}from"node:path";import{execFile as I}from"node:child_process";import{promisify as O}from"node:util";const f=O(I),N=[u(h(),".aws/sso/cache/kiro-auth-token-cli.json"),u(h(),".aws/sso/cache/kiro-auth-token.json")],R="https://codewhisperer.us-east-1.amazonaws.com",b="AmazonCodeWhispererService.GetUsageLimits",S=1e4,E="kirocli:odic:token",C="kirocli:odic:device-registration";async function x(t){try{const{stdout:r}=await f("security",["find-generic-password","-s",t,"-w"],{timeout:5e3});return r.trim()}catch{return null}}async function D(t,r){try{await f("security",["delete-generic-password","-s",t],{timeout:5e3}).catch(()=>{}),await f("security",["add-generic-password","-s",t,"-w",r,"-U"],{timeout:5e3})}catch{}}function L(t){return Date.now()>new Date(t).getTime()}const w=u(h(),"Library/Application Support/kiro-cli/data.sqlite3");async function K(){if(!m(w))return null;try{const{stdout:t}=await f("sqlite3",[w,"SELECT value FROM auth_kv WHERE key='kirocli:odic:token';"],{timeout:5e3}),r=t.trim();if(!r)return null;const e=JSON.parse(r);if(e.access_token&&e.expires_at)return{accessToken:e.access_token,refreshToken:e.refresh_token,expiresAt:e.expires_at,region:e.region??"us-east-1",keychainRaw:e}}catch{}return null}async function v(){const t=await K();if(t)return t;const r=await x(E);if(r)try{const e=JSON.parse(r);if(e.access_token&&e.expires_at)return{accessToken:e.access_token,refreshToken:e.refresh_token,expiresAt:e.expires_at,region:e.region??"us-east-1",keychainRaw:e}}catch{}for(const e of N)if(m(e))try{const n=JSON.parse(await _(e,"utf-8"));if(n.accessToken&&n.expiresAt)return{accessToken:n.accessToken,refreshToken:n.refreshToken??"",expiresAt:n.expiresAt,region:n.region??"us-east-1",profileArn:n.profileArn}}catch{}throw new Error("\u627E\u4E0D\u5230 Kiro token\uFF08SQLite\u3001keychain \u548C\u6587\u4EF6\u5747\u4E0D\u53EF\u7528\uFF09")}async function j(t){if(!t.refreshToken)throw new Error("\u65E0 refreshToken \u53EF\u7528");let r,e,n=!1;if(!n&&m(w))try{const{stdout:i}=await f("sqlite3",[w,"SELECT value FROM auth_kv WHERE key='kirocli:odic:device-registration';"],{timeout:5e3}),c=i.trim();if(c){const l=JSON.parse(c);l.client_id&&l.client_secret&&(r=l.client_id,e=l.client_secret,n=!0)}}catch{}if(!n){const i=await x(C);if(i){const c=JSON.parse(i);r=c.client_id,e=c.client_secret,n=!0}}if(!n){const i=u(h(),".aws/sso/cache"),{readdir:c}=await import("node:fs/promises"),l=await c(i);let p=null;for(const d of l)if(!(d.startsWith("kiro-")||!d.endsWith(".json")))try{const k=JSON.parse(await _(u(i,d),"utf-8"));if(k.clientId&&k.clientSecret){p={clientId:k.clientId,clientSecret:k.clientSecret};break}}catch{}if(!p)throw new Error("\u627E\u4E0D\u5230 OIDC client \u6CE8\u518C\u4FE1\u606F");r=p.clientId,e=p.clientSecret}const o=new AbortController,a=setTimeout(()=>o.abort(),S);let s;try{const i=await fetch(`https://oidc.${t.region}.amazonaws.com/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clientId:r,clientSecret:e,grantType:"refresh_token",refreshToken:t.refreshToken}),signal:o.signal});if(!i.ok)throw new Error(`OIDC refresh ${i.status}: ${await i.text()}`);s=await i.json()}finally{clearTimeout(a)}const T=s.accessToken,y=s.refreshToken??t.refreshToken,A=s.expiresIn??3600,g=new Date(Date.now()+A*1e3).toISOString();if(t.keychainRaw){const i={...t.keychainRaw,access_token:T,refresh_token:y,expires_at:g};await D(E,JSON.stringify(i))}return{...t,accessToken:T,refreshToken:y,expiresAt:g}}async function J(t,r){const e=new AbortController,n=setTimeout(()=>e.abort(),S);try{const o={};r&&(o.profileArn=r);const a=await fetch(R,{method:"POST",headers:{"Content-Type":"application/x-amz-json-1.0","X-Amz-Target":b,Authorization:`Bearer ${t}`,Accept:"application/json"},body:JSON.stringify(o),signal:e.signal});if(!a.ok){const s=await a.text();throw new Error(`GetUsageLimits ${a.status}: ${s}`)}return await a.json()}finally{clearTimeout(n)}}async function M(){try{let t=await v();L(t.expiresAt)&&(t=await j(t));const r=await J(t.accessToken,t.profileArn),e=r.usageBreakdownList?.find(o=>o.resourceType==="CREDIT");let n=null;if(e){const o=e.currentUsageWithPrecision??e.currentUsage,a=e.usageLimitWithPrecision??e.usageLimit,s=e.nextDateReset<1e12?e.nextDateReset*1e3:e.nextDateReset;n={remaining:Math.max(0,a-o),total:a,used:o,unit:e.unit||e.displayName||"Credits",resetsAt:Number.isFinite(s)&&s>0?new Date(s).toISOString():null}}return{provider:"kiro",providerLabel:"Kiro",planName:r.subscriptionInfo?.subscriptionTitle??null,tiers:[],balance:n,success:!0,error:null}}catch(t){const r=t instanceof Error?t.message:String(t);return{provider:"kiro",providerLabel:"Kiro",planName:null,tiers:[],balance:null,success:!1,error:r}}}export{M as queryKiroQuota};