@cortexkit/opencode-openai-auth 0.1.4 → 0.2.1
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 +6 -6
- package/dist/cli.js +10 -10
- package/dist/config.d.ts +3 -1
- package/dist/core/accounts.d.ts +17 -1
- package/dist/core/cachekeep.d.ts +3 -0
- package/dist/core/oauth.d.ts +10 -2
- package/dist/core/quota-manager.d.ts +21 -1
- package/dist/index-1gjhrsep.js +2 -0
- package/dist/index-5j7p33r8.js +2 -0
- package/dist/index-88b7ha2h.js +3 -0
- package/dist/{index-dz7tcrxb.js → index-nngfcx1x.js} +1 -1
- package/dist/{index-eks9xm6s.js → index-qqrj9za2.js} +1 -1
- package/dist/{index-6qjg8qfw.js → index-r68prrfg.js} +1 -1
- package/dist/{index-hrg6bdgb.js → index-vj00ztg7.js} +1 -1
- package/dist/index-xftpnva2.js +92 -0
- package/dist/index.js +64 -70
- package/dist/quota-normalize-s1bctr9a.js +1 -0
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/rpc/rpc-server.d.ts +1 -0
- package/dist/sidebar-state.js +1 -1
- package/dist/{token-zzzp7w2y.js → token-c1q4pea2.js} +1 -1
- package/dist/token-util-ff59e4fw.js +1 -0
- package/dist/tui-preferences.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/ws-pool.d.ts +5 -1
- package/dist/ws.d.ts +9 -1
- package/package.json +3 -1
- package/src/logger.ts +140 -0
- package/src/rpc/rpc-server.ts +7 -0
- package/src/sidebar-state.ts +6 -2
- package/src/tui/command-dialogs.tsx +3 -61
- package/src/util/error.ts +9 -0
- package/src/util/open-url.ts +30 -0
- package/src/util/proxy-env.ts +42 -0
- package/src/util/record.ts +3 -0
- package/src/util/stable-json.ts +15 -0
- package/src/util/uuid-v7.ts +18 -0
- package/dist/index-0s5tftne.js +0 -3
- package/dist/index-jshhys8t.js +0 -2
- package/dist/index-m9dtkj3q.js +0 -92
- package/dist/index-sgfft5d3.js +0 -2
- package/dist/quota-normalize-qh5n0p7v.js +0 -1
- package/dist/token-util-sqwtzxz0.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as a,d as b,e as c,f as d}from"./index-5j7p33r8.js";import"./index-vj00ztg7.js";export{a as toResetIso,c as normalizeWsFrame,d as normalizeWham,b as normalizeQuotaHeaders};
|
package/dist/rpc/port-file.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{aa as a,ba as b}from"../index-nngfcx1x.js";import"../index-vj00ztg7.js";export{a as writePortFile,b as discoverPortFile};
|
package/dist/rpc/rpc-client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ba as c}from"../index-nngfcx1x.js";import"../index-vj00ztg7.js";async function p(n,r,l,o,i){let t=await c(n,r);if(l?.(t),!t)return null;let e=new AbortController,s=setTimeout(()=>e.abort(),2000);try{let a=await fetch(`http://127.0.0.1:${t.port}/rpc/${o}`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.token}`},body:JSON.stringify(i),signal:e.signal});if(!a.ok)return null;return await a.json()}catch{return null}finally{clearTimeout(s)}}function y(n,r,l){let o=!1,i=(t)=>{if(o)return;o=!0,l?.(t)};return{async pending(t,e){return(await p(n,r,i,"pending-notifications",{lastReceivedId:t,sessionId:e}))?.messages??[]},async apply(t){return await p(n,r,i,"apply",{...t})??{text:"apply failed",knobs:{}}}}}export{y as createRpcClient};
|
package/dist/rpc/rpc-dir.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ca as a,da as b}from"../index-qqrj9za2.js";import"../index-vj00ztg7.js";export{a as tmpdir,b as getRpcDir};
|
package/dist/rpc/rpc-server.d.ts
CHANGED
|
@@ -9,5 +9,6 @@ export interface RpcServerOptions {
|
|
|
9
9
|
dir: string;
|
|
10
10
|
drain: typeof drainNotifications;
|
|
11
11
|
apply: (request: ApplyRequest) => Promise<ApplyResult>;
|
|
12
|
+
timeoutMs?: number;
|
|
12
13
|
}
|
|
13
14
|
export declare function startRpcServer(options: RpcServerOptions): Promise<RpcServerHandle>;
|
package/dist/sidebar-state.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{P as a,Q as b,R as c,S as d,T as e,U as f,V as g,W as h,X as i,Y as j,Z as k}from"./index-1gjhrsep.js";import"./index-88b7ha2h.js";import"./index-vj00ztg7.js";export{e as setSidebarState,g as resolveActiveAccount,c as normalizeSidebarState,a as getSidebarStateFile,d as getSidebarState,h as getCollapsedQuotaSummary,f as drainSidebarWrites,k as computeQuotaPacing,j as SEVEN_DAY_MS,i as FIVE_HOUR_MS,b as DEFAULT_SIDEBAR_STATE};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as U,b as V}from"./index-
|
|
1
|
+
import{a as U,b as V}from"./index-r68prrfg.js";import{fa as R}from"./index-vj00ztg7.js";var E=R((M,Q)=>{var{defineProperty:K,getOwnPropertyDescriptor:W,getOwnPropertyNames:X}=Object,Y=Object.prototype.hasOwnProperty,Z=(q,v)=>{for(var z in v)K(q,z,{get:v[z],enumerable:!0})},$=(q,v,z,F)=>{if(v&&typeof v==="object"||typeof v==="function"){for(let G of X(v))if(!Y.call(q,G)&&G!==z)K(q,G,{get:()=>v[G],enumerable:!(F=W(v,G))||F.enumerable})}return q},A=(q)=>$(K({},"__esModule",{value:!0}),q),L={};Z(L,{refreshToken:()=>D});Q.exports=A(L);var H=U(),B=V();async function D(){let{projectId:q,teamId:v}=(0,B.findProjectInfo)(),z=(0,B.loadToken)(q);if(!z||(0,B.isExpired)((0,B.getTokenPayload)(z.token))){let F=await(0,B.getVercelCliToken)();if(!F)throw new H.VercelOidcTokenError("Failed to refresh OIDC token: Log in to Vercel CLI and link your project with `vc link`");if(!q)throw new H.VercelOidcTokenError("Failed to refresh OIDC token: Try re-linking your project with `vc link`");if(z=await(0,B.getVercelOidcToken)(F,q,v),!z)throw new H.VercelOidcTokenError("Failed to refresh OIDC token");(0,B.saveToken)(z,q)}process.env.VERCEL_OIDC_TOKEN=z.token;return}});export default E();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as a}from"./index-r68prrfg.js";import"./index-vj00ztg7.js";export default a();
|
package/dist/tui-preferences.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ea as P,fa as C,ha as _}from"./index-vj00ztg7.js";var g=C((T,L)=>{(function(Q){if(typeof L==="object"&&typeof L.exports==="object"){var J=Q(_,T);if(J!==void 0)L.exports=J}else if(typeof define==="function"&&define.amd)define(["require","exports","./impl/format","./impl/edit","./impl/scanner","./impl/parser"],Q)})(function(Q,J){Object.defineProperty(J,"__esModule",{value:!0}),J.applyEdits=J.modify=J.format=J.printParseErrorCode=J.ParseErrorCode=J.stripComments=J.visit=J.getNodeValue=J.getNodePath=J.findNodeAtOffset=J.findNodeAtLocation=J.parseTree=J.parse=J.getLocation=J.SyntaxKind=J.ScanError=J.createScanner=void 0;let X=Q("./impl/format"),Z=Q("./impl/edit"),G=Q("./impl/scanner"),$=Q("./impl/parser");J.createScanner=G.createScanner;var W;(function(z){z[z.None=0]="None",z[z.UnexpectedEndOfComment=1]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=2]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=3]="UnexpectedEndOfNumber",z[z.InvalidUnicode=4]="InvalidUnicode",z[z.InvalidEscapeCharacter=5]="InvalidEscapeCharacter",z[z.InvalidCharacter=6]="InvalidCharacter"})(W||(J.ScanError=W={}));var H;(function(z){z[z.OpenBraceToken=1]="OpenBraceToken",z[z.CloseBraceToken=2]="CloseBraceToken",z[z.OpenBracketToken=3]="OpenBracketToken",z[z.CloseBracketToken=4]="CloseBracketToken",z[z.CommaToken=5]="CommaToken",z[z.ColonToken=6]="ColonToken",z[z.NullKeyword=7]="NullKeyword",z[z.TrueKeyword=8]="TrueKeyword",z[z.FalseKeyword=9]="FalseKeyword",z[z.StringLiteral=10]="StringLiteral",z[z.NumericLiteral=11]="NumericLiteral",z[z.LineCommentTrivia=12]="LineCommentTrivia",z[z.BlockCommentTrivia=13]="BlockCommentTrivia",z[z.LineBreakTrivia=14]="LineBreakTrivia",z[z.Trivia=15]="Trivia",z[z.Unknown=16]="Unknown",z[z.EOF=17]="EOF"})(H||(J.SyntaxKind=H={})),J.getLocation=$.getLocation,J.parse=$.parse,J.parseTree=$.parseTree,J.findNodeAtLocation=$.findNodeAtLocation,J.findNodeAtOffset=$.findNodeAtOffset,J.getNodePath=$.getNodePath,J.getNodeValue=$.getNodeValue,J.visit=$.visit,J.stripComments=$.stripComments;var q;(function(z){z[z.InvalidSymbol=1]="InvalidSymbol",z[z.InvalidNumberFormat=2]="InvalidNumberFormat",z[z.PropertyNameExpected=3]="PropertyNameExpected",z[z.ValueExpected=4]="ValueExpected",z[z.ColonExpected=5]="ColonExpected",z[z.CommaExpected=6]="CommaExpected",z[z.CloseBraceExpected=7]="CloseBraceExpected",z[z.CloseBracketExpected=8]="CloseBracketExpected",z[z.EndOfFileExpected=9]="EndOfFileExpected",z[z.InvalidCommentToken=10]="InvalidCommentToken",z[z.UnexpectedEndOfComment=11]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=12]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=13]="UnexpectedEndOfNumber",z[z.InvalidUnicode=14]="InvalidUnicode",z[z.InvalidEscapeCharacter=15]="InvalidEscapeCharacter",z[z.InvalidCharacter=16]="InvalidCharacter"})(q||(J.ParseErrorCode=q={}));function D(z){switch(z){case 1:return"InvalidSymbol";case 2:return"InvalidNumberFormat";case 3:return"PropertyNameExpected";case 4:return"ValueExpected";case 5:return"ColonExpected";case 6:return"CommaExpected";case 7:return"CloseBraceExpected";case 8:return"CloseBracketExpected";case 9:return"EndOfFileExpected";case 10:return"InvalidCommentToken";case 11:return"UnexpectedEndOfComment";case 12:return"UnexpectedEndOfString";case 13:return"UnexpectedEndOfNumber";case 14:return"InvalidUnicode";case 15:return"InvalidEscapeCharacter";case 16:return"InvalidCharacter"}return"<unknown ParseErrorCode>"}J.printParseErrorCode=D;function m(z,U,j){return X.format(z,U,j)}J.format=m;function f(z,U,j,A){return Z.setProperty(z,U,j,A)}J.modify=f;function u(z,U){let j=U.slice(0).sort((I,Y)=>{let F=I.offset-Y.offset;if(F===0)return I.length-Y.length;return F}),A=z.length;for(let I=j.length-1;I>=0;I--){let Y=j[I];if(Y.offset+Y.length<=A)z=Z.applyEdit(z,Y);else throw Error("Overlapping edit");A=Y.offset}return z}J.applyEdits=u})});var M=P(g(),1);import{watch as K}from"node:fs";import{mkdir as c,readFile as O,rename as y,writeFile as S}from"node:fs/promises";import{homedir as E}from"node:os";import{basename as l,dirname as v,join as k}from"node:path";var p="OPENCODE_TUI_PREFERENCES_FILE",x="tui-preferences.jsonc";function w(){let Q=process.env[p];if(Q)return Q;let J=process.env.OPENCODE_CONFIG_DIR||k(process.env.XDG_CONFIG_HOME||k(E(),".config"),"opencode");return k(J,x)}function N(Q){return typeof Q==="object"&&Q!==null&&!Array.isArray(Q)}async function Xz(){try{let Q=await O(w(),"utf8"),J=[],X=M.parse(Q,J,{allowTrailingComma:!0});if(J.length>0)return{};return N(X)?X:{}}catch{return{}}}var i="openai-auth",s=160,b={forceToTop:!1,order:s,startCollapsed:!1,rememberCollapsed:!0,collapsed:null,pollMs:1500,refreshDebounceMs:200,header:{label:"OPENAI",showVersion:!0},sections:{quota:!0,fallbackAccounts:!0,routing:!0,health:!0,pacing:!0},appearance:{barWidth:10,barFilledChar:"█",barEmptyChar:"░",warnThreshold:50,errorThreshold:80}};function V(Q,J){return typeof Q==="boolean"?Q:J}function B(Q,J,X,Z){if(typeof Q!=="number"||!Number.isFinite(Q))return J;return Math.min(Math.max(Math.round(Q),X),Z)}function d(Q,J,X){if(typeof Q!=="string"||Q.length===0)return J;return Q.slice(0,X)}function h(Q,J){if(typeof Q!=="string")return J;return[...Q][0]??J}function Zz(Q){let J=Q[i];if(!N(J))return structuredClone(b);let X=b,Z=N(J.header)?J.header:{},G=N(J.sections)?J.sections:{},$=N(J.appearance)?J.appearance:{},W=B($.warnThreshold,X.appearance.warnThreshold,0,99),H=Math.max(B($.errorThreshold,X.appearance.errorThreshold,0,100),Math.min(W+1,100));return{forceToTop:V(J.forceToTop,X.forceToTop),order:B(J.order,X.order,-1e4,1e4),startCollapsed:V(J.startCollapsed,X.startCollapsed),rememberCollapsed:V(J.rememberCollapsed,X.rememberCollapsed),collapsed:typeof J.collapsed==="boolean"?J.collapsed:null,pollMs:B(J.pollMs,X.pollMs,500,30000),refreshDebounceMs:B(J.refreshDebounceMs,X.refreshDebounceMs,50,5000),header:{label:d(Z.label,X.header.label,20),showVersion:V(Z.showVersion,X.header.showVersion)},sections:{quota:V(G.quota,X.sections.quota),fallbackAccounts:V(G.fallbackAccounts,X.sections.fallbackAccounts),routing:V(G.routing,X.sections.routing),health:V(G.health,X.sections.health),pacing:V(G.pacing,X.sections.pacing)},appearance:{barWidth:B($.barWidth,X.appearance.barWidth,4,40),barFilledChar:h($.barFilledChar,X.appearance.barFilledChar),barEmptyChar:h($.barEmptyChar,X.appearance.barEmptyChar),warnThreshold:W,errorThreshold:H}}}var n=-1e5;function $z(Q,J,X){let Z=Q[J];if(!N(Z))return X;if(Z.forceToTop===!0)return n+Object.keys(Q).indexOf(J);return B(Z.order,X,-1e4,1e4)}var t=`// Shared preferences for opencode TUI plugins.
|
|
2
2
|
// One top-level key per plugin (short name). See each plugin's README for
|
|
3
3
|
// its supported settings. This file is safe to hand-edit; plugins update
|
|
4
4
|
// individual keys in place and preserve comments.
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PackageVersion = "0.1
|
|
1
|
+
export declare const PackageVersion = "0.2.1";
|
package/dist/ws-pool.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const TITLE_HEADER = "x-opencode-title";
|
|
2
|
+
export declare const QUOTA_ACCOUNT_HEADER = "x-openai-auth-quota-account";
|
|
2
3
|
export interface CreateWebSocketFetchOptions {
|
|
3
4
|
httpFetch?: typeof globalThis.fetch;
|
|
4
5
|
url?: string;
|
|
@@ -29,17 +30,20 @@ interface PoolEntry {
|
|
|
29
30
|
continuation?: ContinuationState;
|
|
30
31
|
turnID?: string;
|
|
31
32
|
turnStartedAt?: number;
|
|
33
|
+
turnInput?: unknown[];
|
|
34
|
+
turnSignature?: string;
|
|
32
35
|
}
|
|
33
36
|
interface ContinuationState {
|
|
34
37
|
responseID: string;
|
|
35
38
|
input: unknown[];
|
|
36
39
|
signature: string;
|
|
40
|
+
finalizedCallIds: Set<string>;
|
|
37
41
|
}
|
|
38
42
|
export declare function createWebSocketFetch(options?: CreateWebSocketFetchOptions): ((input: RequestInfo | URL, init?: RequestInit) => Promise<Response>) & {
|
|
39
43
|
close: () => void;
|
|
40
44
|
remove: (sessionID: string) => void;
|
|
41
45
|
};
|
|
42
|
-
export declare function applyTurnId(entry: PoolEntry, body: Record<string, unknown>): Record<string, unknown>;
|
|
46
|
+
export declare function applyTurnId(entry: PoolEntry, body: Record<string, unknown>, fullBody?: Record<string, unknown>): Record<string, unknown>;
|
|
43
47
|
export declare function withoutInternalHeaders<T extends {
|
|
44
48
|
headers?: HeadersInit;
|
|
45
49
|
}>(init: T | undefined): T | undefined;
|
package/dist/ws.d.ts
CHANGED
|
@@ -15,7 +15,15 @@ export interface StreamResponsesWebSocketOptions {
|
|
|
15
15
|
idleTimeout?: number;
|
|
16
16
|
signal?: AbortSignal;
|
|
17
17
|
onFirstEvent?: (error?: WrappedError) => void;
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Fires on response.completed/response.done. `finalizedFunctionCallIds` is the
|
|
20
|
+
* set of function/custom tool call ids the response actually finalized (emitted
|
|
21
|
+
* a response.output_item.done for). The pool uses it to decide which suffix
|
|
22
|
+
* function_call items are safe to trim from a continuation: only those present
|
|
23
|
+
* in the chained response may be dropped — an unfinalized call (e.g. an aborted
|
|
24
|
+
* partial) must be kept inline or its function_call_output orphans → 400.
|
|
25
|
+
*/
|
|
26
|
+
onComplete?: (event: Record<string, unknown>, finalizedFunctionCallIds: Set<string>) => void;
|
|
19
27
|
onTerminal?: (event: Record<string, unknown>) => void;
|
|
20
28
|
onRetryableTerminal?: (event: Record<string, unknown>) => Promise<WebSocket | undefined>;
|
|
21
29
|
onConnectionInvalid?: (error: ResponseStreamError) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-openai-auth",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
"src/tui/command-dialogs.tsx",
|
|
41
41
|
"src/sidebar-state.ts",
|
|
42
42
|
"src/tui-preferences.ts",
|
|
43
|
+
"src/logger.ts",
|
|
44
|
+
"src/util",
|
|
43
45
|
"src/rpc",
|
|
44
46
|
"README.md",
|
|
45
47
|
"LICENSE"
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, renameSync, statSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
|
|
5
|
+
type Level = 'error' | 'warn' | 'info' | 'debug' | 'trace'
|
|
6
|
+
const ORDER: Record<Level, number> = {
|
|
7
|
+
error: 0,
|
|
8
|
+
warn: 1,
|
|
9
|
+
info: 2,
|
|
10
|
+
debug: 3,
|
|
11
|
+
trace: 4,
|
|
12
|
+
}
|
|
13
|
+
const isTestEnv = process.env.NODE_ENV === 'test'
|
|
14
|
+
const MAX_BYTES = 5 * 1024 * 1024
|
|
15
|
+
|
|
16
|
+
function logFile(): string {
|
|
17
|
+
return (
|
|
18
|
+
process.env.OPENCODE_OPENAI_AUTH_LOG_FILE ??
|
|
19
|
+
join(tmpdir(), 'opencode-openai-auth.log')
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
let runtimeLevel: Level | undefined
|
|
23
|
+
export function setLogLevel(l: Level | undefined) {
|
|
24
|
+
if (l === undefined || l in ORDER) runtimeLevel = l
|
|
25
|
+
}
|
|
26
|
+
function configuredLevel(): Level {
|
|
27
|
+
if (runtimeLevel) return runtimeLevel
|
|
28
|
+
const env = process.env.OPENCODE_OPENAI_AUTH_LOG_LEVEL as Level | undefined
|
|
29
|
+
if (env && env in ORDER) return env
|
|
30
|
+
return 'info'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const SECRET_KEY_EXACT =
|
|
34
|
+
/^(authorization|x-api-key|cookie|set-cookie|refresh|access|token)$/i
|
|
35
|
+
function isSecretKey(key: string): boolean {
|
|
36
|
+
if (SECRET_KEY_EXACT.test(key)) return true
|
|
37
|
+
const k = key.toLowerCase().replace(/[-_]/g, '')
|
|
38
|
+
// Only the unambiguous ChatGPT stable id (chatgpt-account-id / chatgptAccountId)
|
|
39
|
+
// is sensitive. A bare `accountId` field is overloaded — most diagnostic logs put
|
|
40
|
+
// the INTERNAL account id/key ('main' or a fallback id) there, which is safe and
|
|
41
|
+
// needed for debugging — so it is intentionally NOT redacted by name. The genuine
|
|
42
|
+
// ChatGPT id is kept out of logs at the source instead.
|
|
43
|
+
if (k === 'chatgptaccountid') return true
|
|
44
|
+
if (k.includes('apikey')) return true
|
|
45
|
+
if (k.endsWith('secret') || k.endsWith('password')) return true
|
|
46
|
+
if (k.endsWith('token') && !k.endsWith('tokens')) return true
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
const TOKEN_VALUE = /\b(Bearer\s+[\w.-]+|sk-[\w-]+|eyJ[\w.-]+)\b/g
|
|
50
|
+
function redact(value: unknown): unknown {
|
|
51
|
+
return redactInner(value, new WeakSet<object>())
|
|
52
|
+
}
|
|
53
|
+
function redactInner(value: unknown, seen: WeakSet<object>): unknown {
|
|
54
|
+
if (typeof value === 'string')
|
|
55
|
+
return value.replace(TOKEN_VALUE, '***REDACTED***')
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
if (seen.has(value)) return '[Circular]'
|
|
58
|
+
seen.add(value)
|
|
59
|
+
const arr = value.map((v) => redactInner(v, seen))
|
|
60
|
+
seen.delete(value)
|
|
61
|
+
return arr
|
|
62
|
+
}
|
|
63
|
+
if (value && typeof value === 'object') {
|
|
64
|
+
if (seen.has(value)) return '[Circular]'
|
|
65
|
+
seen.add(value)
|
|
66
|
+
const out: Record<string, unknown> = {}
|
|
67
|
+
for (const [k, v] of Object.entries(value))
|
|
68
|
+
out[k] = isSecretKey(k) ? '***REDACTED***' : redactInner(v, seen)
|
|
69
|
+
seen.delete(value)
|
|
70
|
+
return out
|
|
71
|
+
}
|
|
72
|
+
return value
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let buffer: string[] = []
|
|
76
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
77
|
+
const ROTATE_KEEP = 3
|
|
78
|
+
function rotateIfNeeded() {
|
|
79
|
+
try {
|
|
80
|
+
const f = logFile()
|
|
81
|
+
if (!(existsSync(f) && statSync(f).size > MAX_BYTES)) return
|
|
82
|
+
for (let i = ROTATE_KEEP - 1; i >= 1; i--) {
|
|
83
|
+
if (existsSync(`${f}.${i}`)) renameSync(`${f}.${i}`, `${f}.${i + 1}`)
|
|
84
|
+
}
|
|
85
|
+
renameSync(f, `${f}.1`)
|
|
86
|
+
} catch {
|
|
87
|
+
/* never throw */
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function flush() {
|
|
91
|
+
if (!buffer.length) return
|
|
92
|
+
const text = buffer.join('')
|
|
93
|
+
buffer = []
|
|
94
|
+
try {
|
|
95
|
+
rotateIfNeeded()
|
|
96
|
+
appendFileSync(logFile(), text)
|
|
97
|
+
} catch {
|
|
98
|
+
/* never throw */
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function schedule() {
|
|
102
|
+
if (!timer)
|
|
103
|
+
timer = setTimeout(() => {
|
|
104
|
+
timer = undefined
|
|
105
|
+
flush()
|
|
106
|
+
}, 500)
|
|
107
|
+
}
|
|
108
|
+
if (!isTestEnv) process.on('exit', flush)
|
|
109
|
+
|
|
110
|
+
function safeSerialize(data: unknown): string {
|
|
111
|
+
try {
|
|
112
|
+
return ` ${JSON.stringify(redact(data))}`
|
|
113
|
+
} catch {
|
|
114
|
+
return ' [unserializable]'
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function emit(channel: string, level: Level, message: string, data?: unknown) {
|
|
119
|
+
if (ORDER[level] > ORDER[configuredLevel()]) return
|
|
120
|
+
const line =
|
|
121
|
+
`[${new Date().toISOString()}] ${level.toUpperCase()} [${channel}] ${message}` +
|
|
122
|
+
(data === undefined ? '' : safeSerialize(data)) +
|
|
123
|
+
'\n'
|
|
124
|
+
buffer.push(line)
|
|
125
|
+
if (buffer.length >= 50) flush()
|
|
126
|
+
else schedule()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function createLogger(channel: string) {
|
|
130
|
+
return {
|
|
131
|
+
error: (m: string, d?: unknown) => emit(channel, 'error', m, d),
|
|
132
|
+
warn: (m: string, d?: unknown) => emit(channel, 'warn', m, d),
|
|
133
|
+
info: (m: string, d?: unknown) => emit(channel, 'info', m, d),
|
|
134
|
+
debug: (m: string, d?: unknown) => emit(channel, 'debug', m, d),
|
|
135
|
+
trace: (m: string, d?: unknown) => emit(channel, 'trace', m, d),
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export async function flushForTest() {
|
|
139
|
+
flush()
|
|
140
|
+
}
|
package/src/rpc/rpc-server.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface RpcServerOptions {
|
|
|
23
23
|
dir: string
|
|
24
24
|
drain: typeof drainNotifications
|
|
25
25
|
apply: (request: ApplyRequest) => Promise<ApplyResult>
|
|
26
|
+
timeoutMs?: number
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
function readBody(req: IncomingMessage): Promise<string> {
|
|
@@ -54,9 +55,15 @@ export async function startRpcServer(
|
|
|
54
55
|
options: RpcServerOptions,
|
|
55
56
|
): Promise<RpcServerHandle> {
|
|
56
57
|
const token = randomBytes(32).toString('hex')
|
|
58
|
+
const timeoutMs = options.timeoutMs ?? 2000
|
|
57
59
|
const server = createServer((req, res) => {
|
|
60
|
+
req.setTimeout(timeoutMs, () => {
|
|
61
|
+
req.socket.destroy()
|
|
62
|
+
})
|
|
58
63
|
void dispatch(req, res)
|
|
59
64
|
})
|
|
65
|
+
server.requestTimeout = timeoutMs
|
|
66
|
+
server.headersTimeout = timeoutMs
|
|
60
67
|
|
|
61
68
|
async function dispatch(req: IncomingMessage, res: ServerResponse) {
|
|
62
69
|
const json = (status: number, value: unknown) => {
|
package/src/sidebar-state.ts
CHANGED
|
@@ -34,7 +34,8 @@ export interface SidebarState {
|
|
|
34
34
|
lastUpdated: number
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
import {
|
|
37
|
+
import { randomUUID } from 'node:crypto'
|
|
38
|
+
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'
|
|
38
39
|
import { tmpdir } from 'node:os'
|
|
39
40
|
import { dirname, join } from 'node:path'
|
|
40
41
|
import { createLogger } from './logger'
|
|
@@ -188,10 +189,13 @@ async function doWriteSidebarState(
|
|
|
188
189
|
state: SidebarState,
|
|
189
190
|
file: string,
|
|
190
191
|
): Promise<void> {
|
|
192
|
+
const tempPath = `${file}.${randomUUID()}.tmp`
|
|
191
193
|
try {
|
|
192
194
|
await mkdir(dirname(file), { recursive: true })
|
|
193
|
-
await writeFile(
|
|
195
|
+
await writeFile(tempPath, JSON.stringify(state), 'utf8')
|
|
196
|
+
await rename(tempPath, file)
|
|
194
197
|
} catch (e) {
|
|
198
|
+
await rm(tempPath, { force: true }).catch(() => {})
|
|
195
199
|
logSb.warn('sidebar write failed', {
|
|
196
200
|
pid: process.pid,
|
|
197
201
|
error: e instanceof Error ? e.message : String(e),
|
|
@@ -341,10 +341,9 @@ function openAccountDialog(api: TuiPluginApi, apply: ApplyFn) {
|
|
|
341
341
|
showAddFlow()
|
|
342
342
|
return
|
|
343
343
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
344
|
+
// Main has no per-account actions (routing is mode-driven; main is
|
|
345
|
+
// not removable or reorderable) — the row is informational only.
|
|
346
|
+
if (option.value === 'main') return
|
|
348
347
|
showL2Fallback(option.value)
|
|
349
348
|
}}
|
|
350
349
|
/>
|
|
@@ -352,47 +351,6 @@ function openAccountDialog(api: TuiPluginApi, apply: ApplyFn) {
|
|
|
352
351
|
})
|
|
353
352
|
}
|
|
354
353
|
|
|
355
|
-
function showL2Main() {
|
|
356
|
-
void getSidebarState().then((state) => {
|
|
357
|
-
const DialogSelectInner = api.ui.DialogSelect<string>
|
|
358
|
-
const isActive = state.activeId === 'main' || !state.activeId
|
|
359
|
-
const options: Array<{
|
|
360
|
-
title: string
|
|
361
|
-
value: string
|
|
362
|
-
description: string
|
|
363
|
-
}> = []
|
|
364
|
-
if (!isActive) {
|
|
365
|
-
options.push({
|
|
366
|
-
title: 'Switch to main account',
|
|
367
|
-
value: 'switch',
|
|
368
|
-
description: 'Make main the active account',
|
|
369
|
-
})
|
|
370
|
-
}
|
|
371
|
-
options.push({
|
|
372
|
-
title: 'Back',
|
|
373
|
-
value: 'back',
|
|
374
|
-
description: 'Return to account list',
|
|
375
|
-
})
|
|
376
|
-
api.ui.dialog.setSize('xlarge')
|
|
377
|
-
api.ui.dialog.replace(() => (
|
|
378
|
-
<DialogSelectInner
|
|
379
|
-
title='Main account'
|
|
380
|
-
options={options}
|
|
381
|
-
onSelect={(option) => {
|
|
382
|
-
if (option.value === 'switch') {
|
|
383
|
-
void apply('openai-account', 'switch main').then((r) => {
|
|
384
|
-
api.ui.toast({ message: r.text })
|
|
385
|
-
showL1()
|
|
386
|
-
})
|
|
387
|
-
return
|
|
388
|
-
}
|
|
389
|
-
showL1()
|
|
390
|
-
}}
|
|
391
|
-
/>
|
|
392
|
-
))
|
|
393
|
-
})
|
|
394
|
-
}
|
|
395
|
-
|
|
396
354
|
function showL2Fallback(id: string) {
|
|
397
355
|
void getSidebarState().then((state) => {
|
|
398
356
|
const DialogSelectInner = api.ui.DialogSelect<string>
|
|
@@ -402,21 +360,12 @@ function openAccountDialog(api: TuiPluginApi, apply: ApplyFn) {
|
|
|
402
360
|
showL1()
|
|
403
361
|
return
|
|
404
362
|
}
|
|
405
|
-
const isActive = state.activeId === id
|
|
406
363
|
const options: Array<{
|
|
407
364
|
title: string
|
|
408
365
|
value: string
|
|
409
366
|
description: string
|
|
410
367
|
}> = []
|
|
411
368
|
|
|
412
|
-
if (!isActive) {
|
|
413
|
-
options.push({
|
|
414
|
-
title: 'Switch to this account',
|
|
415
|
-
value: 'switch',
|
|
416
|
-
description: 'Make this the active account',
|
|
417
|
-
})
|
|
418
|
-
}
|
|
419
|
-
|
|
420
369
|
options.push({
|
|
421
370
|
title: 'Remove',
|
|
422
371
|
value: 'remove',
|
|
@@ -457,13 +406,6 @@ function openAccountDialog(api: TuiPluginApi, apply: ApplyFn) {
|
|
|
457
406
|
title={fb.label ?? fb.id}
|
|
458
407
|
options={options}
|
|
459
408
|
onSelect={(option) => {
|
|
460
|
-
if (option.value === 'switch') {
|
|
461
|
-
void apply('openai-account', `switch ${id}`).then((r) => {
|
|
462
|
-
api.ui.toast({ message: r.text })
|
|
463
|
-
showL1()
|
|
464
|
-
})
|
|
465
|
-
return
|
|
466
|
-
}
|
|
467
409
|
if (option.value === 'remove') {
|
|
468
410
|
api.ui.dialog.setSize('xlarge')
|
|
469
411
|
api.ui.dialog.replace(() => (
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { execFileSync as defaultExecFileSync } from 'node:child_process'
|
|
2
|
+
|
|
3
|
+
type BrowserExec = (
|
|
4
|
+
file: string,
|
|
5
|
+
args: string[],
|
|
6
|
+
options: { stdio: 'ignore'; timeout: number },
|
|
7
|
+
) => unknown
|
|
8
|
+
|
|
9
|
+
export function openUrl(
|
|
10
|
+
url: string,
|
|
11
|
+
platform: NodeJS.Platform = process.platform,
|
|
12
|
+
execFileSync: BrowserExec = defaultExecFileSync,
|
|
13
|
+
) {
|
|
14
|
+
try {
|
|
15
|
+
if (platform === 'win32') {
|
|
16
|
+
execFileSync('cmd', ['/c', 'start', '', url], {
|
|
17
|
+
stdio: 'ignore',
|
|
18
|
+
timeout: 3000,
|
|
19
|
+
})
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
execFileSync(platform === 'darwin' ? 'open' : 'xdg-open', [url], {
|
|
24
|
+
stdio: 'ignore',
|
|
25
|
+
timeout: 3000,
|
|
26
|
+
})
|
|
27
|
+
} catch {
|
|
28
|
+
// Browser launch is best effort; the printed URL remains the fallback.
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function proxyEnv(name: string) {
|
|
2
|
+
return process.env[name] ?? process.env[name.toLowerCase()]
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function shouldBypassProxy(url: URL): boolean {
|
|
6
|
+
const noProxy = proxyEnv('NO_PROXY')
|
|
7
|
+
if (!noProxy) return false
|
|
8
|
+
|
|
9
|
+
const host = url.hostname.toLowerCase()
|
|
10
|
+
const entries = noProxy
|
|
11
|
+
.split(',')
|
|
12
|
+
.map((s) => s.trim().toLowerCase())
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
if (entry === '*') return true
|
|
17
|
+
|
|
18
|
+
if (entry.startsWith('.')) {
|
|
19
|
+
if (host === entry.slice(1) || host.endsWith(entry)) {
|
|
20
|
+
return true
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
if (host === entry || host.endsWith(`.${entry}`)) {
|
|
24
|
+
return true
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getProxyForUrl(url: string) {
|
|
33
|
+
const parsed = new URL(url)
|
|
34
|
+
if (shouldBypassProxy(parsed)) return undefined
|
|
35
|
+
|
|
36
|
+
if (parsed.protocol === 'http:') return proxyEnv('HTTP_PROXY')
|
|
37
|
+
if (parsed.protocol === 'https:')
|
|
38
|
+
return proxyEnv('HTTPS_PROXY') ?? proxyEnv('HTTP_PROXY')
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const ProxyEnv = { getProxyForUrl }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isRecord } from './record'
|
|
2
|
+
|
|
3
|
+
export function stableStringify(value: unknown): string {
|
|
4
|
+
return JSON.stringify(stable(value)) ?? 'undefined'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function stable(value: unknown): unknown {
|
|
8
|
+
if (Array.isArray(value)) return value.map(stable)
|
|
9
|
+
if (!isRecord(value)) return value
|
|
10
|
+
return Object.fromEntries(
|
|
11
|
+
Object.keys(value)
|
|
12
|
+
.sort()
|
|
13
|
+
.map((key) => [key, stable(value[key])]),
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Minimal UUIDv7 generator for Codex session/thread/turn identity parity.
|
|
2
|
+
export function uuidV7(): string {
|
|
3
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16))
|
|
4
|
+
const ms = Date.now()
|
|
5
|
+
// 48-bit big-endian millisecond timestamp.
|
|
6
|
+
bytes[0] = (ms / 2 ** 40) & 0xff
|
|
7
|
+
bytes[1] = (ms / 2 ** 32) & 0xff
|
|
8
|
+
bytes[2] = (ms / 2 ** 24) & 0xff
|
|
9
|
+
bytes[3] = (ms / 2 ** 16) & 0xff
|
|
10
|
+
bytes[4] = (ms / 2 ** 8) & 0xff
|
|
11
|
+
bytes[5] = ms & 0xff
|
|
12
|
+
// Version 7 in the high nibble of byte 6.
|
|
13
|
+
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x70
|
|
14
|
+
// RFC 4122 variant (10xx) in the high bits of byte 8.
|
|
15
|
+
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80
|
|
16
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')
|
|
17
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
|
|
18
|
+
}
|
package/dist/index-0s5tftne.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{appendFileSync as k,existsSync as g,renameSync as p,statSync as w}from"node:fs";import{tmpdir as y}from"node:os";import{join as L}from"node:path";var s={error:0,warn:1,info:2,debug:3,trace:4},l=!1,m=5242880;function E(){return process.env.OPENCODE_OPENAI_AUTH_LOG_FILE??L(y(),"opencode-openai-auth.log")}var f;function x(n){if(n===void 0||n in s)f=n}function O(){if(f)return f;let n=process.env.OPENCODE_OPENAI_AUTH_LOG_LEVEL;if(n&&n in s)return n;return"info"}var T=/^(authorization|x-api-key|cookie|set-cookie|refresh|access|token)$/i;function _(n){if(T.test(n))return!0;let e=n.toLowerCase().replace(/[-_]/g,"");if(e.includes("apikey"))return!0;if(e.endsWith("secret")||e.endsWith("password"))return!0;if(e.endsWith("token")&&!e.endsWith("tokens"))return!0;return!1}var b=/\b(Bearer\s+[\w.-]+|sk-[\w-]+|eyJ[\w.-]+)\b/g;function h(n){return d(n,new WeakSet)}function d(n,e){if(typeof n==="string")return n.replace(b,"***REDACTED***");if(Array.isArray(n)){if(e.has(n))return"[Circular]";e.add(n);let t=n.map((r)=>d(r,e));return e.delete(n),t}if(n&&typeof n==="object"){if(e.has(n))return"[Circular]";e.add(n);let t={};for(let[r,u]of Object.entries(n))t[r]=_(r)?"***REDACTED***":d(u,e);return e.delete(n),t}return n}var o=[],c,S=3;function $(){try{let n=E();if(!(g(n)&&w(n).size>m))return;for(let e=S-1;e>=1;e--)if(g(`${n}.${e}`))p(`${n}.${e}`,`${n}.${e+1}`);p(n,`${n}.1`)}catch{}}function a(){if(!o.length)return;let n=o.join("");o=[];try{$(),k(E(),n)}catch{}}function A(){if(!c)c=setTimeout(()=>{c=void 0,a()},500)}if(!l)process.on("exit",a);function C(n){try{return` ${JSON.stringify(h(n))}`}catch{return" [unserializable]"}}function i(n,e,t,r){if(s[e]>s[O()])return;let u=`[${new Date().toISOString()}] ${e.toUpperCase()} [${n}] ${t}`+(r===void 0?"":C(r))+`
|
|
2
|
-
`;if(o.push(u),o.length>=50)a();else A()}function j(n){return{error:(e,t)=>i(n,"error",e,t),warn:(e,t)=>i(n,"warn",e,t),info:(e,t)=>i(n,"info",e,t),debug:(e,t)=>i(n,"debug",e,t),trace:(e,t)=>i(n,"trace",e,t)}}
|
|
3
|
-
export{x as X,j as Y};
|
package/dist/index-jshhys8t.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{Y as v}from"./index-0s5tftne.js";import{mkdir as h,readFile as O,writeFile as N}from"node:fs/promises";import{tmpdir as C}from"node:os";import{dirname as g,join as R}from"node:path";var k=v("sidebar"),L="OPENCODE_OPENAI_AUTH_SIDEBAR_STATE_FILE",D=R(C(),"opencode-openai-auth"),f=R(D,"sidebar-state.json");function W(){return process.env[L]||f}var B={main:{quota:null,killed:!1},fallbacks:[],activeId:void 0,route:"main",lastUpdated:0};function F(K){if(K===null||typeof K!=="object"||Array.isArray(K))return B;let x=K,J=x.main,Q;if(J!==null&&typeof J==="object"&&!Array.isArray(J)){let H=J;Q={quota:"quota"in H?H.quota:null,killed:typeof H.killed==="boolean"?H.killed:!1,...typeof H.quotaBackedOff==="boolean"?{quotaBackedOff:H.quotaBackedOff}:{},...typeof H.quotaBackoffUntil==="number"?{quotaBackoffUntil:H.quotaBackoffUntil}:{},...typeof H.refreshBackedOff==="boolean"?{refreshBackedOff:H.refreshBackedOff}:{},...typeof H.refreshBackoffUntil==="number"?{refreshBackoffUntil:H.refreshBackoffUntil}:{}}}else Q={quota:null,killed:!1};let Y=x.fallbacks,X=Array.isArray(Y)?Y.filter((H)=>H!==null&&typeof H==="object"&&!Array.isArray(H)&&typeof H.id==="string").map((H)=>({id:H.id,label:typeof H.label==="string"?H.label:void 0,quota:"quota"in H?H.quota:null,killed:typeof H.killed==="boolean"?H.killed:!1,enabled:typeof H.enabled==="boolean"?H.enabled:!0})):[],Z=typeof x.activeId==="string"?x.activeId:void 0,V=typeof x.route==="string"?x.route:B.route,$=typeof x.lastUpdated==="number"?x.lastUpdated:0,j=typeof x.planType==="string"?x.planType:void 0,z=typeof x.credits==="number"?x.credits:void 0;return{main:Q,fallbacks:X,activeId:Z,route:V,lastUpdated:$,...j!==void 0?{planType:j}:{},...z!==void 0?{credits:z}:{}}}async function p(){try{let K=await O(W(),"utf8");return F(JSON.parse(K))}catch{return B}}var G=Promise.resolve();function u(K,x=W()){return G=G.then(()=>I(K,x)).catch(()=>{}),G}async function I(K,x){try{await h(g(x),{recursive:!0}),await N(x,JSON.stringify(K),"utf8")}catch(J){k.warn("sidebar write failed",{pid:process.pid,error:J instanceof Error?J.message:String(J)})}}function T(){return G}function E(K){let x=K.activeId;if(x&&x!=="main"){let J=K.fallbacks.find((Q)=>Q.enabled&&Q.id===x);if(J)return{id:J.id,name:J.label??J.id,quota:J.quota,killed:J.killed}}return{id:"main",name:"main",quota:K.main.quota,killed:K.main.killed}}function A(K){let x=K?.primary?.usedPercent??null,J=K?.secondary?.usedPercent??null;if(x==null&&J==null)return{primaryUsedPercent:x,secondaryUsedPercent:J,text:null};return{primaryUsedPercent:x,secondaryUsedPercent:J,text:`5h: ${x==null?"—":`${Math.round(x)}%`} 7d: ${J==null?"—":`${Math.round(J)}%`}`}}var m=18000000,c=604800000,M=300000,U=0.01,b=1;function w(K,x,J){if(!K.resetsAt)return null;let Q=new Date(K.resetsAt).getTime();if(!Number.isFinite(Q))return null;let Y=Q-x,X=J-Y;if(X<M)return null;if(X<x*U)return null;if(X>=x)return null;let Z=K.usedPercent,V=Math.min(Math.max(X/x*100,0),100),$=Z-V,j=Math.abs($)<b?"on-pace":$>0?"deficit":"reserve",z=null;if(Z>0){let H=X*100/Z,q=Y+H;if(q<Q)z=new Date(q).toISOString()}return{pacePercent:V,deltaPercent:$,state:j,runsOutAt:z}}
|
|
2
|
-
export{W as M,B as N,F as O,p as P,u as Q,T as R,E as S,A as T,m as U,c as V,w as W};
|