@cortexkit/opencode-openai-auth 0.11.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -26
- package/dist/index-766sfrh5.js +1 -0
- package/dist/index-8kj7sa1p.js +12 -0
- package/dist/index-8rqfbhfw.js +221 -0
- package/dist/index-b3q3j39x.js +2 -0
- package/dist/index-bmd7x5aq.js +2 -0
- package/dist/index-eqh53k8v.js +2 -0
- package/dist/{index-sdt2d2ra.js → index-hy9030jh.js} +1 -1
- package/dist/index-jyr8y8k4.js +7 -0
- package/dist/index-rv08jmd4.js +2 -0
- package/dist/index.d.ts +578 -145
- package/dist/index.js +1 -110
- package/dist/pool-lifecycle-DbDmgabd.d.ts +2430 -0
- package/dist/port-file-Do3lQIEk.d.ts +29 -0
- package/dist/protocol-BOBdkqqm.d.ts +198 -0
- package/dist/rpc/port-file.d.ts +4 -17
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +2 -2
- package/dist/rpc/protocol.js +1 -0
- package/dist/rpc/rpc-client.d.ts +5 -4
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.d.ts +2 -0
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/sidebar-state-D6cUKgkm.d.ts +385 -0
- package/dist/sidebar-state.d.ts +2 -2
- package/dist/sidebar-state.js +1 -1
- package/dist/token-util-wdza526f.js +1 -0
- package/dist/{token-9823y0pc.js → token-ypk61xj0.js} +1 -1
- package/dist/tui-preferences.js +5 -2
- package/dist/tui.d.ts +9 -4
- package/dist/v2/server.d.ts +54 -0
- package/dist/v2/server.js +4 -0
- package/package.json +21 -9
- package/src/tui/entry.mjs +26 -25
- package/src/tui-compiled/raw/logger.js +24 -0
- package/src/tui-compiled/raw/rpc/protocol.js +3 -0
- package/src/tui-compiled/raw/rpc/rpc-client.js +7 -0
- package/src/tui-compiled/raw/rpc/rpc-dir.js +27 -0
- package/src/tui-compiled/raw/selector.js +21 -0
- package/src/tui-compiled/raw/shared/0332701351b3eeec-format.js +138 -0
- package/src/tui-compiled/raw/shared/04a6357627de5ed0-errors.js +38 -0
- package/src/tui-compiled/raw/shared/053cc17b5419261e-codec.js +12 -0
- package/src/tui-compiled/raw/shared/0766d78c6f481f18-watcher.js +157 -0
- package/src/tui-compiled/raw/shared/08eef45626a94992-reserved.js +13 -0
- package/src/tui-compiled/raw/shared/39c3cb05738fcf6d-projection.js +134 -0
- package/src/tui-compiled/raw/shared/3c2d4169d00f907e-tui-support.js +5 -0
- package/src/tui-compiled/raw/shared/3e7d45a77c6ad4d5-index.js +4 -0
- package/src/tui-compiled/raw/shared/415792e85d80819e-capture-sink.js +13 -0
- package/src/tui-compiled/raw/shared/42ae93ab554fcec2-open-url.js +28 -0
- package/src/tui-compiled/raw/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
- package/src/tui-compiled/raw/shared/458b934e92490331-mutate.js +500 -0
- package/src/tui-compiled/raw/shared/45bf5343bbbf9add-admission.js +142 -0
- package/src/tui-compiled/raw/shared/46f3dff1266160e8-client.js +2 -0
- package/src/tui-compiled/raw/shared/4825e03209d6ae3e-with-lock.js +48 -0
- package/src/tui-compiled/raw/shared/4f36d333d081d040-merge.js +162 -0
- package/src/tui-compiled/raw/shared/54eba94fd64c8c3c-index.js +7 -0
- package/src/tui-compiled/raw/shared/7211cfec06a298bc-atomic-write.js +80 -0
- package/src/tui-compiled/raw/shared/754d6017f8e419cf-builtins.js +534 -0
- package/src/tui-compiled/raw/shared/75bd8c3bd63b315f-identity.js +79 -0
- package/src/tui-compiled/raw/shared/7a91bb630e6f4804-index.js +1 -0
- package/src/tui-compiled/raw/shared/826342a4b6a8b89b-model.js +16 -0
- package/src/tui-compiled/raw/shared/83068c0dbe8a6f13-publication.js +304 -0
- package/src/tui-compiled/raw/shared/836fc615de6afbd9-settings.js +120 -0
- package/src/tui-compiled/raw/shared/83bc3ba9d0fe2761-runtime.js +68 -0
- package/src/tui-compiled/raw/shared/8a18abe4fa2977c8-menu.js +300 -0
- package/src/tui-compiled/raw/shared/901e149c5a0dbbff-sticky.js +339 -0
- package/src/tui-compiled/raw/shared/9020c407ce64f672-ordered.js +58 -0
- package/src/tui-compiled/raw/shared/905bffc0220f43d0-map.js +105 -0
- package/src/tui-compiled/raw/shared/94820e324f2b4c09-seam.js +301 -0
- package/src/tui-compiled/raw/shared/97484d9bb98c705b-provider-state.js +226 -0
- package/src/tui-compiled/raw/shared/9968e3cade6969a1-index.js +2 -0
- package/src/tui-compiled/raw/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
- package/src/tui-compiled/raw/shared/9d3fc9e37456504e-refresh.js +225 -0
- package/src/tui-compiled/raw/shared/9deae3c4da4fd63b-hooks.js +34 -0
- package/src/tui-compiled/raw/shared/a09b6a533c11858f-tui-preferences.js +77 -0
- package/src/tui-compiled/raw/shared/a62a27a204266acd-attribution.js +60 -0
- package/src/tui-compiled/raw/shared/a910277ac43a8626-pull.js +143 -0
- package/src/tui-compiled/raw/shared/b85c5301fddcb98a-paths.js +5 -0
- package/src/tui-compiled/raw/shared/c14988621dc60a75-port-file.js +217 -0
- package/src/tui-compiled/raw/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
- package/src/tui-compiled/raw/shared/c6b176708c446d6d-pins.js +34 -0
- package/src/tui-compiled/raw/shared/c7d252bedee01600-pi.js +206 -0
- package/src/tui-compiled/raw/shared/c852a15cfe489d12-pool.js +120 -0
- package/src/tui-compiled/raw/shared/cc9e0702a752cea7-index.js +3 -0
- package/src/tui-compiled/raw/shared/cd94316a888b0a8e-schema.js +754 -0
- package/src/tui-compiled/raw/shared/d319517e3ea6ad70-redact.js +83 -0
- package/src/tui-compiled/raw/shared/d5a8f1061ee2f114-index.js +5 -0
- package/src/tui-compiled/raw/shared/d669d72c40e9e116-logger.js +19 -0
- package/src/tui-compiled/raw/shared/d7dcba96fea1685c-engine.js +246 -0
- package/src/tui-compiled/raw/shared/dbef697d1df1cd84-index.js +5 -0
- package/src/tui-compiled/raw/shared/e4acc4cb713f300c-protocol.js +3 -0
- package/src/tui-compiled/raw/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
- package/src/tui-compiled/raw/shared/ec971142548df90e-index.js +6 -0
- package/src/tui-compiled/raw/shared/f37fcefc7aedbf25-error.js +11 -0
- package/src/tui-compiled/raw/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
- package/src/tui-compiled/raw/shared/fd45e82f2f9c0a0d-torn.js +294 -0
- package/src/tui-compiled/raw/shared/fe02abeadc535f77-rows.js +1258 -0
- package/src/tui-compiled/raw/sidebar-state.js +960 -0
- package/src/tui-compiled/raw/tui/command-dialogs.tsx +424 -0
- package/src/tui-compiled/raw/tui-preferences.js +135 -0
- package/src/tui-compiled/raw/tui.tsx +135 -143
- package/src/tui-compiled/runtime/logger.js +24 -0
- package/src/tui-compiled/runtime/rpc/protocol.js +3 -0
- package/src/tui-compiled/runtime/rpc/rpc-client.js +7 -0
- package/src/tui-compiled/runtime/rpc/rpc-dir.js +27 -0
- package/src/tui-compiled/runtime/selector.js +21 -0
- package/src/tui-compiled/runtime/shared/0332701351b3eeec-format.js +138 -0
- package/src/tui-compiled/runtime/shared/04a6357627de5ed0-errors.js +38 -0
- package/src/tui-compiled/runtime/shared/053cc17b5419261e-codec.js +12 -0
- package/src/tui-compiled/runtime/shared/0766d78c6f481f18-watcher.js +157 -0
- package/src/tui-compiled/runtime/shared/08eef45626a94992-reserved.js +13 -0
- package/src/tui-compiled/runtime/shared/39c3cb05738fcf6d-projection.js +134 -0
- package/src/tui-compiled/runtime/shared/3c2d4169d00f907e-tui-support.js +5 -0
- package/src/tui-compiled/runtime/shared/3e7d45a77c6ad4d5-index.js +4 -0
- package/src/tui-compiled/runtime/shared/415792e85d80819e-capture-sink.js +13 -0
- package/src/tui-compiled/runtime/shared/42ae93ab554fcec2-open-url.js +28 -0
- package/src/tui-compiled/runtime/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
- package/src/tui-compiled/runtime/shared/458b934e92490331-mutate.js +500 -0
- package/src/tui-compiled/runtime/shared/45bf5343bbbf9add-admission.js +142 -0
- package/src/tui-compiled/runtime/shared/46f3dff1266160e8-client.js +2 -0
- package/src/tui-compiled/runtime/shared/4825e03209d6ae3e-with-lock.js +48 -0
- package/src/tui-compiled/runtime/shared/4f36d333d081d040-merge.js +162 -0
- package/src/tui-compiled/runtime/shared/54eba94fd64c8c3c-index.js +7 -0
- package/src/tui-compiled/runtime/shared/7211cfec06a298bc-atomic-write.js +80 -0
- package/src/tui-compiled/runtime/shared/754d6017f8e419cf-builtins.js +534 -0
- package/src/tui-compiled/runtime/shared/75bd8c3bd63b315f-identity.js +79 -0
- package/src/tui-compiled/runtime/shared/7a91bb630e6f4804-index.js +1 -0
- package/src/tui-compiled/runtime/shared/826342a4b6a8b89b-model.js +16 -0
- package/src/tui-compiled/runtime/shared/83068c0dbe8a6f13-publication.js +304 -0
- package/src/tui-compiled/runtime/shared/836fc615de6afbd9-settings.js +120 -0
- package/src/tui-compiled/runtime/shared/83bc3ba9d0fe2761-runtime.js +68 -0
- package/src/tui-compiled/runtime/shared/8a18abe4fa2977c8-menu.js +300 -0
- package/src/tui-compiled/runtime/shared/901e149c5a0dbbff-sticky.js +339 -0
- package/src/tui-compiled/runtime/shared/9020c407ce64f672-ordered.js +58 -0
- package/src/tui-compiled/runtime/shared/905bffc0220f43d0-map.js +105 -0
- package/src/tui-compiled/runtime/shared/94820e324f2b4c09-seam.js +301 -0
- package/src/tui-compiled/runtime/shared/97484d9bb98c705b-provider-state.js +226 -0
- package/src/tui-compiled/runtime/shared/9968e3cade6969a1-index.js +2 -0
- package/src/tui-compiled/runtime/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
- package/src/tui-compiled/runtime/shared/9d3fc9e37456504e-refresh.js +225 -0
- package/src/tui-compiled/runtime/shared/9deae3c4da4fd63b-hooks.js +34 -0
- package/src/tui-compiled/runtime/shared/a09b6a533c11858f-tui-preferences.js +77 -0
- package/src/tui-compiled/runtime/shared/a62a27a204266acd-attribution.js +60 -0
- package/src/tui-compiled/runtime/shared/a910277ac43a8626-pull.js +143 -0
- package/src/tui-compiled/runtime/shared/b85c5301fddcb98a-paths.js +5 -0
- package/src/tui-compiled/runtime/shared/c14988621dc60a75-port-file.js +217 -0
- package/src/tui-compiled/runtime/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
- package/src/tui-compiled/runtime/shared/c6b176708c446d6d-pins.js +34 -0
- package/src/tui-compiled/runtime/shared/c7d252bedee01600-pi.js +206 -0
- package/src/tui-compiled/runtime/shared/c852a15cfe489d12-pool.js +120 -0
- package/src/tui-compiled/runtime/shared/cc9e0702a752cea7-index.js +3 -0
- package/src/tui-compiled/runtime/shared/cd94316a888b0a8e-schema.js +754 -0
- package/src/tui-compiled/runtime/shared/d319517e3ea6ad70-redact.js +83 -0
- package/src/tui-compiled/runtime/shared/d5a8f1061ee2f114-index.js +5 -0
- package/src/tui-compiled/runtime/shared/d669d72c40e9e116-logger.js +19 -0
- package/src/tui-compiled/runtime/shared/d7dcba96fea1685c-engine.js +246 -0
- package/src/tui-compiled/runtime/shared/dbef697d1df1cd84-index.js +5 -0
- package/src/tui-compiled/runtime/shared/e4acc4cb713f300c-protocol.js +3 -0
- package/src/tui-compiled/runtime/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
- package/src/tui-compiled/runtime/shared/ec971142548df90e-index.js +6 -0
- package/src/tui-compiled/runtime/shared/f37fcefc7aedbf25-error.js +11 -0
- package/src/tui-compiled/runtime/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
- package/src/tui-compiled/runtime/shared/fd45e82f2f9c0a0d-torn.js +294 -0
- package/src/tui-compiled/runtime/shared/fe02abeadc535f77-rows.js +1258 -0
- package/src/tui-compiled/runtime/sidebar-state.js +960 -0
- package/src/tui-compiled/runtime/tui/command-dialogs.js +378 -0
- package/src/tui-compiled/runtime/tui-preferences.js +135 -0
- package/src/tui-compiled/runtime/{tui.tsx → tui.js} +288 -316
- package/dist/index-4effjmr6.js +0 -2
- package/dist/index-9ehhb051.js +0 -2
- package/dist/index-g3hsdjh4.js +0 -2
- package/dist/index-hh52v8sb.js +0 -2
- package/dist/index-jwn4r3sw.js +0 -1
- package/dist/index-sq36b120.js +0 -211
- package/dist/internal-BTndPmW2.d.ts +0 -914
- package/dist/quota-normalize-08hpp51v.js +0 -1
- package/dist/sidebar-state-CoKYKcLn.d.ts +0 -220
- package/dist/token-util-ypgz7rge.js +0 -1
- package/src/tui-compiled/raw/core-account-paths.ts +0 -114
- package/src/tui-compiled/raw/core-custody-state.ts +0 -243
- package/src/tui-compiled/raw/logger.ts +0 -53
- package/src/tui-compiled/raw/rpc-port-file.ts +0 -177
- package/src/tui-compiled/raw/rpc-protocol.ts +0 -12
- package/src/tui-compiled/raw/rpc-rpc-client.ts +0 -82
- package/src/tui-compiled/raw/rpc-rpc-dir.ts +0 -46
- package/src/tui-compiled/raw/sidebar-state.ts +0 -1659
- package/src/tui-compiled/raw/tui-command-dialogs.tsx +0 -1096
- package/src/tui-compiled/raw/tui-preferences.ts +0 -328
- package/src/tui-compiled/runtime/core-account-paths.ts +0 -114
- package/src/tui-compiled/runtime/core-custody-state.ts +0 -243
- package/src/tui-compiled/runtime/logger.ts +0 -53
- package/src/tui-compiled/runtime/rpc-port-file.ts +0 -177
- package/src/tui-compiled/runtime/rpc-protocol.ts +0 -12
- package/src/tui-compiled/runtime/rpc-rpc-client.ts +0 -82
- package/src/tui-compiled/runtime/rpc-rpc-dir.ts +0 -46
- package/src/tui-compiled/runtime/sidebar-state.ts +0 -1659
- package/src/tui-compiled/runtime/tui-command-dialogs.tsx +0 -855
- package/src/tui-compiled/runtime/tui-preferences.ts +0 -328
- package/src/tui-compiled/shared/internal.ts +0 -6
- package/src/tui-compiled/shared/logger.ts +0 -264
- package/src/tui-compiled/shared/paths.ts +0 -33
- package/src/tui-compiled/shared/refresh-file-lock.ts +0 -384
- package/src/tui-compiled/shared/util/error.ts +0 -9
- package/src/tui-compiled/shared/util/open-url.ts +0 -30
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{$ as F1,A as N1,Aa as W$,B as D1,C as a1,D as o1,E as r1,F as t1,G as e1,Ga as t2,H as I2,Ha as e2,I as $$,J as A1,K as Y$,Ka as H$,L as Z$,M as K$,N as O1,O as x1,P as k2,Q as j2,R as U2,S as z$,T as X$,U as J$,V as j$,W as v2,X as Q$,Y as G$,Z as R2,_ as C1,aa as P1,ba as T1,c as h1,ca as M1,d as f1,da as L1,e as W2,ea as B$,f as H2,fa as V$,g as l1,ga as V2,h as p1,ha as _1,i as J1,j as j1,k as Q1,ka as w1,l as E1,la as I1,m as R1,n as c1,na as a2,o as d1,oa as o2,p as G1,pa as r2,q as i1,r as B1,s as V1,t as W1,u as H1,v as N2,w as n1,x as B2,y as s1,z as U1}from"../index-8rqfbhfw.js";import{Na as X1}from"../index-eqh53k8v.js";import"../index-jyr8y8k4.js";import"../index-rv08jmd4.js";import"../index-8kj7sa1p.js";import{gc as J2}from"../index-bmd7x5aq.js";import"../index-766sfrh5.js";import{wc as g1,xc as u1}from"../index-b3q3j39x.js";class d extends Error{kind;providerID;sessionID;requestKind;constructor($){super($.message??`${$.providerID} ${$.requestKind} request refused (${$.kind})`);this.name="OpenCode2AuthError",this.kind=$.kind,this.providerID=$.providerID,this.sessionID=$.sessionID,this.requestKind=$.requestKind}}function s($){return`common-auth-placeholder.${$}`}function i2($){let Z=s($.integrationID);return{type:"oauth",methodID:$.methodID,access:Z,refresh:Z,expires:$.now+31536000000,metadata:{["commonAuthPlaceholder"]:!0}}}function z2($,Z){if($?.type!=="oauth")return!1;let X=Z===void 0?void 0:s(Z),z=(j)=>X===void 0?j.startsWith("common-auth-placeholder."):j===X;return z($.access)&&z($.refresh)}var n2="No browser sign-in: this connects the plugin to the accounts in its vault.";async function M2($,Z){let X=Z.now??Date.now,{integrationID:z}=Z,j=(P)=>i2({integrationID:z,methodID:P,now:X()}),B=async(P,A)=>{return await Z.onLogin(P,{integrationID:z,methodID:A}),j(A)},F=(P)=>async(A)=>{let H=P.method.id,W=await P.authorize(A),O={url:W.url,instructions:W.instructions,...W.expiresAt===void 0?{}:{expiresAt:W.expiresAt}};if(W.mode==="auto")return{...O,mode:"auto",callback:W.callback.then((x)=>B(x,H))};let w=W.callback;return{...O,mode:"code",callback:async(x)=>B(await w(x),H)}};return $.integration.transform((P)=>{let A=Z.label===void 0?{}:{label:()=>Z.label};for(let O of Z.methods)P.method.update({integrationID:z,method:O.method,authorize:F(O),refresh:async(w)=>{if(!z2(w,z))throw Error("This login method refreshes only its pool placeholder; sign in again with the plugin's login method.");return j(w.methodID)},...A});let H=Z.vault;if(H===void 0)return;let W=H.method.id;P.method.update({integrationID:z,method:H.method,authorize:async()=>({url:"",instructions:H.instructions??n2,mode:"auto",callback:Promise.resolve().then(()=>H.activate({integrationID:z,methodID:W})).then(()=>j(W))}),refresh:async(O)=>{if(!z2(O,z)||O.methodID!==W)throw Error("This login method refreshes only its vault placeholder; choose the plugin's vault login method again.");return j(W)},...A})})}function F$($){let Z=[],X;for(let z of $.split(/\r?\n/))if(z.startsWith("data:"))Z.push(z.slice(5).replace(/^ /,""));else if(z.startsWith("event:"))X=z.slice(6).trim();if(Z.length===0)return;return X===void 0?{data:Z.join(`
|
|
2
|
+
`)}:{event:X,data:Z.join(`
|
|
3
|
+
`)}}function L2($,Z){let X=new TextDecoder,z="",j=(F)=>{let P=F$(F);if(!P)return;try{$(P)}catch(A){Z(A)}},B=()=>{for(;;){let F=/\r?\n\r?\n/.exec(z);if(!F)return;let P=z.slice(0,F.index);z=z.slice(F.index+F[0].length),j(P)}};return new TransformStream({transform(F,P){P.enqueue(F),z+=X.decode(F,{stream:!0}),B()},flush(){if(z+=X.decode(),B(),z.trim()!=="")j(z);z=""}})}var s2=512,$2="x-common-auth-attempt",e=($,Z)=>JSON.stringify([$,Z]),a=($)=>({providerID:$.model.providerID,modelID:$.model.id,sessionID:$.sessionID,agent:$.agent,kind:$.kind});function P$($){let Z=$.headers;return typeof Z==="object"&&Z!==null}function T$($,Z){let X=$.getReader();return new ReadableStream({async pull(z){let j;try{j=await X.read()}catch(B){Z({reason:"failed",message:B instanceof Error?B.message:String(B)}),z.error(B);return}if(j.done)Z(),z.close();else z.enqueue(j.value)},async cancel(z){Z({reason:"cancelled",...z===void 0?{}:{message:String(z)}}),await X.cancel(z)}})}function _2($,Z){for(let[X,z]of Object.entries(Z)){let j=X.toLowerCase();for(let B of Object.keys($))if(B.toLowerCase()===j)delete $[B];if(z!==null)$[X]=z}}function M$($,Z){for(let[X,z]of Object.entries(Z))if(z===null)$.delete(X);else $.set(X,z)}async function q2($,Z,X={}){let{providerID:z}=Z,j=X.gateOnPlaceholder,B=(Y)=>j===void 0||j.credential(Y)===s(z),F=X.logger,P=Math.max(1,X.maxRecords??s2),A=[...X.hostCredentials??[s(z)],...j?[s(z)]:[]].filter((Y)=>Y!==""),H=new Map,W=new WeakMap,O=new Map,w=new Set,x=new Map,b=!1,r=!1,c=new Map,v=0,u=!1,m=new Map,I=(Y,K)=>{try{F?.warn(Y,K)}catch{}},J=(Y)=>Y instanceof Error?Y.message:String(Y),V=async(Y,K)=>{let Q=c.get(Y);if(!Q||Q.size===0)return;await Promise.all([...Q].map(async(G)=>{try{await G(K)}catch(_){I("opencode2 auth listener threw; the request is unaffected",{event:Y,error:J(_)})}}))},M=async(Y,K)=>{try{await Z.onAttemptEnd?.(Y,K)}catch(Q){I("opencode2 auth onAttemptEnd threw; the request is unaffected",{attemptId:Y.attemptId,error:J(Q)})}},T=(Y,K)=>{if(Y.ended||!Y.attempt)return;let Q={...Y.status===void 0?{}:{status:Y.status},outputStarted:Y.outputStarted,...Y.limit?{limit:Y.limit.signal}:{},...K?{error:K}:{}};if(K)Y.endError=K;Y.ended=M(Y.attempt,Q)},R=(Y,K)=>T(Y,{reason:"abandoned",message:K}),L=(Y)=>[...H.values()].filter((K)=>K.key===Y),D=(Y)=>L(Y).at(-1),k=(Y)=>{if(H.delete(Y.id),!L(Y.key).length)O.delete(Y.key)},S=(Y)=>{H.set(Y.id,Y);while(H.size>P){let K=H.values().next().value;if(K===void 0)break;R(K,"dropped to keep the record bound"),k(K)}},g=(Y)=>Y.accountId===void 0?void 0:{...Y.scope,accountId:Y.accountId},p=(Y,K)=>new d({kind:"no-account",providerID:z,sessionID:Y.sessionID,requestKind:Y.kind,...K===void 0?{}:{message:K}}),l=(Y)=>{m.get(Y)?.clear(),m.delete(Y)},U=async(Y,K)=>{let Q=()=>p(Y,`${z} ${Y.kind} request has no account: its auth selection was revoked by session forgetting or installation disposal`);if(u)throw Q();let G=m.get(Y.sessionID);if(!G)G=new Set,m.set(Y.sessionID,G);let _={};G.add(_);let E=()=>{if(!G.has(_))throw Q()};try{return await K(E)}finally{if(G.delete(_),G.size===0&&m.get(Y.sessionID)===G)m.delete(Y.sessionID)}},N=(Y,K)=>{for(let[Q,G]of K)if(A.some((_)=>G.includes(_)))throw new d({kind:"host-credential-on-wire",providerID:z,sessionID:Y.sessionID,requestKind:Y.kind,message:`${z} ${Y.kind} request still carries the host's placeholder credential in ${Q}; the adapter's account headers must replace it`})},h=(Y)=>{for(let K of L(Y))if(K.ended||!K.attempt)k(K);else if(K.transport==="ws"||K.judged)R(K,"a newer attempt of its session and kind began"),k(K)},f=async(Y,K,Q,G)=>{let _=e(Y.sessionID,Y.kind),E=O.get(_);O.delete(_);let C=E?.accountId??D(_)?.accountId;h(_);let q={...Y,...C===void 0?{}:{previousAccountId:C},...E?.rerouteFrom===void 0?{}:{rerouteFrom:E.rerouteFrom}},y=await Z.chooseAccount(q);Q();let n={},c2;if(y!==void 0){let Q2=await Z.accountHeaders({...Y,accountId:y});if(Q(),P$(Q2))n=Q2.headers,c2=Q2.attempt;else n=Q2}let d2=`attempt-${++v}`,K2={id:d2,key:_,scope:Y,accountId:y,headers:n,attempt:y===void 0?void 0:{...Y,accountId:y,attemptId:d2,transport:G,data:c2},...G===void 0?{}:{transport:G},outputStarted:!1};if(S(K2),K2.attempt)V("select",{...Y,accountId:K2.attempt.accountId,hook:K,...q.previousAccountId===void 0?{}:{previousAccountId:q.previousAccountId},...q.rerouteFrom===void 0?{}:{rerouteFrom:q.rerouteFrom},handle:K2.attempt});return K2},Z2=async(Y,K,Q,G,_)=>{let E=e(Y.sessionID,Y.kind),C=G===void 0?void 0:H.get(G);if(C?.key!==E)C=void 0;if(!C){if(C=D(E),L(E).filter((y)=>y.transport===void 0&&!y.ended).length>1&&!r)r=!0,I("opencode2 auth tied a send without its attempt mark to the newest of several waiting attempts",{sessionID:Y.sessionID,kind:Y.kind})}if(!C||C.transport!==void 0||C.ended)C=await f(Y,K,_,Q);else if(C.transport=Q,C.attempt)C.attempt.transport=Q;if(Q==="ws"){for(let q of L(E))if(q!==C&&q.transport==="ws")R(q,"a newer attempt of its session and kind went out over WebSocket")}return C},F2=async(Y,K)=>{try{return await K()}catch(Q){throw T(Y,{reason:"failed",message:J(Q)}),Q}},f2=(Y,K)=>{let Q=L(e(Y,K)).filter((G)=>G.transport==="ws").at(-1);return Q?.attempt!==void 0&&!Q.ended?Q:void 0},P2=(Y,K,Q)=>{let G=g(Y);if(!G||!Y.attempt||Y.limit)return;Y.limit={signal:K,delivered:V("limit",{...G,via:Q,limit:K,outputStarted:Y.outputStarted,handle:Y.attempt})}},m2=(Y,K,Q,G)=>{let _=g(Y);if(!_||!Y.attempt)return;V("quota",{..._,transport:K,...G===void 0?{}:{status:G},quota:Q,handle:Y.attempt})},g2=(Y,K,Q,G)=>{let _=Y.attempt;if(!_)return;let E=Z.inspectEvent?.(G===void 0?{transport:K,data:Q,attempt:_}:{transport:K,data:Q,event:G,attempt:_});if(!E)return;if(E.quota!==void 0)m2(Y,K,E.quota);if(E.outputStarted)Y.outputStarted=!0;if(E.limit)P2(Y,E.limit,K);if(E.error!==void 0)T(Y,{reason:"failed",message:E.error});else if(E.done)T(Y)},U$=(Y,K)=>{let Q=Y.attempt;if(!Q||K.accountId===Y.accountId)return;if(Q.reboundFrom??=Q.accountId,Q.accountId=K.accountId,"data"in K)Q.data=K.data;Y.accountId=K.accountId},N$=(Y)=>!Y.attempt||Y.endError!==void 0||Y.status!==void 0&&Y.status>=400,C$=(Y)=>{let K=[...H.values()].filter((q)=>q.scope.sessionID===Y),Q=K.filter((q)=>q.limit&&!q.rerouted).at(-1);if(Q)return Q;let G=K.filter((q)=>q.scope.kind==="primary"),_=G.length>0?G:K;if(_.length<=1)return _[0];let E=_.filter((q)=>!q.judged&&N$(q)).at(-1);if(E)return E;let C=_.filter((q)=>q.attempt&&!q.ended);if(C.length>1)return"ambiguous";return C[0]??_.at(-1)},t={providerID:z},o=[];o.push(await $.session.hook("model.request",(Y)=>{if(j)return;return U(a(Y),async(K)=>{let Q=await f(a(Y),"model.request",K);if(K(),Q.accountId===void 0)throw p(Q.scope);_2(Y.headers,{...Q.headers,[$2]:Q.id})})},t)),o.push(await $.session.hook("http.request",(Y)=>{if(j){let K=B(Y.request.headers);if(x.set(Y.sessionID,K),!K)return}return U(a(Y),async(K)=>{let Q=Y.request.headers.get($2)??void 0,G=j?await f(a(Y),"http.request",K,"http"):await Z2(a(Y),"http.request","http",Q,K);K();let _=g(G),E=G.attempt;if(!_||!E)throw p(G.scope);await F2(G,async()=>{let C=Y.request;if(Z.rewriteRequest){if(Q!==void 0){let n=new Headers(C.headers);n.delete($2),C=new Request(C,{headers:n})}C=await Z.rewriteRequest({..._,request:C,attempt:E})??C,K()}let q=new Headers(C.headers);q.delete($2),M$(q,G.headers),N(G.scope,q.entries());let y=new Request(C,{headers:q});W.set(y,G),Y.request=y})})},t)),o.push(await $.session.hook("http.response",async(Y)=>{let K=W.get(Y.request);if(!K){if(j)return;if(!b)b=!0,I("opencode2 auth dropped an http.response whose request it did not produce",{sessionID:Y.sessionID,kind:Y.kind});return}let Q=Y.response;if(Z.answeredBy&&K.attempt){let q=K.attempt,y=await F2(K,async()=>Z.answeredBy?.({request:Y.request,response:Q,attempt:q}));if(y)U$(K,y)}let G=g(K),_=K.attempt;if(!G||!_)return;K.status=Q.status;let E=Z.quotaFromHeaders?.(Q.headers,Q.status,_);if(E!==void 0)m2(K,"http",E,Q.status);if(!Q.ok&&Z.limitFromResponse){let q=await Z.limitFromResponse({status:Q.status,headers:Q.headers,body:()=>Q.clone().text(),attempt:_});if(q)P2(K,q,"http")}if(!Q.ok||!Q.body)T(K);let C=Q;if(Q.body&&(Z.inspectEvent||Z.onAttemptEnd)){let q=Q.body;if(Z.inspectEvent)q=q.pipeThrough(L2((y)=>g2(K,"http",y.data,y.event),(y)=>I("opencode2 auth event inspection threw",{error:J(y)})));C=new Response(T$(q,(y)=>T(K,y)),{status:Q.status,statusText:Q.statusText,headers:Q.headers})}if(Z.rewriteResponse)C=await Z.rewriteResponse({...G,request:Y.request,response:C,attempt:_})??C;if(C!==Q)Y.response=C},t)),o.push(await $.session.hook("experimental.ws.handshake",(Y)=>{if(j){let K=B(new Headers(Y.headers)),Q=e(Y.sessionID,Y.kind);if(x.set(Y.sessionID,K),w.delete(Q),!K)return}return U(a(Y),async(K)=>{let Q;for(let C of Object.keys(Y.headers)){if(C.toLowerCase()!==$2)continue;Q??=Y.headers[C],delete Y.headers[C]}let G=j?await f(a(Y),"experimental.ws.handshake",K,"ws"):await Z2(a(Y),"experimental.ws.handshake","ws",Q,K);K();let _=g(G),E=G.attempt;if(!_||!E)throw p(G.scope);await F2(G,async()=>{_2(Y.headers,G.headers);let C=Z.rewriteHandshakeURL?.({..._,url:Y.url,attempt:E});if(C!==void 0)Y.url=C;if(N(G.scope,Object.entries(Y.headers)),j)w.add(G.key)})})},t));let u2=Z.rewriteWebSocketFrame?.bind(Z);if(u2)o.push(await $.session.hook("experimental.ws.send",async(Y)=>{let K=a(Y);if(j&&!w.has(e(K.sessionID,K.kind)))return;let Q=f2(K.sessionID,K.kind),G=await u2({...K,attempt:Q?.attempt,frame:Y.frame});if(G!==void 0)Y.frame=G},t));o.push(await $.session.hook("experimental.ws.receive",(Y)=>{if(j&&!w.has(e(Y.sessionID,Y.kind)))return;let K=f2(Y.sessionID,Y.kind);if(!K)return;try{g2(K,"ws",Y.frame)}catch(Q){I("opencode2 auth frame inspection threw",{error:J(Q)})}},t)),o.push(await $.session.hook("retry",async(Y)=>{if(j&&x.get(Y.sessionID)!==!0)return;let K=C$(Y.sessionID);if(!K)return;let Q=Y.decision;if(K==="ambiguous"){await V("retry",{sessionID:Y.sessionID,attempt:Y.attempt,reason:"host-decides",hostDecision:Q,decision:Q});return}let G=K;await U(G.scope,async(_)=>{G.judged=!0;let E=Q,C,q,y=G.attempt;if(G.accountId===void 0||!y)E={retry:!1},C="no-account";else if(G.outputStarted)E={retry:!1},C="output-started";else{if(!G.limit){let n=Z.limitFromError?.(Y.error,y);if(n)P2(G,n,"error")}if(G.ended)await G.ended,_();if(G.limit)await G.limit.delivered,_(),G.rerouted=!0,q={accountId:G.accountId,limit:G.limit.signal},E={retry:!0,delay:0},C="reroute";else C="host-decides"}if(Y.decision=E,E.retry&&G.accountId!==void 0)O.set(G.key,{sessionID:G.scope.sessionID,accountId:G.accountId,...q===void 0?{}:{rerouteFrom:q}});await V("retry",{sessionID:Y.sessionID,...G.accountId===void 0?{}:{accountId:G.accountId},kind:G.scope.kind,attempt:Y.attempt,reason:C,hostDecision:Q,decision:E,...y===void 0?{}:{handle:y}})})},t));let l2=(Y)=>{l(Y),x.delete(Y);for(let K of w)if(JSON.parse(K)[0]===Y)w.delete(K);for(let K of[...H.values()]){if(K.scope.sessionID!==Y)continue;R(K,"its session was forgotten"),H.delete(K.id)}for(let[K,Q]of O)if(Q.sessionID===Y)O.delete(K)},T2=new AbortController,p2=$.event;if(p2)(async()=>{try{for await(let Y of p2.subscribe({signal:T2.signal}))if(Y.type==="session.deleted")l2(Y.data.sessionID)}catch(Y){if(!T2.signal.aborted)I("opencode2 auth stopped listening for session deletion",{error:J(Y)})}})();return{on(Y,K){let Q=c.get(Y);if(!Q)Q=new Set,c.set(Y,Q);let G=K;return Q.add(G),()=>{Q.delete(G)}},accountFor(Y,K){return D(e(Y,K))?.accountId},forgetSession:l2,get size(){return H.size},async dispose(){if(u)return;u=!0;for(let Y of m.keys())l(Y);T2.abort();for(let Y of H.values())R(Y,"the installation was disposed");H.clear(),O.clear(),w.clear(),x.clear(),c.clear(),await Promise.all(o.map(async(Y)=>{try{await Y.dispose()}catch(K){I("opencode2 auth hook did not dispose cleanly",{error:J(K)})}}))}}}var x2={version:o2,"user-agent":r2,originator:"codex_exec"},L$="x-openai-internal-codex-responses-lite",_$=1024,q$=/(?:^|\/)gpt-6-(?:astra|sol|luna)$/i,G2=($)=>typeof $==="object"&&$!==null&&!Array.isArray($),x$=($)=>G2($)&&($.type==="message"||("role"in $))&&$.role==="user",S$=($)=>G2($)&&$.type==="configuration_update";class S2{#$=new Map;apply($,Z,X){if(Z.kind!=="primary")return!1;let z=typeof $.model==="string"?$.model:"";if(!t2.has(z))return!1;if(q$.test(z))return!1;let j=G2($.reasoning)?$.reasoning:void 0,B=typeof j?.effort==="string"?j.effort:void 0;if(!B)return!1;let F=this.#$.get(Z.sessionID);if(F===void 0)return this.#Y(Z.sessionID,B),!1;if(B===F)return!1;let P=Array.isArray($.input)?$.input:void 0;if(!P||P.length===0)return!1;let A=P.findLastIndex(x$);if(A<0){if(X==="http")return!1;return $.reasoning={...j,effort:F},!0}if($.reasoning={...j,effort:F},!S$(P[A-1]))P.splice(A,0,{type:"configuration_update",reasoning:{effort:B}});return!0}forget($){this.#$.delete($)}#Y($,Z){this.#$.set($,Z);while(this.#$.size>_$){let X=this.#$.keys().next().value;if(X===void 0)break;this.#$.delete(X)}}}function E$($){if(typeof $.model!=="string"||!a2.has($.model))return!1;return e2($),!0}function $1($){try{let Z=JSON.parse($);return G2(Z)?Z:void 0}catch{return}}async function Y1($,Z,X,z){if($.method.toUpperCase()!=="POST"||$.body===null)return;if(!/\/responses\/?$/.test(new URL($.url).pathname))return;let j=$1(await $.clone().text());if(!j)return;let B="max_output_tokens"in j;delete j.max_output_tokens;let F=X.apply(j,Z,"http"),P=z&&E$(j);if(!B&&!F&&!P)return;let A=new Headers($.headers);if(A.delete("content-length"),P)A.set(L$,"true");return new Request($,{headers:A,body:JSON.stringify(j)})}function Z1($,Z,X){let z=$1($);if(z?.type!=="response.create")return;let j="max_output_tokens"in z;delete z.max_output_tokens;let B=X.apply(z,Z,"ws");return j||B?JSON.stringify(z):void 0}function R$($){return $.replace(/\/responses\/?$/,"").replace(/\/$/,"")}function E2($,Z){let X=new URL($);if(X.origin!=="https://api.openai.com"&&X.origin!=="wss://api.openai.com"||!/\/responses\/?$/.test(X.pathname))return $;let z=new URL(`${R$(Z)}/responses`);if(z.search=X.search,X.protocol==="ws:"||X.protocol==="wss:")z.protocol=z.protocol==="https:"?"wss:":"ws:";return z.href}var i="openai",D$=60000,K1=1,A$="request refused: no account with a usable credential",O$=1024,Y2=($)=>typeof $==="object"&&$!==null&&!Array.isArray($);function D2($){try{return JSON.parse($)}catch{return}}function z1($){let Z=W2($);return Z?H2(Z):void 0}var b$=new Set(["usage_limit_reached","rate_limit_exceeded"]);function A2($,Z,X){return{reason:$.window,...X!==void 0?{status:X}:{},...$.resetAt!==void 0?{retryAfterMs:Math.max(0,$.resetAt-Z)}:{}}}function k$($,Z){let X=V2($);if(X){let B=typeof $.status==="number"?$.status:void 0;return A2(X,Z,B)}if($.type!=="response.failed"||!Y2($.response))return;let z=$.response.failed;if(Y2(z)&&typeof z.rate_limit_reached_type==="string")return{reason:z.rate_limit_reached_type};let j=$.response.error;if(Y2(j)){let B=typeof j.code==="string"?j.code:typeof j.type==="string"?j.type:void 0;if(B&&b$.has(B))return{reason:B}}return}function y$($,Z){let X=D2($);if(!Y2(X))return;let z=typeof X.type==="string"?X.type:void 0;if(z==="codex.rate_limits")return{quota:{snapshot:Q1(X),complete:!0}};let j=k$(X,Z);if(j)return{limit:j};if(z===void 0||z==="error")return;return _1(z)?void 0:{outputStarted:!0}}function w$($){let Z=!1;for(let X of $.keys())if(X.toLowerCase().startsWith("x-codex-")){Z=!0;break}if(!Z)return;return{snapshot:J1($),complete:j1($)}}function I$($){let Z=$.get("retry-after");if(Z===null)return;let X=Number(Z);return Number.isFinite(X)&&X>=0?X*1000:void 0}function q1($){let{source:Z,pins:X}=$,z=$.now??Date.now,j=$.log,B=$.vault,F=new S2,P=new Map,A=(J)=>`${J.sessionID}\x00${J.kind}\x00${J.accountId}`,H=new Map,W=new Map,O=(J,V)=>{W.delete(J),W.set(J,V);while(W.size>O$){let M=W.keys().next().value;if(M===void 0)break;W.delete(M)}},w=(J,V,M)=>{let T=M?[]:L1(Z.peek().rows,J,V,B?.identities()??new Set),R=(B?.routes()??[]).filter((L)=>L.kind==="oauth"&&B1(B2(L.quota),J,V));return[...T.map((L)=>({id:L.id,...L.identity!==void 0?{identity:L.identity}:{},quota:L.quota,row:L})),...R.map((L)=>({id:L.id,...L.identity!==void 0?{identity:L.identity}:{},quota:L.quota}))]},x=(J,V,M)=>{let T=new Map;if(V1(V))for(let S of J)T.set(S.id,H1(B2(S.quota),V,S.id===R2?void 0:S.id,M));let R=J.map((S)=>({id:S.id,kind:"oauth",...X1(S.quota)?{quota:S.quota}:{}})),L=J.flatMap((S)=>S.row?[S.row]:[]),D=new Set(J.filter((S)=>!S.row).map((S)=>S.id)),k=Z.rateLimitMarks(L);for(let S of D){let g=H.get(S);if(g===void 0)continue;if(g<=M)H.delete(S);else k.set(S,g)}return{rows:R,now:M,rateLimitMarks:k,refreshBackoff:Z.refreshBackoffFor(L),killswitch:T,requestPull:(S)=>D.has(S)?B?.requestReading(S):Z.requestReading(S)}},b=(J,V,M,T)=>{let R=F1({...J,placement:C1(V)});if(R.kind==="block"){j?.debug("pool admission refused the request",{reason:R.block.reason});return}let L=R.order.filter((D)=>!M.has(D));if(T!==void 0&&L.includes(T))return T;return L[0]},r=(J,V,M,T,R,L)=>{let D={requestBytes:K1,reservePercent:(N)=>W1(M,N===R2?void 0:N),resetCredits:()=>{return}},k=P1(J),S=(N,h)=>X.place({sessionId:T,requestBytes:K1,validPinnedAccountIds:J.rows.map((f)=>f.id).filter((f)=>!k.excluded.has(f)),excludeAccountIds:N,quotaCheckedAtByAccount:Object.fromEntries(J.rows.map((f)=>[f.id,k.projections.get(f.id)?.checkedAt])),wireAccountIdByAccount:Object.fromEntries(V.map((f)=>[f.id,f.identity])),select:(f)=>T1(J,D,new Set(N),f),persist:h}),g=[...R],p=S(g,L);if(!p)return;let l=p.accountId,U=M1(J,D,l);if(U.kind==="none")return;if(U.kind==="move"){let N=S([...g,l],L);if(N)return N.accountId;return U.reason==="killswitch"?void 0:l}if(U.kind==="detour")return S([...g,l],!1)?.accountId;return l},u={providerID:i,chooseAccount:async(J)=>{let V=await B?.authority(),M=await $.storage(),T=V!==void 0&&V!=="local",R=()=>{throw new d({kind:"no-account",providerID:i,sessionID:J.sessionID,requestKind:J.kind,message:U2})};if(T)await $.awaitVaultRoster?.();else{if(!(await Z.current()).active){if(await $.slotPlaceholderWithoutMain?.())R();j?.warn("the account pool does not serve requests yet; refusing the request",{kind:J.kind});return}await Z.prepareTokens(Z.peek().rows,M)}let L=z(),D=w(M,L,T),k=new Map(D.map((h)=>[h.id,h])),S=x(D,M,L),g=new Set(J.rerouteFrom?[J.rerouteFrom.accountId]:[]),p=M?.routing?.mode??"main-first",l=J.kind==="primary",U=new Set,N;for(;;){let h=new Set([...g,...U]);if(N=void 0,p==="sticky-balanced")N=r(S,D,M,J.sessionID,h,l&&U.size===0);if(N??=b(S,p,h,l?void 0:W.get(J.sessionID)),N===void 0||U.has(N)){N=void 0;break}let f=k.get(N)?.row;if(f){if(Z.usableToken(f)!==void 0)break;j?.debug("pool row holds no usable token; choosing again",{accountId:N})}else{if(!B)break;let Z2=await B.authorize(N);if(Z2){P.set(A({...J,accountId:N}),Z2);break}j?.debug("the vault refused to serve an account; choosing again",{accountId:N})}U.add(N)}if(N===void 0&&!T&&D.length===0&&await $.slotPlaceholderWithoutMain?.())R();if(N===void 0&&T&&B&&(D.length===0||U.size>0)){let h=U.size>0?"vault-refused":N1(B);throw j?.warn("vault mode: no vault account can serve; refusing the request",{cause:h}),new d({kind:"no-account",providerID:i,sessionID:J.sessionID,requestKind:J.kind,message:U1[h]})}if(N!==void 0&&l)O(J.sessionID,N);return j?.debug("pool account chosen",{kind:J.kind,accountId:N,mode:p,...J.rerouteFrom?{rerouteFrom:J.rerouteFrom.accountId}:{}}),N},accountHeaders:async(J)=>{let{accountId:V}=J,M=()=>new d({kind:"no-account",providerID:i,sessionID:J.sessionID,requestKind:J.kind,message:A$}),T=B&&await B.authority()!=="local"?void 0:Z.peek().rows.find((D)=>D.id===V);if(!T&&B){let D=A(J),k=P.get(D)??await B.authorize(V);if(P.delete(D),!k)throw j?.warn("the vault refused to serve an account; refusing the request",{accountId:V}),M();return{headers:{...x2,"session-id":J.sessionID,authorization:`Bearer ${k.accessToken}`,"chatgpt-account-id":k.accountIdentity??z1(k.accessToken)??null},attempt:{kind:"vault",routeId:V,receipt:k}}}let R=T?Z.usableToken(T):void 0;if(!T||!R)throw j?.warn("pool row holds no usable token; refusing the request",{accountId:V}),M();let L=T.identity??z1(R);return{headers:{...x2,"session-id":J.sessionID,authorization:`Bearer ${R}`,"chatgpt-account-id":L??null},attempt:{kind:"pool",token:R,credentialEpoch:T.credentialEpoch}}},async rewriteRequest({request:J,sessionID:V,kind:M}){let T=$.codexEndpoint?E2(J.url,$.codexEndpoint()):J.url,R=T===J.url?J:new Request(T,J);return await Y1(R,{sessionID:V,kind:M},F,$.responsesLite?.()??!1)??R},rewriteHandshakeURL:({url:J})=>$.codexEndpoint?E2(J,$.codexEndpoint()):void 0,rewriteWebSocketFrame:({frame:J,sessionID:V,kind:M})=>Z1(J,{sessionID:V,kind:M},F),quotaFromHeaders:(J)=>w$(J),async limitFromResponse({status:J,headers:V,body:M}){if(J<400)return;let T=D2(await M().catch(()=>"")),R=V2({...Y2(T)?T:{},status:J});if(!R)return;let L=z(),D=A2(R,L,J);if(D.retryAfterMs!==void 0)return D;let k=I$(V);return k===void 0?D:{...D,retryAfterMs:k}},inspectEvent:({data:J})=>y$(J,z()),limitFromError(J){let V=D2(J.message),M=V2({...Y2(V)?V:{},...J.status!==void 0?{status:J.status}:{}})??(J.message.includes("usage_limit_reached")?{window:"usage_limit_reached"}:void 0);return M?A2(M,z(),J.status):void 0},async onAttemptEnd(J,V){let M=J.data;if(M?.kind!=="vault"||V.status!==401)return;j?.warn("a vault account answered 401; reporting it to the vault",{accountId:J.accountId}),await B?.reportFailure(M.receipt,401)}},m=(J,V,M)=>{let T=M.data;if(T?.kind==="vault"){B?.recordSnapshot(J,V.snapshot,V.complete,T.receipt);return}if(T?.kind!=="pool")return;if(Z.peek().rows.find((L)=>L.id===J)?.credentialEpoch!==T.credentialEpoch){j?.debug("quota reading dropped: the row holds another credential",{accountId:J});return}Z.recordSnapshot(J,V.snapshot,T.token,V.complete)},I=(J,V,M)=>{let T=z(),R=M.data?.kind==="vault",L=R?B?.routes().find((S)=>S.id===J)?.quota:Z.peek().rows.find((S)=>S.id===J)?.quota,D=G1(B2(L),V.reason,T,D$,V.retryAfterMs!==void 0?T+V.retryAfterMs:void 0);if(!R){Z.markRateLimited(J,D);return}let k=H.get(J);if(k===void 0||k<D)H.set(J,D)};return{adapter:u,attach(J){J.on("quota",(V)=>m(V.accountId,V.quota,V.handle)),J.on("limit",(V)=>{I(V.accountId,V.limit,V.handle),j?.debug("pool row refused the request",{accountId:V.accountId,via:V.via,reason:V.limit.reason,outputStarted:V.outputStarted})})},forgetSession(J){X.forget(J),W.delete(J),F.forget(J)}}}import{randomUUID as v$}from"node:crypto";import{chmod as h$,mkdir as f$,readFile as m$,rename as g$,rm as u$,stat as l$,writeFile as p$}from"node:fs/promises";import{dirname as c$,resolve as d$}from"node:path";var i$="(no OpenCode 1 logins on this machine)";function n$($=process.env){return x1($)}async function O2($){let Z;try{Z=await m$($,"utf8")}catch(z){if(z.code==="ENOENT")return;throw z}let X=JSON.parse(Z);if(!X||typeof X!=="object"||Array.isArray(X))throw Error(`${$} does not hold a JSON object`);return X}function b2($){return $===void 0?void 0:JSON.stringify($)}async function s$($){try{return(await l$($)).mode&511}catch(Z){if(Z.code==="ENOENT")return;throw Z}}function S1($=n$()){let Z=new Map;return{path:d$($),async get(X){let z=(await O2($))?.[X.path.id];return Z.set(X.path.id,b2(z)),z},async set(X){let z=X.path.id,j=await s$($),B=await O2($)??{};if(!Z.has(z)||Z.get(z)!==b2(B[z]))throw new k2;B[z]=X.body,await f$(c$($),{recursive:!0});let F=`${$}.${v$()}.tmp`;try{await p$(F,`${JSON.stringify(B,null,2)}
|
|
4
|
+
`,{mode:j??384}),await h$(F,j??384),await g$(F,$)}catch(P){throw await u$(F,{force:!0}).catch(()=>{}),P}return Z.set(z,b2(X.body)),!0},async all(){let X=await O2($);if(!X||Object.keys(X).length===0)return{[i$]:!0};return X}}}var a$="openai-auth-pool-browser",o$="openai-auth-pool-headless",r$="openai-auth-vault",t$="Vault login requires this OpenCode host to be enrolled in the Claustrum vault. Connect to the vault or disconnect this host before trying again.";async function X2($){if(await $.authority()!=="local")throw Error("This host is connected to the credential vault or its enrollment is unreadable; the login was not stored. Disconnect this host from the vault to add a local login.")}function b1($){return{method:{id:r$,type:"oauth",label:"ChatGPT accounts in the vault"},async activate(){if(await E1($.paths,$.name)!=="vault")throw Error(t$)}}}function k1($){let Z=(X,z,j)=>({method:{id:X,type:"oauth",label:z},async authorize(){if($.vault)await X2($.vault);let B=await $.beginLogin({version:$.version,headless:j});return{url:B.url,instructions:B.instructions,mode:"auto",callback:B.completion}}});return[Z(a$,"ChatGPT (openai-auth pool, browser)",!1),Z(o$,"ChatGPT (openai-auth pool, headless)",!0)]}function y2($){let Z=(X)=>typeof X==="string"?X:"";return z2({type:"oauth",access:Z($.access),refresh:Z($.refresh)})||$.refresh===A1||R1($)}function e$($){if($.accountId)return $.accountId;let Z=$.access?W2($.access):void 0;return Z?H2(Z):void 0}async function y1($,Z,X,z={}){if(z.vault)await X2(z.vault);if(y2(X))throw Error("the credential is not a ChatGPT login (a placeholder or a tombstone); it was not stored");let j=D1($,{rowLocks:(x)=>O1(Z,x)}),B=await j.read();if(B.status!=="ready")throw Error(`the account pool cannot be read (${B.status}); the login was not stored`);let F={type:"oauth",refresh:X.refresh,...X.access!==void 0?{access:X.access}:{},...X.expires!==void 0?{expires:X.expires}:{}},P=e$(X);if(z.vault)await X2(z.vault);let A=P!==void 0?{identity:P}:{};if(z.origin==="import"){let x=B.rows.find((b)=>P!==void 0&&b.identity===P||b.credential?.type==="oauth"&&b.credential.refresh===X.refresh);if(x)return{id:x.id,operation:"kept"}}let H=B.rows.filter((x)=>x.type==="oauth"&&!x.invalid),W=P?H.find((x)=>x.identity===P):void 0,O=B.rows.find((x)=>x.id===N2);if(W)return{id:(await j.replace(W.id,F,A)).id,operation:"replaced"};if(!O){let x=await j.add({id:N2,credential:F,...A,...X.label!==void 0?{label:X.label}:{}});return{id:x.id,operation:x.outcome==="added-disabled"?"added-disabled":"added"}}if(!O.invalid&&!O.credential)return{id:(await j.replace(N2,F,A)).id,operation:"replaced"};let w=await j.add({id:X.id,credential:F,...A,...X.label!==void 0?{label:X.label}:{}});return{id:w.id,operation:w.outcome==="added-disabled"?"added-disabled":w.outcome==="rotated"?"replaced":"added"}}function $Y($){if(!w1($.modelID??$.id)){$.enabled=!1;return}let Z=I1($.id);if(Z)$.limit={...Z}}function v1($){return $.model.transform((Z)=>{for(let X of Z.list(i))Z.update(i,String(X.id),(z)=>$Y(z))})}var YY=1024;class w2{pins=new Map;limit;constructor($=YY){this.limit=Math.max(1,$)}get($){return this.pins.get($)?.accountId}forget($){this.pins.delete($)}get size(){return this.pins.size}set($,Z){this.pins.delete($),this.pins.set($,Z);while(this.pins.size>this.limit){let X=this.pins.keys().next().value;if(X===void 0)break;this.pins.delete(X)}}place($){let Z=this.pins.get($.sessionId),X=new Set($.validPinnedAccountIds);if(Z&&!$.excludeAccountIds.includes(Z.accountId)&&h1(Z,X,$.wireAccountIdByAccount[Z.accountId])){if($.persist){let F=$.quotaCheckedAtByAccount[Z.accountId];this.set($.sessionId,{accountId:Z.accountId,inputBytes:$.requestBytes,...Z.wireIdentity!==void 0?{wireIdentity:Z.wireIdentity}:{},...F!==void 0?{quotaCheckedAt:F}:{}})}return{accountId:Z.accountId}}let z=new Map(Object.entries($.quotaCheckedAtByAccount)),j=f1(this.pins,z,$.sessionId),B=$.select(j);if(!B)return;if($.persist){let F=$.wireAccountIdByAccount[B.accountId];this.set($.sessionId,{accountId:B.accountId,inputBytes:$.requestBytes,...F!==void 0?{wireIdentity:F}:{},...B.quotaCheckedAt!==void 0?{quotaCheckedAt:B.quotaCheckedAt}:{}})}return{accountId:B.accountId}}}var C2="cortexkit-openai-auth",ZY=5000;async function m1($,Z,X){let z=new Map,j=new Map,B=(H)=>{j.delete(H);for(let W of z.keys())if(W.startsWith(`${H}:`))z.delete(W)},P=await q2({session:{hook:(H,W,O)=>$.session.hook(H,async(w)=>{let x=H==="http.request"?w:void 0,b=H==="experimental.ws.handshake"?w:void 0,c=(x?x.request.headers:b?new Headers(b.headers):void 0)?.get("authorization")===`Bearer ${s(Z.providerID)}`;try{await W(w)}catch(v){if(c&&!(v instanceof d))throw o1(v);throw v}if(x||b){let v=x??b;if(!v)return;let m=(x?x.request.headers:new Headers(v.headers)).get("authorization")?.replace(/^Bearer /,"");if(c&&m)j.set(v.sessionID,m);else j.delete(v.sessionID);if(b){let I=`${b.sessionID}:${b.kind}`;if(c&&m)z.set(I,m);else z.delete(I)}if(j.size>512){let I=j.keys().next().value;if(I)j.delete(I)}if(z.size>512){let I=z.keys().next().value;if(I)z.delete(I)}}else if(H==="experimental.ws.receive"){let v=w,u=z.get(`${v.sessionID}:${v.kind}`);if(u)v.frame=r1(v.frame,u)}else if(H==="retry"){let v=w,u=j.get(v.sessionID);if(u)Object.assign(v.error,{message:a1(v.error.message,u)})}},O)},event:{subscribe:(H)=>async function*(){for await(let W of $.event.subscribe(H)){if(W.type==="session.deleted")B(W.data.sessionID);yield W}}()}},{...Z,rewriteResponse:async(H)=>{let W=await Z.rewriteResponse?.(H)??H.response;return t1(W,H.request.headers.get("authorization")?.replace(/^Bearer /,""))}},{gateOnPlaceholder:{credential:(H)=>{let W=H.get("authorization");return W?.startsWith("Bearer ")?W.slice(7):void 0}},logger:{warn:(H,W)=>X.warn(H,{data:W})}}),A=P.dispose;return{...P,dispose:async()=>{j.clear(),z.clear(),await A()}}}function h2($={}){return{id:C2,setup:(Z)=>KY(Z,$)}}async function KY($,Z={}){let S=[];try{let X=J2("opencode2");let z=J2("quota");let j=Z.paths??(()=>$$(e1()));let B=Z.fetch??fetch;let F=Z.version??X$;let P=Z.poolMigration??W$;if(!P&&!j2(j().configPath)){X.info("openai-auth stands aside on OpenCode 2: this install has not moved into the account pool and the migration is switched off, so OpenCode 2 serves its own ChatGPT login");let U=(h)=>{throw new d({kind:"no-account",providerID:i,sessionID:h.sessionID,requestKind:h.kind,message:U2})},N=await m1($,{providerID:i,chooseAccount:U,accountHeaders:U},X);return()=>N.dispose()}let A=Z.vault?.firstRosterWaitMs??j$;let H=V$({host:"opencode",stateDir:Z.vault?.stateDir??s1(j().statePath),reservedRouteIds:()=>b.peek().rows.map((U)=>U.id),...Z.vault?.connectionFile?{connectionFile:Z.vault.connectionFile}:{},...Z.vault?.connectScoped?{connectScoped:Z.vault.connectScoped}:{},...Z.vault?.connectEnrollment?{connectEnrollment:Z.vault.connectEnrollment}:{},...Z.vault?.pollIntervalMs!==void 0?{pollIntervalMs:Z.vault.pollIntervalMs}:{},fetchImpl:()=>B},j());let W=H.vault;let O=!1;const w=g1(S,{[Symbol.dispose](){if(!O)H.release()}},0);await W.authority();let x=H.firstRoster;let b=new Q$({paths:j,refreshProvider:async(U)=>{let N=await c1({refreshToken:U.refresh,fetchImpl:B,now:Date.now}),h=l1({id_token:"",access_token:N.access,refresh_token:N.refresh});return{...N,...h?{identity:h}:{}}},pullQuota:async(U)=>{let N=U.credential;if(N.type!=="oauth"||!N.access)throw Error("the row holds no access token to poll with");let h=await d1({accessToken:N.access,fetchImpl:B,now:Date.now,...U.identity?{accountId:U.identity}:{},accountKey:U.id,logger:z});return n1(h,Date.now(),!0)},vaultIdentities:()=>W.identities(),vaultMode:()=>W.enrolled(),vaultAuthority:()=>W.authority(),vaultFirstRoster:x,vaultFirstRosterBackgroundWaitMs:A,log:J2("pool")});await b.load();let r=Z.heartbeat===!1?void 0:await B$({version:F,logger:J2("heartbeat")});let c=Z.fence??(()=>z$({currentVersion:F}));let v=Z.slot??S1();let u=async()=>Y$(j(),await v.get({path:{id:"openai"}}));let m=!1;let I=P?G$({paths:j,slot:v,version:F,fence:c,runDeps:{vaultServes:()=>W.serves()},paused:async()=>await W.authority()!=="local",migrate:async(U)=>{let N=await Z$(U);return m=N.status==="refused",b.load(),N},adopt:async(U)=>{if(!await v2(x,A))return{status:"retry",reason:"vault-roster-pending"};let N=await K$(U);return b.load(),N}}):void 0;I?.start();H.start();let J=new w2;let V=q1({source:b,slotPlaceholderWithoutMain:u,storage:()=>i1(j()),pins:J,vault:W,awaitVaultRoster:async()=>{await v2(x,J$)},responsesLite:()=>I2().responsesLite,codexEndpoint:()=>I2().codexApiEndpoint,log:X});let M=await m1($,V.adapter,X);V.attach(M);let T=async(U)=>{if(await X2(W),j2(j().configPath))return;if(await I?.idle(),j2(j().configPath))return;if(m&&!y2(U)&&(await c()).open){await b.poolStore().initialize();return}throw Error("OpenAI accounts move to the shared account pool once every OpenCode process on this machine runs this version of openai-auth; the login was not stored. Update or close the older processes and sign in again.")};let R=async(U)=>{await T(U);let N=await y1(b.poolStore(),j(),U,{vault:W});if(!j2(j().configPath))await I?.requestAdoption();await b.load(),X.info("ChatGPT login stored in the account pool",N)};let L=await M2($,{integrationID:i,methods:k1({beginLogin:Z.beginLogin??p1,version:F,vault:W}),onLogin:(U)=>R(U),label:"ChatGPT (openai-auth account pool)",vault:b1(W)});let D=await v1($);let k=new AbortController;(async()=>{try{for await(let U of $.event.subscribe({signal:k.signal}))if(U.type==="session.deleted")V.forgetSession(U.data.sessionID)}catch(U){if(!k.signal.aborted)X.warn("stopped listening for session deletion",{error:U instanceof Error?U.message:String(U)})}})();O=!0;return async()=>{k.abort(),I?.dispose(),await Promise.allSettled([M.dispose(),L.dispose(),D.dispose()]),await Promise.race([b.settled(),new Promise((U)=>setTimeout(U,ZY).unref?.())]),H.release(),b.dispose(),await r?.release()}}catch(g){var p=g,l=1}finally{u1(S,p,l)}}var zY=h2(),_Z={id:C2,setup:zY.setup,server:H$.server};export{_Z as default,h2 as createOpenAIAuthPlugin,C2 as OPENAI_AUTH_PLUGIN_ID};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-openai-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"./tui-prefs": {
|
|
23
23
|
"types": "./dist/tui-preferences.d.ts",
|
|
24
24
|
"import": "./dist/tui-preferences.js"
|
|
25
|
+
},
|
|
26
|
+
"./server": {
|
|
27
|
+
"types": "./dist/v2/server.d.ts",
|
|
28
|
+
"import": "./dist/v2/server.js"
|
|
25
29
|
}
|
|
26
30
|
},
|
|
27
31
|
"oc-plugin": [
|
|
@@ -39,9 +43,9 @@
|
|
|
39
43
|
"LICENSE"
|
|
40
44
|
],
|
|
41
45
|
"scripts": {
|
|
42
|
-
"build": "rm -rf dist && bun build src/index.ts src/sidebar-state.ts src/tui-preferences.ts src/rpc/rpc-client.ts src/rpc/port-file.ts src/rpc/protocol.ts src/rpc/rpc-dir.ts --outdir dist --target node --format esm --splitting --external @opencode-ai/plugin --minify && bun run build:types && bun run build:tui",
|
|
46
|
+
"build": "rm -rf dist && bun build src/index.ts src/v2/server.ts src/sidebar-state.ts src/tui-preferences.ts src/rpc/rpc-client.ts src/rpc/port-file.ts src/rpc/protocol.ts src/rpc/rpc-dir.ts --outdir dist --target node --format esm --splitting --external @opencode-ai/plugin --external @opencode/plugin --minify && bun run build:types && bun run build:tui",
|
|
43
47
|
"build:tui": "bun scripts/build-tui.ts",
|
|
44
|
-
"build:types": "cd ../core && bun run build && cd ../opencode && tsdown --config tsdown.config.ts",
|
|
48
|
+
"build:types": "cd ../core && bun run build && cd ../opencode && tsdown --config tsdown.config.ts --config-loader native",
|
|
45
49
|
"build:dev": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
46
50
|
"test": "bun test src/tests",
|
|
47
51
|
"typecheck": "tsc",
|
|
@@ -49,17 +53,25 @@
|
|
|
49
53
|
"prepublishOnly": "bun run build"
|
|
50
54
|
},
|
|
51
55
|
"peerDependencies": {
|
|
52
|
-
"@opencode-ai/plugin": "*"
|
|
56
|
+
"@opencode-ai/plugin": "*",
|
|
57
|
+
"@opencode/plugin": ">=2.0.21"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"@opencode/plugin": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
53
63
|
},
|
|
54
64
|
"devDependencies": {
|
|
55
65
|
"@ai-sdk/openai": "3.0.88",
|
|
56
|
-
"@cortexkit/
|
|
66
|
+
"@cortexkit/claustrum-client": "^0.6.2",
|
|
67
|
+
"@cortexkit/common-auth": "^0.14.2",
|
|
68
|
+
"@cortexkit/openai-auth-core": "workspace:*",
|
|
69
|
+
"@opencode/plugin": "2.0.22"
|
|
57
70
|
},
|
|
58
71
|
"dependencies": {
|
|
59
|
-
"@
|
|
60
|
-
"@opentui/
|
|
61
|
-
"
|
|
62
|
-
"ai": "^7.0.107",
|
|
72
|
+
"@opentui/core": ">=0.5.14",
|
|
73
|
+
"@opentui/solid": ">=0.5.14",
|
|
74
|
+
"ai": "^7.0.127",
|
|
63
75
|
"jsonc-parser": "^3.3.1",
|
|
64
76
|
"solid-js": "1.9.12"
|
|
65
77
|
}
|
package/src/tui/entry.mjs
CHANGED
|
@@ -1,37 +1,38 @@
|
|
|
1
1
|
// Prefer the host OpenTUI runtime registry when it exists. OpenTUI 0.4.x
|
|
2
2
|
// registers these virtual modules process-wide, allowing the precompiled TUI to
|
|
3
3
|
// share the host's single Solid/OpenTUI runtime when loaded from node_modules.
|
|
4
|
-
|
|
4
|
+
// Older hosts and bare Bun do not provide the virtual registry. Their source
|
|
5
|
+
// loader still applies the Solid transform, so the raw TSX variant is the
|
|
6
|
+
// fallback. The selector that makes this choice is emitted by the TUI build
|
|
7
|
+
// next to each variant, so it resolves inside the published package.
|
|
8
|
+
import { loadTui } from '../tui-compiled/runtime/selector.js'
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/Cannot find|Could not resolve|Module not found|Unable to resolve/.test(
|
|
10
|
-
message,
|
|
11
|
-
) && message.includes('opentui:runtime-module:')
|
|
12
|
-
)
|
|
13
|
-
}
|
|
10
|
+
const rawEntry = new URL('../tui-compiled/raw/tui.tsx', import.meta.url).href
|
|
11
|
+
const runtimeEntry = new URL('../tui-compiled/runtime/tui.js', import.meta.url)
|
|
12
|
+
.href
|
|
14
13
|
|
|
14
|
+
// Which import the selector ran last: the registry probe, or one variant.
|
|
15
|
+
let stage = 'probe'
|
|
15
16
|
let mod
|
|
16
17
|
try {
|
|
17
|
-
await
|
|
18
|
+
mod = await loadTui({
|
|
19
|
+
rawEntry,
|
|
20
|
+
runtimeEntry,
|
|
21
|
+
importModule: (specifier) => {
|
|
22
|
+
if (specifier === rawEntry) stage = 'raw'
|
|
23
|
+
else if (specifier === runtimeEntry) stage = 'runtime'
|
|
24
|
+
return import(specifier)
|
|
25
|
+
},
|
|
26
|
+
})
|
|
18
27
|
} catch (error) {
|
|
19
|
-
|
|
28
|
+
// A probe failure other than a missing registry, and a failed runtime
|
|
29
|
+
// variant, are reported before rethrowing; a raw-variant failure propagates
|
|
30
|
+
// as it is.
|
|
31
|
+
if (stage === 'probe')
|
|
20
32
|
console.error('OpenAI Auth TUI runtime registry probe failed', error)
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
// Older hosts and bare Bun do not provide the virtual registry. Their source
|
|
24
|
-
// loader still applies the Solid transform, so retain the raw TSX fallback.
|
|
25
|
-
mod = await import('../tui-compiled/raw/tui.tsx')
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!mod) {
|
|
29
|
-
try {
|
|
30
|
-
mod = await import('../tui-compiled/runtime/tui.tsx')
|
|
31
|
-
} catch (error) {
|
|
33
|
+
else if (stage === 'runtime')
|
|
32
34
|
console.error('OpenAI Auth compiled TUI failed to load', error)
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
+
throw error
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
export default mod
|
|
38
|
+
export default mod
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { tmpdir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
flushLogs,
|
|
5
|
+
initLogger
|
|
6
|
+
} from "./shared/3c2d4169d00f907e-tui-support.js";
|
|
7
|
+
export {
|
|
8
|
+
createLogger,
|
|
9
|
+
flushForTest,
|
|
10
|
+
flushLogs,
|
|
11
|
+
redact,
|
|
12
|
+
redactStrings,
|
|
13
|
+
setLogLevel
|
|
14
|
+
} from "./shared/3c2d4169d00f907e-tui-support.js";
|
|
15
|
+
const LEVELS = ["error", "warn", "info", "debug", "trace"];
|
|
16
|
+
export function getLogFile() {
|
|
17
|
+
return process.env.OPENCODE_OPENAI_AUTH_LOG_FILE ?? join(tmpdir(), "opencode-openai-auth.log");
|
|
18
|
+
}
|
|
19
|
+
export function getEnvLogLevel() {
|
|
20
|
+
const env = process.env.OPENCODE_OPENAI_AUTH_LOG_LEVEL;
|
|
21
|
+
return env && LEVELS.includes(env) ? env : void 0;
|
|
22
|
+
}
|
|
23
|
+
initLogger({ file: getLogFile, level: getEnvLogLevel });
|
|
24
|
+
process.on("exit", flushLogs);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRpcClient as createCommonRpcClient
|
|
3
|
+
} from "../shared/46f3dff1266160e8-client.js";
|
|
4
|
+
export { DEFAULT_RPC_TIMEOUT_MS } from "../shared/46f3dff1266160e8-client.js";
|
|
5
|
+
export function createRpcClient(dir, expectedPid, onSelected) {
|
|
6
|
+
return createCommonRpcClient(dir, expectedPid, onSelected);
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { homedir, tmpdir } from "node:os";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
const RPC_DIR_ENV = "OPENCODE_OPENAI_AUTH_RPC_DIR";
|
|
5
|
+
export const RPC_DIRECTORY_PREFIX = "openai-auth-";
|
|
6
|
+
function rpcHash(projectDirectory) {
|
|
7
|
+
return createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
8
|
+
}
|
|
9
|
+
function defaultRpcRoot() {
|
|
10
|
+
const base = process.env.XDG_STATE_HOME || join(homedir(), ".local", "state");
|
|
11
|
+
return join(base, "cortexkit", "openai-auth", "rpc");
|
|
12
|
+
}
|
|
13
|
+
export function getRpcDir(projectDirectory) {
|
|
14
|
+
const override = process.env[RPC_DIR_ENV]?.trim();
|
|
15
|
+
if (override)
|
|
16
|
+
return resolve(projectDirectory, override);
|
|
17
|
+
return join(defaultRpcRoot(), `${RPC_DIRECTORY_PREFIX}${rpcHash(projectDirectory)}`);
|
|
18
|
+
}
|
|
19
|
+
export async function resolveRpcDir(projectDirectory) {
|
|
20
|
+
const override = process.env[RPC_DIR_ENV]?.trim();
|
|
21
|
+
if (override)
|
|
22
|
+
return { dir: resolve(projectDirectory, override), secureDir: !1 };
|
|
23
|
+
const dir = getRpcDir(projectDirectory);
|
|
24
|
+
return { dir, secureDir: !0, sweepRoot: dirname(dir) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { tmpdir };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isAbsolute } from 'node:path';
|
|
2
|
+
export async function loadTui({ rawEntry, runtimeEntry, importModule = (entry) => import(entry), }) {
|
|
3
|
+
for (const [option, entry] of Object.entries({ rawEntry, runtimeEntry })) {
|
|
4
|
+
if (!isAbsolute(entry) &&
|
|
5
|
+
!(entry.startsWith('file:') && new URL(entry).pathname.startsWith('/'))) {
|
|
6
|
+
throw new Error(`${option} must be an absolute path or file URL: ${entry}`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
let entry = runtimeEntry;
|
|
10
|
+
try {
|
|
11
|
+
await importModule(`opentui:runtime-module:${encodeURIComponent('@opentui/solid')}`);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15
|
+
if (!/Cannot find|Could not resolve|Module not found|Unable to resolve/.test(message) ||
|
|
16
|
+
!message.includes('opentui:runtime-module:'))
|
|
17
|
+
throw error;
|
|
18
|
+
entry = rawEntry;
|
|
19
|
+
}
|
|
20
|
+
return (await importModule(entry)).default;
|
|
21
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// The one quota wording every surface shows: the command menu's dialogs on
|
|
2
|
+
// OpenCode and Pi, the terminal auth menu, and the plugins' sidebars. Each of
|
|
3
|
+
// them calls `formatQuota` (or `quotaTextParts` for one line per window), so
|
|
4
|
+
// the wording cannot drift between plugins or between a dialog and a sidebar.
|
|
5
|
+
//
|
|
6
|
+
// The rules: a window is named by its length (`5h`, `7d`), or, for a window
|
|
7
|
+
// that binds only one model family, by the label the plugin stored it under.
|
|
8
|
+
// Every figure is "% left". A window the provider did not report (an absence
|
|
9
|
+
// record or a retirement tombstone) is left out rather than written as "not
|
|
10
|
+
// reported". A reset is a short time from now (`resets 2h`). A reading older
|
|
11
|
+
// than fifteen minutes says how old it is (`checked 3h ago`).
|
|
12
|
+
//
|
|
13
|
+
// Like the rest of this module it reads no clock: `now` is a parameter.
|
|
14
|
+
import { ALL_SCOPE } from "./905bffc0220f43d0-map.js";
|
|
15
|
+
/** A reading older than this is marked with its age. */
|
|
16
|
+
export const QUOTA_STALE_AFTER_MS = 15 * 60_000;
|
|
17
|
+
/** What `formatQuota` returns when there is no reading at all. */
|
|
18
|
+
export const NO_QUOTA_READING = 'no quota reading yet';
|
|
19
|
+
/**
|
|
20
|
+
* What `formatQuota` returns when the provider answered but reported no
|
|
21
|
+
* limit for any window (every window is an absence record or a tombstone).
|
|
22
|
+
*/
|
|
23
|
+
export const NO_LIMITS_REPORTED = 'no limits reported';
|
|
24
|
+
/**
|
|
25
|
+
* A length or a time span in its largest whole-ish unit: `45m`, `5h`, `3d`.
|
|
26
|
+
* Spans under an hour are minutes, under a day hours, otherwise days, each
|
|
27
|
+
* rounded to the nearest unit and never shown as zero.
|
|
28
|
+
*/
|
|
29
|
+
function shortSpan(ms) {
|
|
30
|
+
const minutes = Math.max(1, Math.round(ms / 60_000));
|
|
31
|
+
if (minutes < 60)
|
|
32
|
+
return `${minutes}m`;
|
|
33
|
+
const hours = Math.round(minutes / 60);
|
|
34
|
+
if (hours < 24)
|
|
35
|
+
return `${hours}h`;
|
|
36
|
+
return `${Math.max(1, Math.round(minutes / 1440))}d`;
|
|
37
|
+
}
|
|
38
|
+
/** A stored window length in minutes as its exact name: `5h`, `7d`, `90m`. */
|
|
39
|
+
function lengthName(minutes) {
|
|
40
|
+
if (minutes % 1440 === 0)
|
|
41
|
+
return `${minutes / 1440}d`;
|
|
42
|
+
if (minutes % 60 === 0)
|
|
43
|
+
return `${minutes / 60}h`;
|
|
44
|
+
return `${minutes}m`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The name a window is shown under. A general window (scope `all`) is named
|
|
48
|
+
* by its stored length; a model-scoped window keeps the label the plugin
|
|
49
|
+
* stored it under, which already names the model (`seven_day_opus`). A
|
|
50
|
+
* general window stored without a length (a snapshot from before lengths
|
|
51
|
+
* were recorded) falls back to its label.
|
|
52
|
+
*/
|
|
53
|
+
export function quotaWindowName(limit) {
|
|
54
|
+
if (limit.scope !== ALL_SCOPE || limit.windowMinutes === undefined)
|
|
55
|
+
return limit.label;
|
|
56
|
+
return lengthName(limit.windowMinutes);
|
|
57
|
+
}
|
|
58
|
+
function percentLeft(remaining) {
|
|
59
|
+
return `${Math.round(Math.min(100, Math.max(0, remaining)))}% left`;
|
|
60
|
+
}
|
|
61
|
+
function resetText(resetsAt, now) {
|
|
62
|
+
if (resetsAt === undefined)
|
|
63
|
+
return '';
|
|
64
|
+
const at = Date.parse(resetsAt);
|
|
65
|
+
// A reset already passed says nothing about the future; leave it out.
|
|
66
|
+
if (!Number.isFinite(at) || at <= now)
|
|
67
|
+
return '';
|
|
68
|
+
return `, resets ${shortSpan(at - now)}`;
|
|
69
|
+
}
|
|
70
|
+
/** Shortest window first, so `5h` reads before `7d`; unknown lengths last. */
|
|
71
|
+
function displayOrder(left, right) {
|
|
72
|
+
const l = left.windowMinutes;
|
|
73
|
+
const r = right.windowMinutes;
|
|
74
|
+
if (l !== undefined && r !== undefined && l !== r)
|
|
75
|
+
return l - r;
|
|
76
|
+
if ((l === undefined) !== (r === undefined))
|
|
77
|
+
return l === undefined ? 1 : -1;
|
|
78
|
+
if (left.scope !== right.scope) {
|
|
79
|
+
if (left.scope === ALL_SCOPE)
|
|
80
|
+
return -1;
|
|
81
|
+
if (right.scope === ALL_SCOPE)
|
|
82
|
+
return 1;
|
|
83
|
+
return left.scope < right.scope ? -1 : 1;
|
|
84
|
+
}
|
|
85
|
+
return left.label < right.label ? -1 : left.label > right.label ? 1 : 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The pieces of a quota text, in order: one per reported window
|
|
89
|
+
* (`5h 58% left, resets 2h`), then the credit budget when there is one, then
|
|
90
|
+
* `checked 3h ago` when the oldest piece shown is stale. Empty when nothing
|
|
91
|
+
* was reported; `formatQuota` turns that into a sentence. A caller printing
|
|
92
|
+
* one line per window (the terminal auth menu) uses these directly.
|
|
93
|
+
*/
|
|
94
|
+
export function quotaTextParts(quota, options) {
|
|
95
|
+
if (!quota)
|
|
96
|
+
return [];
|
|
97
|
+
const { now } = options;
|
|
98
|
+
const full = (options.form ?? 'full') === 'full';
|
|
99
|
+
const parts = [];
|
|
100
|
+
const times = [];
|
|
101
|
+
const readings = quota.limits
|
|
102
|
+
.filter((limit) => limit.kind === 'reading' && limit.remainingPercent !== undefined)
|
|
103
|
+
.sort(displayOrder);
|
|
104
|
+
for (const limit of readings) {
|
|
105
|
+
times.push(limit.checkedAt);
|
|
106
|
+
parts.push(`${quotaWindowName(limit)} ${percentLeft(limit.remainingPercent ?? 0)}${full ? resetText(limit.resetsAt, now) : ''}`);
|
|
107
|
+
}
|
|
108
|
+
const budget = quota.budget;
|
|
109
|
+
if (budget) {
|
|
110
|
+
times.push(budget.checkedAt);
|
|
111
|
+
const figure = budget.reached
|
|
112
|
+
? 'credits spent'
|
|
113
|
+
: budget.remainingPercent !== undefined
|
|
114
|
+
? `credits ${percentLeft(budget.remainingPercent)}`
|
|
115
|
+
: 'credits available';
|
|
116
|
+
parts.push(`${figure}${full ? resetText(budget.resetsAt, now) : ''}`);
|
|
117
|
+
}
|
|
118
|
+
if (times.length > 0) {
|
|
119
|
+
const age = now - Math.min(...times);
|
|
120
|
+
if (age > (options.staleAfterMs ?? QUOTA_STALE_AFTER_MS))
|
|
121
|
+
parts.push(`checked ${shortSpan(age)} ago`);
|
|
122
|
+
}
|
|
123
|
+
return parts;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A row's quota as one line: `5h 58% left · 7d 90% left` in the compact
|
|
127
|
+
* form, with `, resets 2h` after each window in the full form. With nothing
|
|
128
|
+
* reported it is `NO_QUOTA_READING`, or `NO_LIMITS_REPORTED` when the
|
|
129
|
+
* provider answered without a limit for any window.
|
|
130
|
+
*/
|
|
131
|
+
export function formatQuota(quota, options) {
|
|
132
|
+
const parts = quotaTextParts(quota, options);
|
|
133
|
+
if (parts.length > 0)
|
|
134
|
+
return parts.join(' · ');
|
|
135
|
+
return quota && quota.limits.length > 0
|
|
136
|
+
? NO_LIMITS_REPORTED
|
|
137
|
+
: NO_QUOTA_READING;
|
|
138
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single failure value of every store operation. `committed` is present
|
|
3
|
+
* only when the operation had already written a credential to the state file
|
|
4
|
+
* before it failed (a partial rotation or refresh): it is the credential now
|
|
5
|
+
* on disk, so a caller never has to re-read the files to learn it.
|
|
6
|
+
*/
|
|
7
|
+
export class PoolOperationError extends Error {
|
|
8
|
+
operation;
|
|
9
|
+
rowId;
|
|
10
|
+
phase;
|
|
11
|
+
retryable;
|
|
12
|
+
kind;
|
|
13
|
+
committed;
|
|
14
|
+
constructor(details) {
|
|
15
|
+
super(details.message ??
|
|
16
|
+
`${details.operation} failed (${details.kind}, ${details.phase})`, details.cause === undefined ? undefined : { cause: details.cause });
|
|
17
|
+
this.name = 'PoolOperationError';
|
|
18
|
+
this.operation = details.operation;
|
|
19
|
+
this.rowId = details.rowId;
|
|
20
|
+
this.phase = details.phase;
|
|
21
|
+
this.retryable = details.retryable;
|
|
22
|
+
this.kind = details.kind;
|
|
23
|
+
this.committed = details.committed;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Thrown when a row operation or a refresh is called from inside a hook of a
|
|
28
|
+
* lock-holding operation (or from any continuation created inside one). It is
|
|
29
|
+
* thrown before any lock is taken or waited for.
|
|
30
|
+
*/
|
|
31
|
+
export class PoolReentryError extends Error {
|
|
32
|
+
operation;
|
|
33
|
+
constructor(operation) {
|
|
34
|
+
super(`${operation} was called from inside a store hook; hand it to the caller's continuation instead`);
|
|
35
|
+
this.name = 'PoolReentryError';
|
|
36
|
+
this.operation = operation;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { isQuotaMap } from "./905bffc0220f43d0-map.js";
|
|
2
|
+
import { mergeQuotaObservation } from "./4f36d333d081d040-merge.js";
|
|
3
|
+
/**
|
|
4
|
+
* The quota codec the account-pool store (the `/store` subpath) takes when it
|
|
5
|
+
* is opened: `validate` checks a stored per-row quota map and `merge` applies
|
|
6
|
+
* an observation to one, so the store persists the map without interpreting
|
|
7
|
+
* it.
|
|
8
|
+
*/
|
|
9
|
+
export const quotaCodec = Object.freeze({
|
|
10
|
+
validate: isQuotaMap,
|
|
11
|
+
merge: mergeQuotaObservation,
|
|
12
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { readFileSync, realpathSync, statSync, watch, } from "node:fs";
|
|
2
|
+
import { readFile, stat } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
4
|
+
import { clearTimeout, setTimeout } from "node:timers";
|
|
5
|
+
function canonicalPath(file) {
|
|
6
|
+
const path = resolve(file);
|
|
7
|
+
try {
|
|
8
|
+
return realpathSync(path);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// Resolve existing ancestors even when the preferences file is not yet present.
|
|
12
|
+
const parent = dirname(path);
|
|
13
|
+
return parent === path ? path : join(canonicalPath(parent), basename(path));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function identity(info) {
|
|
17
|
+
return info && `${info.dev}:${info.ino}:${info.mtimeMs}:${info.size}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Share one directory watcher and metadata poll per canonical path, including
|
|
21
|
+
* across bundled copies. Native events use a 150 ms debounce; missed events
|
|
22
|
+
* recover through a 1,000 ms stat probe (formerly a 100 ms full-file poll).
|
|
23
|
+
* Watching the directory preserves atomic replacement and missing-file recovery.
|
|
24
|
+
* Only changed raw text notifies; each subscriber is isolated and independently
|
|
25
|
+
* disposable. The last unsubscribe stops both the native watcher and the poll.
|
|
26
|
+
*/
|
|
27
|
+
export function watchTuiPreferences(file, onChange, options = {}) {
|
|
28
|
+
const globals = globalThis;
|
|
29
|
+
const key = Symbol.for('cortexkit.common-auth.tui-prefs.watchers.v1');
|
|
30
|
+
const registry = globals[key] ?? new Map();
|
|
31
|
+
globals[key] = registry;
|
|
32
|
+
const path = canonicalPath(file);
|
|
33
|
+
const shared = registry.get(path);
|
|
34
|
+
if (shared)
|
|
35
|
+
return shared.subscribe(onChange);
|
|
36
|
+
const name = basename(path);
|
|
37
|
+
const read = options.fs?.readFile ?? ((file) => readFile(file, 'utf8'));
|
|
38
|
+
const probe = options.fs?.stat ?? stat;
|
|
39
|
+
let timer = null;
|
|
40
|
+
let pollTimer = null;
|
|
41
|
+
let lastMetadata = null;
|
|
42
|
+
let lastSeen = null;
|
|
43
|
+
// Seed synchronously so an immediate write cannot be absorbed as the baseline.
|
|
44
|
+
// The metadata is recorded only together with contents that were read, as in
|
|
45
|
+
// the probe below: if the first read fails, the probe keeps trying.
|
|
46
|
+
try {
|
|
47
|
+
const metadata = identity((options.fs?.statSync ?? statSync)(path));
|
|
48
|
+
lastSeen = (options.fs?.readFileSync ?? ((file) => readFileSync(file, 'utf8')))(path);
|
|
49
|
+
lastMetadata = metadata;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Missing or unreadable files are picked up by the probe later.
|
|
53
|
+
}
|
|
54
|
+
let disposed = false;
|
|
55
|
+
const subscribers = new Set();
|
|
56
|
+
let checking = Promise.resolve();
|
|
57
|
+
const checkForChange = (native) => {
|
|
58
|
+
// Serialize native and poll checks so an older read cannot roll back state.
|
|
59
|
+
checking = checking.then(async () => {
|
|
60
|
+
if (disposed)
|
|
61
|
+
return;
|
|
62
|
+
const metadata = identity(await probe(path).catch(() => null));
|
|
63
|
+
if (!native && metadata === lastMetadata)
|
|
64
|
+
return;
|
|
65
|
+
// A missing file has nothing to read; remember that so the probe stays
|
|
66
|
+
// quiet until it reappears.
|
|
67
|
+
if (metadata === null) {
|
|
68
|
+
lastMetadata = null;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const text = await read(path).catch(() => null);
|
|
72
|
+
if (disposed)
|
|
73
|
+
return;
|
|
74
|
+
// Record the metadata only once its contents were read. A failed read
|
|
75
|
+
// leaves the old value, so the next probe sees a change and retries
|
|
76
|
+
// instead of skipping a file whose new contents were never observed.
|
|
77
|
+
if (text === null)
|
|
78
|
+
return;
|
|
79
|
+
lastMetadata = metadata;
|
|
80
|
+
if (text === lastSeen)
|
|
81
|
+
return;
|
|
82
|
+
lastSeen = text;
|
|
83
|
+
for (const subscriber of [...subscribers]) {
|
|
84
|
+
if (!subscribers.has(subscriber))
|
|
85
|
+
continue;
|
|
86
|
+
try {
|
|
87
|
+
subscriber.notify();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// A broken subscriber must not prevent the others from observing a change.
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return checking;
|
|
95
|
+
};
|
|
96
|
+
const scheduleCheck = () => {
|
|
97
|
+
if (disposed)
|
|
98
|
+
return;
|
|
99
|
+
if (timer)
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
timer = setTimeout(() => {
|
|
102
|
+
timer = null;
|
|
103
|
+
void checkForChange(true);
|
|
104
|
+
}, 150);
|
|
105
|
+
};
|
|
106
|
+
let watcher = null;
|
|
107
|
+
try {
|
|
108
|
+
watcher = (options.watchDirectory ?? watch)(dirname(path), (_event, filename) => {
|
|
109
|
+
const eventName = filename?.toString();
|
|
110
|
+
const isOurs = eventName === name ||
|
|
111
|
+
(eventName?.startsWith(`${name}.`) && eventName.endsWith('.tmp'));
|
|
112
|
+
if (eventName != null && !isOurs)
|
|
113
|
+
return;
|
|
114
|
+
scheduleCheck();
|
|
115
|
+
});
|
|
116
|
+
watcher.on('error', () => {
|
|
117
|
+
// Polling continues if the native watcher fails after construction.
|
|
118
|
+
watcher?.close();
|
|
119
|
+
watcher = null;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// The probe still recovers a later creation, even with no native watcher.
|
|
124
|
+
}
|
|
125
|
+
const schedulePoll = () => {
|
|
126
|
+
if (disposed || pollTimer)
|
|
127
|
+
return;
|
|
128
|
+
pollTimer = setTimeout(() => {
|
|
129
|
+
pollTimer = null;
|
|
130
|
+
if (disposed)
|
|
131
|
+
return;
|
|
132
|
+
void checkForChange(false).finally(schedulePoll);
|
|
133
|
+
}, 1_000);
|
|
134
|
+
pollTimer.unref();
|
|
135
|
+
};
|
|
136
|
+
const entry = {
|
|
137
|
+
subscribe: (notify) => {
|
|
138
|
+
const subscriber = { notify };
|
|
139
|
+
subscribers.add(subscriber);
|
|
140
|
+
return () => {
|
|
141
|
+
if (!subscribers.delete(subscriber) || subscribers.size > 0)
|
|
142
|
+
return;
|
|
143
|
+
disposed = true;
|
|
144
|
+
if (timer)
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
if (pollTimer)
|
|
147
|
+
clearTimeout(pollTimer);
|
|
148
|
+
watcher?.close();
|
|
149
|
+
if (registry.get(path) === entry)
|
|
150
|
+
registry.delete(path);
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
registry.set(path, entry);
|
|
155
|
+
schedulePoll();
|
|
156
|
+
return entry.subscribe(onChange);
|
|
157
|
+
}
|