@simonesiega/codex-limits 0.1.6 → 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.
@@ -0,0 +1,295 @@
1
+ # JSON output
2
+
3
+ [← Documentation hub](../README.md) · [Project README](../../README.md)
4
+
5
+ `codex-limits` provides predictable, machine-readable JSON for scripts and automation.
6
+
7
+ ## Commands
8
+
9
+ ```bash
10
+ # Usage windows, coupons, and combined warnings
11
+ codex-limits --json
12
+
13
+ # Coupon summary only
14
+ codex-limits coupons --json
15
+
16
+ # Safe environment and connectivity diagnostics
17
+ codex-limits doctor --json
18
+ ```
19
+
20
+ `status --json` is not part of the CLI grammar. Use the root `--json` option for usage data.
21
+
22
+ Successful commands write one pretty-printed JSON value, followed by a newline, to standard output and exit with code `0`. Loading or serialization failures write a safe message to standard error, write no partial JSON to standard output, and exit with code `1`.
23
+
24
+ The doctor document reports only versions, a generic operating-system name, booleans, and bounded status values. It never includes credentials, private paths, endpoint URLs, configuration contents, or raw Codex files.
25
+
26
+ Warnings and unavailable live data do not cause a non-zero exit code when the command can still produce a valid JSON document. Consumers should inspect the `warnings` arrays and nullable fields when determining data availability.
27
+
28
+ ## Complete limits document
29
+
30
+ `codex-limits --json` returns this shape:
31
+
32
+ ```ts
33
+ interface CodexLimitsJson {
34
+ windows: {
35
+ fiveHour: UsageWindowJson | null;
36
+ weekly: UsageWindowJson | null;
37
+ };
38
+ coupons: CouponSummaryJson | null;
39
+ warnings: string[];
40
+ }
41
+
42
+ interface UsageWindowJson {
43
+ label: string;
44
+ remainingPercent: number | null;
45
+ usedPercent: number | null;
46
+ resetsAt: string | null;
47
+ resetsIn: string | null;
48
+ }
49
+ ```
50
+
51
+ This example represents one snapshot captured at `2026-07-13T16:00:07.000Z` on a system configured for UTC:
52
+
53
+ ```json
54
+ {
55
+ "windows": {
56
+ "fiveHour": {
57
+ "label": "5-hour usage limit",
58
+ "remainingPercent": 93,
59
+ "usedPercent": 7,
60
+ "resetsAt": "2026-07-13T19:55:07.000Z",
61
+ "resetsIn": "3h 55m"
62
+ },
63
+ "weekly": {
64
+ "label": "Weekly usage limit",
65
+ "remainingPercent": 11,
66
+ "usedPercent": 89,
67
+ "resetsAt": "2026-07-15T17:40:07.000Z",
68
+ "resetsIn": "2d 1h 40m"
69
+ }
70
+ },
71
+ "coupons": {
72
+ "available": 2,
73
+ "earnedThisPeriod": 4,
74
+ "nextExpirationDate": "Monday 20 July 2026",
75
+ "nextExpirationIn": "7d 4h 38m",
76
+ "items": [
77
+ {
78
+ "index": 1,
79
+ "status": "available",
80
+ "grantedAt": "2026-06-20T20:38:07Z",
81
+ "expiresAt": "2026-07-20T20:38:07Z",
82
+ "expirationDate": "Monday 20 July 2026",
83
+ "expiresIn": "7d 4h 38m"
84
+ },
85
+ {
86
+ "index": 2,
87
+ "status": "available",
88
+ "grantedAt": "2026-06-27T20:38:07Z",
89
+ "expiresAt": "2026-07-27T20:38:07Z",
90
+ "expirationDate": "Monday 27 July 2026",
91
+ "expiresIn": "14d 4h 38m"
92
+ }
93
+ ],
94
+ "warnings": []
95
+ },
96
+ "warnings": []
97
+ }
98
+ ```
99
+
100
+ ## Coupon document
101
+
102
+ `codex-limits coupons --json` returns the coupon object directly:
103
+
104
+ ```ts
105
+ interface CouponSummaryJson {
106
+ available: number | null;
107
+ earnedThisPeriod: number | null;
108
+ nextExpirationDate: string | null;
109
+ nextExpirationIn: string | null;
110
+ items: CouponItemJson[];
111
+ warnings: string[];
112
+ }
113
+
114
+ interface CouponItemJson {
115
+ index: number;
116
+ status: string | null;
117
+ grantedAt: string | null;
118
+ expiresAt: string | null;
119
+ expirationDate: string | null;
120
+ expiresIn: string | null;
121
+ }
122
+ ```
123
+
124
+ Using the same reference time and timezone as the complete example:
125
+
126
+ ```json
127
+ {
128
+ "available": 1,
129
+ "earnedThisPeriod": 4,
130
+ "nextExpirationDate": "Monday 20 July 2026",
131
+ "nextExpirationIn": "7d 4h 38m",
132
+ "items": [
133
+ {
134
+ "index": 1,
135
+ "status": "available",
136
+ "grantedAt": "2026-06-20T20:38:07Z",
137
+ "expiresAt": "2026-07-20T20:38:07Z",
138
+ "expirationDate": "Monday 20 July 2026",
139
+ "expiresIn": "7d 4h 38m"
140
+ }
141
+ ],
142
+ "warnings": []
143
+ }
144
+ ```
145
+
146
+ ## Doctor document
147
+
148
+ `codex-limits doctor --json` returns this shape:
149
+
150
+ ```ts
151
+ type AgentIntegrationStatus = "installed" | "not-installed" | "unknown";
152
+
153
+ interface DoctorJson {
154
+ packageVersion: string;
155
+ nodeVersion: string;
156
+ operatingSystem: string;
157
+ codexHomeDetected: boolean;
158
+ authenticationFound: boolean;
159
+ localUsageFound: boolean;
160
+ liveEndpoint: "not-checked" | "reachable" | "unreachable";
161
+ agentIntegrations: Record<string, AgentIntegrationStatus>;
162
+ }
163
+ ```
164
+
165
+ Example:
166
+
167
+ ```json
168
+ {
169
+ "packageVersion": "1.0.0",
170
+ "nodeVersion": "22.0.0",
171
+ "operatingSystem": "Windows",
172
+ "codexHomeDetected": true,
173
+ "authenticationFound": true,
174
+ "localUsageFound": true,
175
+ "liveEndpoint": "reachable",
176
+ "agentIntegrations": {
177
+ "opencode": "installed",
178
+ "pi": "installed",
179
+ "copilot": "installed"
180
+ }
181
+ }
182
+ ```
183
+
184
+ `authenticationFound` means complete credentials were discovered; it never exposes or serializes those values. `localUsageFound` means at least one recognized local usage window was read. `liveEndpoint` is `not-checked` when authentication is unavailable, `reachable` when the endpoint returns an HTTP response, and `unreachable` for invalid endpoint configuration, timeouts, or network failures. `agentIntegrations` maps every registered agent ID to its bounded installation check; a value is `unknown` only when that adapter cannot safely determine its state.
185
+
186
+ ## Field reference
187
+
188
+ ### Usage windows
189
+
190
+ | Field | Meaning |
191
+ | ------------------ | ------------------------------------------------------------------------------ |
192
+ | `label` | Stable human-readable window label. |
193
+ | `remainingPercent` | Remaining capacity from `0` to `100`, or `null` when unknown. |
194
+ | `usedPercent` | Used capacity from `0` to `100`, or `null` when unknown. |
195
+ | `resetsAt` | Reset timestamp normalized to an ISO 8601 UTC string, or `null`. |
196
+ | `resetsIn` | Compact non-negative duration such as `2d 1h 40m`, without seconds, or `null`. |
197
+
198
+ A whole window is `null` when no recognized data exists for it. When a window is partially available, unknown fields remain present with `null` values. Percentages are clamped to `0`–`100` and rounded to at most one decimal place. When no reset timestamp is available, fallback `resetsIn` text is accepted only in compact `d`, `h`, `m`, and `s` form and is normalized without seconds; unrecognized free-form text is discarded.
199
+
200
+ For comparisons and stored data, prefer canonical fields such as `resetsAt` and `expiresAt`. Human-readable fields such as `resetsIn`, `expirationDate`, and `expiresIn` are calculated when the command runs and may depend on the machine's local timezone.
201
+
202
+ ### Coupon summary
203
+
204
+ | Field | Meaning |
205
+ | -------------------- | ------------------------------------------------------------------------------------ |
206
+ | `available` | Available reset-credit count as a non-negative integer, or `null` when not returned. |
207
+ | `earnedThisPeriod` | Total earned reset credits as a non-negative integer, or `null`. |
208
+ | `nextExpirationDate` | Local calendar date for the next available coupon, or otherwise the soonest coupon. |
209
+ | `nextExpirationIn` | Compact non-negative duration until that expiration. |
210
+ | `items` | Valid coupon entries sorted by expiration time. |
211
+ | `warnings` | Safe coupon-specific availability or payload warnings. |
212
+
213
+ Coupon `index` values are one-based and assigned after sorting. `grantedAt` and `expiresAt` preserve bounded RFC 3339 timestamp strings from the service. `expirationDate` is rendered in the machine's local timezone as `Weekday D Month YYYY`; `expiresIn` is calculated at command execution time. Coupon entries with malformed or extra timestamp text are omitted and produce a warning.
214
+
215
+ The complete limits contract permits `coupons: null` when a core caller intentionally omits coupon loading. The standard `codex-limits --json` command requests coupons and normally returns a coupon summary object, including an unavailable summary when credentials or network data are missing.
216
+
217
+ ## Contract stability
218
+
219
+ The documented field names and value types form the public JSON contract. Consumers should tolerate `null` values and additional warning messages.
220
+
221
+ Existing fields are not removed, renamed, or assigned incompatible types without being documented as a breaking change. New additive fields may be introduced in a future schema revision, so consumers should update the schema they use when adopting a newer contract version. Human-readable labels and warning text should not be used as stable identifiers.
222
+
223
+ ## Warnings and unavailable data
224
+
225
+ Unavailable values are represented predictably with `null`, empty arrays, and safe warning strings rather than omitted fields. For example, unavailable coupon data has null summary values and an empty `items` array:
226
+
227
+ ```json
228
+ {
229
+ "available": null,
230
+ "earnedThisPeriod": null,
231
+ "nextExpirationDate": null,
232
+ "nextExpirationIn": null,
233
+ "items": [],
234
+ "warnings": [
235
+ "Live reset coupons require a readable Codex auth.json file or CODEX_LIMITS_ACCESS_TOKEN and CODEX_LIMITS_ACCOUNT_ID."
236
+ ]
237
+ }
238
+ ```
239
+
240
+ The top-level `warnings` array combines usage and coupon warnings. `coupons.warnings` contains coupon warnings specifically, so a coupon warning can also appear in the combined list.
241
+
242
+ ## Deliberately omitted fields
243
+
244
+ The public JSON contract does not expose internal availability statuses or source metadata. In particular, it omits:
245
+
246
+ - internal availability statuses;
247
+ - `usageSource`;
248
+ - coupon `source` labels and endpoint URLs;
249
+ - opaque reset-coupon IDs and reset types used internally for confirmed redemption;
250
+ - access tokens and account IDs;
251
+ - authorization headers, cookies, raw local files, and private paths.
252
+
253
+ Warnings pass through the shared redaction layer before serialization. Raw exceptions are replaced with fixed operation errors.
254
+
255
+ ## Script examples
256
+
257
+ Read the weekly remaining percentage with `jq`:
258
+
259
+ ```bash
260
+ codex-limits --json | jq '.windows.weekly.remainingPercent'
261
+ ```
262
+
263
+ Read the available coupon count:
264
+
265
+ ```bash
266
+ codex-limits coupons --json | jq '.available'
267
+ ```
268
+
269
+ Check whether the live usage endpoint is reachable:
270
+
271
+ ```bash
272
+ codex-limits doctor --json | jq -e '.liveEndpoint == "reachable"'
273
+ ```
274
+
275
+ Fail a shell script when the CLI fails, while keeping standard output machine-readable:
276
+
277
+ ```bash
278
+ if ! limits_json="$(codex-limits --json)"; then
279
+ echo "Could not read Codex limits" >&2
280
+ exit 1
281
+ fi
282
+
283
+ printf '%s\n' "$limits_json" | jq '.windows'
284
+ ```
285
+
286
+ Consumers should tolerate `null` values and warning entries. Parse fields as JSON data instead of depending on pretty-print whitespace or terminal-oriented text.
287
+
288
+ ## Related documentation
289
+
290
+ - [Example JSON output](../examples/codex-limits-output.example.json) — Complete example response produced by `codex-limits --json`.
291
+ - [JSON Schema](../schema/codex-limits.schema.json) — Machine-readable schema for validating the complete JSON response.
292
+ - [Compatibility](compatibility.md) — Runtime, operating system, local-data, terminal, and network requirements.
293
+ - [Agent integrations](agent-integrations.md) — Installation, architecture, behavior, and development of supported agent integrations.
294
+ - [Documentation hub](../README.md) — Task-oriented index for CLI, automation, agent, development, and security guides.
295
+ - [Project README](../../README.md) — Product overview, installation, commands, configuration, and troubleshooting.
@@ -0,0 +1,183 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/simonesiega/codex-limits/main/docs/schema/codex-limits.schema.json",
4
+ "title": "Codex Limits JSON Output",
5
+ "description": "Schema for the output of `codex-limits --json`.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["windows", "coupons", "warnings"],
9
+ "properties": {
10
+ "windows": {
11
+ "type": "object",
12
+ "additionalProperties": false,
13
+ "required": ["fiveHour", "weekly"],
14
+ "properties": {
15
+ "fiveHour": {
16
+ "$ref": "#/$defs/nullableUsageWindow"
17
+ },
18
+ "weekly": {
19
+ "$ref": "#/$defs/nullableUsageWindow"
20
+ }
21
+ }
22
+ },
23
+ "coupons": {
24
+ "anyOf": [
25
+ {
26
+ "$ref": "#/$defs/couponSummary"
27
+ },
28
+ {
29
+ "type": "null"
30
+ }
31
+ ]
32
+ },
33
+ "warnings": {
34
+ "$ref": "#/$defs/warnings"
35
+ }
36
+ },
37
+ "$defs": {
38
+ "nullableUsageWindow": {
39
+ "anyOf": [
40
+ {
41
+ "$ref": "#/$defs/usageWindow"
42
+ },
43
+ {
44
+ "type": "null"
45
+ }
46
+ ]
47
+ },
48
+ "usageWindow": {
49
+ "type": "object",
50
+ "additionalProperties": false,
51
+ "required": ["label", "remainingPercent", "usedPercent", "resetsAt", "resetsIn"],
52
+ "properties": {
53
+ "label": {
54
+ "type": "string"
55
+ },
56
+ "remainingPercent": {
57
+ "$ref": "#/$defs/nullablePercent"
58
+ },
59
+ "usedPercent": {
60
+ "$ref": "#/$defs/nullablePercent"
61
+ },
62
+ "resetsAt": {
63
+ "$ref": "#/$defs/nullableDateTime"
64
+ },
65
+ "resetsIn": {
66
+ "$ref": "#/$defs/nullableString"
67
+ }
68
+ }
69
+ },
70
+ "nullableDateTime": {
71
+ "anyOf": [
72
+ {
73
+ "type": "string",
74
+ "format": "date-time"
75
+ },
76
+ {
77
+ "type": "null"
78
+ }
79
+ ]
80
+ },
81
+ "nullablePercent": {
82
+ "anyOf": [
83
+ {
84
+ "type": "number",
85
+ "minimum": 0,
86
+ "maximum": 100
87
+ },
88
+ {
89
+ "type": "null"
90
+ }
91
+ ]
92
+ },
93
+ "couponSummary": {
94
+ "type": "object",
95
+ "additionalProperties": false,
96
+ "required": [
97
+ "available",
98
+ "earnedThisPeriod",
99
+ "nextExpirationDate",
100
+ "nextExpirationIn",
101
+ "items",
102
+ "warnings"
103
+ ],
104
+ "properties": {
105
+ "available": {
106
+ "$ref": "#/$defs/nullableNonNegativeInteger"
107
+ },
108
+ "earnedThisPeriod": {
109
+ "$ref": "#/$defs/nullableNonNegativeInteger"
110
+ },
111
+ "nextExpirationDate": {
112
+ "$ref": "#/$defs/nullableString"
113
+ },
114
+ "nextExpirationIn": {
115
+ "$ref": "#/$defs/nullableString"
116
+ },
117
+ "items": {
118
+ "type": "array",
119
+ "items": {
120
+ "$ref": "#/$defs/couponItem"
121
+ }
122
+ },
123
+ "warnings": {
124
+ "$ref": "#/$defs/warnings"
125
+ }
126
+ }
127
+ },
128
+ "couponItem": {
129
+ "type": "object",
130
+ "additionalProperties": false,
131
+ "required": ["index", "status", "grantedAt", "expiresAt", "expirationDate", "expiresIn"],
132
+ "properties": {
133
+ "index": {
134
+ "type": "integer",
135
+ "minimum": 1
136
+ },
137
+ "status": {
138
+ "anyOf": [
139
+ {
140
+ "type": "string",
141
+ "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$"
142
+ },
143
+ {
144
+ "type": "null"
145
+ }
146
+ ]
147
+ },
148
+ "grantedAt": {
149
+ "$ref": "#/$defs/nullableDateTime"
150
+ },
151
+ "expiresAt": {
152
+ "$ref": "#/$defs/nullableDateTime"
153
+ },
154
+ "expirationDate": {
155
+ "$ref": "#/$defs/nullableString"
156
+ },
157
+ "expiresIn": {
158
+ "$ref": "#/$defs/nullableString"
159
+ }
160
+ }
161
+ },
162
+ "nullableNonNegativeInteger": {
163
+ "anyOf": [
164
+ {
165
+ "type": "integer",
166
+ "minimum": 0
167
+ },
168
+ {
169
+ "type": "null"
170
+ }
171
+ ]
172
+ },
173
+ "nullableString": {
174
+ "type": ["string", "null"]
175
+ },
176
+ "warnings": {
177
+ "type": "array",
178
+ "items": {
179
+ "type": "string"
180
+ }
181
+ }
182
+ }
183
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simonesiega/codex-limits",
3
- "version": "0.1.6",
3
+ "version": "1.0.0",
4
4
  "description": "Check Codex usage limits, reset times, and reset-credit coupons from a fast, read-only terminal dashboard.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,16 +14,17 @@
14
14
  "exports": {
15
15
  ".": {
16
16
  "types": "./types/index.d.ts",
17
- "import": "./dist/index.js"
17
+ "import": "./dist/opencode.js"
18
18
  }
19
19
  },
20
20
  "types": "./types/index.d.ts",
21
21
  "files": [
22
22
  "dist",
23
23
  "types",
24
- "docs/photos",
24
+ "docs",
25
25
  "scripts/postinstall.cjs",
26
26
  "README.md",
27
+ "CONTRIBUTING.md",
27
28
  "CHANGELOG.md",
28
29
  "SECURITY.md",
29
30
  ".env.example",
@@ -55,7 +56,8 @@
55
56
  "developer-tools",
56
57
  "rate-limits",
57
58
  "usage-tracker",
58
- "pi-package"
59
+ "pi-package",
60
+ "github-copilot-cli"
59
61
  ],
60
62
  "author": "Simone Siega",
61
63
  "license": "MIT",
@@ -89,6 +91,7 @@
89
91
  "devDependencies": {
90
92
  "@earendil-works/pi-coding-agent": "^0.81.1",
91
93
  "@earendil-works/pi-tui": "^0.81.1",
94
+ "@github/copilot-sdk": "^1.0.8",
92
95
  "@opencode-ai/plugin": "^1.18.4",
93
96
  "@types/node": "^22.20.1",
94
97
  "@types/react": "^19.2.17",
package/dist/index.js DELETED
@@ -1,5 +0,0 @@
1
- import{createRequire as LZ}from"node:module";var BZ=Object.create;var{getPrototypeOf:CZ,defineProperty:r,getOwnPropertyNames:OZ}=Object;var DZ=Object.prototype.hasOwnProperty;function TZ(Q){return this[Q]}var AZ,FZ,A1=(Q,Z,$)=>{var G=Q!=null&&typeof Q==="object";if(G){var J=Z?AZ??=new WeakMap:FZ??=new WeakMap,z=J.get(Q);if(z)return z}$=Q!=null?BZ(CZ(Q)):{};let j=Z||!Q||!Q.__esModule?r($,"default",{value:Q,enumerable:!0}):$;for(let K of OZ(Q))if(!DZ.call(j,K))r(j,K,{get:TZ.bind(Q,K),enumerable:!0});if(G)J.set(Q,j);return j};var F1=(Q,Z)=>()=>(Z||Q((Z={exports:{}}).exports,Z),Z.exports);var IZ=(Q)=>Q;function bZ(Q,Z){this[Q]=IZ.bind(null,Z)}var I1=(Q,Z)=>{for(var $ in Z)r(Q,$,{get:Z[$],enumerable:!0,configurable:!0,set:bZ.bind(Z,$)})};var kZ=(Q,Z)=>()=>(Q&&(Z=Q(Q=0)),Z);var b1=(Q)=>Promise.all(Q),L1=LZ(import.meta.url);function p(Q,Z={}){let $=Z.includeSeconds??!1,G=Math.max(Math.floor(Q/1000),0),J=Math.floor(G/86400);G%=86400;let z=Math.floor(G/3600);G%=3600;let j=Math.floor(G/60),K=G%60,X=[];if(J>0)X.push(`${J}d`);if(z>0)X.push(`${z}h`);if(j>0)X.push(`${j}m`);if($&&K>0)X.push(`${K}s`);return X.length>0?X.join(" "):$?`${K}s`:"0m"}function F(Q){if(typeof Q==="number"&&Number.isFinite(Q)){let Z=Q<10000000000?Q*1000:Q,$=new Date(Z);return Number.isNaN($.getTime())?null:$}if(typeof Q==="string"&&Q.trim().length>0){let Z=Q.trim(),$=Number(Z);if(Number.isFinite($))return F($);let G=new Date(Z);return Number.isNaN(G.getTime())?null:G}return null}function mQ(Q){return`${f0[Q.getDay()]} ${Q.getDate()} ${S0[Q.getMonth()]} ${Q.getFullYear()}`}function C2(Q){return`${Q.getDate()} ${E0[Q.getMonth()]} ${Q.getFullYear()} ${w0(Q)}`}function w0(Q){return`${vQ(Q.getHours())}:${vQ(Q.getMinutes())}`}function O2(Q,Z){return Q.getFullYear()===Z.getFullYear()&&Q.getMonth()===Z.getMonth()&&Q.getDate()===Z.getDate()}function vQ(Q){return String(Q).padStart(2,"0")}var f0,S0,E0;var JQ=kZ(()=>{f0=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],S0=["January","February","March","April","May","June","July","August","September","October","November","December"],E0=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function xQ(Q){let Z=[];if(Q.windows.fiveHour)Z.push(NQ("5-hour",Q.windows.fiveHour));if(Q.windows.weekly)Z.push(NQ("Weekly",Q.windows.weekly));if(Z.length===0)Z.push(["Usage limits Unavailable"]);if(Z.push(PZ(Q)),Q.warnings.length>0)Z.push(["Warnings",...Q.warnings.map(($)=>`- ${$}`)]);return Z.map(($)=>$.join(`
2
- `)).join(`
3
-
4
- `)}function NQ(Q,Z){let $=Z.remainingPercent;return[`${Q} ${RZ($)}`,`Remaining ${$===null?"Unknown":`${Math.round($)}% remaining`}`,_Z($),`Reset ${Z.resetsIn?`in ${Z.resetsIn}`:"unknown"}`]}function _Z(Q){let Z=Q===null?0:Math.min(Math.max(Q,0),100),$=Math.round(Z/100*22);return`[${"=".repeat($)}${" ".repeat(22-$)}] ${Math.round(Z)}%`}function PZ(Q){let Z=Q.coupons?.available,G=[`Reset credits ${Z===null||Z===void 0?"Unknown":`${Z} ${Z===1?"credit":"credits"} available`}`],J=Q.coupons?.nextExpirationIn,z=Q.coupons?.nextExpirationDate,j=J&&z?`${J} (${z})`:J??z;if(j!==null&&j!==void 0)G.push(`Next expires ${j}`);return G}function RZ(Q){if(Q===null)return"Unknown";if(Q>=50)return"Healthy";if(Q>=15)return"Low";return"Critical"}function BQ(Q){return xQ(Q)}import{constants as yZ}from"node:fs";import{access as fZ,stat as SZ}from"node:fs/promises";import{homedir as EZ}from"node:os";import{join as b,normalize as o}from"node:path";function U(Q,Z){let $=Q[Z]?.trim();return $?$:null}function T(Q){return Q??process.env}var CQ="CODEX_LIMITS_HOME";function wZ(Q={}){let Z=T(Q.env),$=Q.homeDirectory??U(Z,"HOME")??U(Z,"USERPROFILE")??EZ(),G=Q.appData??U(Z,"APPDATA"),J=Q.localAppData??U(Z,"LOCALAPPDATA"),z=[];if(D(z,U(Z,CQ),"env"),D(z,U(Z,"CODEX_HOME"),"env"),$)D(z,b($,".codex"),"default"),D(z,b($,".config","codex"),"default"),D(z,b($,"Library","Application Support","Codex"),"default"),D(z,b($,"Library","Application Support","Parall","Codex",".codex"),"default");return D(z,G?b(G,"Codex"):null,"default"),D(z,J?b(J,"Codex"):null,"default"),vZ(z)}async function E(Q={}){let Z=T(Q.env),$=U(Z,CQ),G=await Promise.all(wZ(Q).map(async(J)=>({...J,exists:await gZ(J.path)})));return{overrideHome:$?o($):null,candidates:G,foundHome:G.find((J)=>J.exists)?.path??null}}function D(Q,Z,$){if(Z)Q.push({path:o(Z),source:$})}async function gZ(Q){try{if(!(await SZ(Q)).isDirectory())return!1;return await fZ(Q,yZ.R_OK),!0}catch{return!1}}function vZ(Q){let Z=new Set,$=[];for(let G of Q){let J=o(G.path),z=process.platform==="win32"?J.toLowerCase():J;if(!Z.has(z))Z.add(z),$.push({path:J,source:G.source})}return $}import{createReadStream as rZ}from"node:fs";import{lstat as oZ,opendir as iZ,realpath as IQ,stat as nZ}from"node:fs/promises";import{join as bQ}from"node:path";import{isAbsolute as uZ,relative as DQ,resolve as OQ,sep as dZ}from"node:path";var mZ=/(?:chatgpt[-_]?account[-_]?id|access[-_]?token|refresh[-_]?token|id[-_]?token|session[-_]?token|api[-_]?key|account[-_]?id|authorization|cookie|client[-_]?secret|password|credential|secret|token)/,hZ=/[\u0000-\u001f\u007f-\u009f]/g,cZ=new RegExp(`["']?${mZ.source}["']?\\s*[:=]\\s*(?:"(?:\\\\.|[^"\\\\])*"|'(?:\\\\.|[^'\\\\])*'|[^,\\r\\n}\\]&]+)`,"gi"),pZ=[cZ,/Bearer\s+[A-Za-z0-9._~+/=-]+/gi,/\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}(?:\.[A-Za-z0-9_-]{5,})?\b/g,/sk-[A-Za-z0-9_-]{10,}/g,/\b[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}\b/gi,/[A-Za-z0-9_-]{32,}/g];function A(Q){let Z=Q;for(let $ of pZ)Z=Z.replace($,"[redacted]");return Z.replace(hZ,"?")}function i(Q){return Q.map(A)}function TQ(Q,Z){let $=DQ(OQ(Q),OQ(Z));return $===""||!AQ($)}function B(Q,Z){let $=DQ(Q,Z);return $&&!AQ($)?lZ($):"."}function lZ(Q){let Z=A(Q);return Z.length<=240?Z:`${Z.slice(0,239)}…`}function AQ(Q){return Q===".."||Q.startsWith(`..${dZ}`)||uZ(Q)}function H(Q){return typeof Q==="object"&&Q!==null&&!Array.isArray(Q)}function x(Q,Z){let $=Q[Z];return typeof $==="string"&&$.trim()?$.trim():null}var sZ=8,n=20,w=1000,tZ=512,aZ=1000,kQ=25000000,eZ=1e6,Q0=/^rollout-.*\.jsonl$/i;async function LQ(Q){let Z=bQ(Q,"sessions"),$=[];if(await X0(Z))return $.push("Skipped the symbolic-link Codex sessions directory."),{homePath:Q,sessionsRoot:Z,files:[],latestSnapshot:null,warnings:$};let G=await Z0(Q,Z,$),J=[],z=null;for(let j of G.slice(0,n)){let K=B(Q,j.path);if(j.size>kQ){$.push(`Skipped ${K} because it is too large to inspect safely.`),J.push(s(j.path,K,j.modifiedAtMs,!1,"too-large"));continue}try{let X=await J0(Q,j.path);if(J.push(s(j.path,K,j.modifiedAtMs,X.snapshot!==null,null)),X.skippedOversizedLine)$.push(`Skipped an oversized JSONL line in ${K}.`);if(X.snapshot&&!z)z=X.snapshot}catch(X){let Y=X instanceof t;$.push(Y?`Skipped ${K} because it grew too large to inspect safely.`:`Could not inspect ${K}.`),J.push(s(j.path,K,j.modifiedAtMs,!1,Y?"too-large":"read-error"))}if(z)break}if(G.length>n)$.push(`Skipped ${G.length-n} older session files to keep inspection small.`);if(G.length>0&&!z)$.push("No token-count rate-limit snapshot was found in local Codex session logs.");return{homePath:Q,sessionsRoot:Z,files:J,latestSnapshot:z,warnings:$}}async function Z0(Q,Z,$){let G={directories:0,files:[],hitLimit:!1,skippedSymlink:!1};if(await _Q(Z,0,G,$,Q),G.hitLimit)$.push("Stopped session discovery after reaching a safe inspection limit.");if(G.skippedSymlink)$.push("Skipped symbolic links while inspecting Codex sessions.");let J;try{J=await IQ(Z)}catch{return[]}let z=[];for(let j of G.files){let K=await $0(Q,J,j,$);if(K)z.push(K)}return z.sort((j,K)=>K.modifiedAtMs-j.modifiedAtMs||j.path.localeCompare(K.path))}async function $0(Q,Z,$,G){try{let[J,z]=await Promise.all([nZ($),IQ($)]);if(!J.isFile()||!TQ(Z,z))return G.push(`Could not inspect ${B(Q,$)}.`),null;return{path:$,modifiedAtMs:J.mtimeMs,size:J.size}}catch{return G.push(`Could not inspect ${B(Q,$)}.`),null}}async function _Q(Q,Z,$,G,J){if(Z>sZ||$.files.length>=w){$.hitLimit||=$.files.length>=w;return}if($.directories>=tZ){$.hitLimit=!0;return}$.directories+=1;let z;try{z=await G0(Q,$)}catch{if(Z>0)G.push(`Could not inspect ${B(J,Q)}.`);return}for(let j of z){if($.files.length>=w){$.hitLimit=!0;return}let K=bQ(Q,j.name);if(j.isSymbolicLink()){$.skippedSymlink=!0;continue}if(j.isDirectory()){await _Q(K,Z+1,$,G,J);continue}if(j.isFile()&&Q0.test(j.name)){if($.files.push(K),$.files.length>=w){$.hitLimit=!0;return}}}}async function G0(Q,Z){let $=await iZ(Q),G=[];for await(let J of $){if(G.length>=aZ){Z.hitLimit=!0;break}G.push(J)}return G.sort((J,z)=>z.name.localeCompare(J.name))}async function J0(Q,Z){let $=B(Q,Z),G=rZ(Z,{encoding:"utf8",highWaterMark:65536}),J=null,z=null,j="",K=0,X=0,Y=!1,V=!1;for await(let M of G){let C=String(M);if(X+=Buffer.byteLength(C,"utf8"),X>kQ)throw new t;let f=0;while(f<C.length){let S=C.indexOf(`
5
- `,f),xZ=S===-1?C.length:S,UQ=C.slice(f,xZ);if(!Y){let O=Buffer.byteLength(UQ,"utf8");if(K+O>eZ)j="",K=0,Y=!0,V=!0;else j+=UQ,K+=O}if(S===-1)break;if(!Y){let O=FQ(j.endsWith("\r")?j.slice(0,-1):j);if(O.threadId)J=O.threadId;if(O.rateLimits)z={sessionFile:Z,relativePath:$,threadId:J,eventTimestamp:O.timestamp,rateLimits:O.rateLimits}}j="",K=0,Y=!1,f=S+1}}if(!Y&&j.length>0){let M=FQ(j.endsWith("\r")?j.slice(0,-1):j);if(M.threadId)J=M.threadId;if(M.rateLimits)z={sessionFile:Z,relativePath:$,threadId:J,eventTimestamp:M.timestamp,rateLimits:M.rateLimits}}return{snapshot:z,skippedOversizedLine:V}}function FQ(Q){let Z=z0(Q);if(!Z)return{threadId:null,timestamp:null,rateLimits:null};return{threadId:j0(Z),timestamp:x(Z,"timestamp"),rateLimits:K0(Z)}}function z0(Q){let Z=Q.trim();if(!Z)return null;try{let $=JSON.parse(Z);return H($)?$:null}catch{return null}}function j0(Q){if(Q.type!=="session_meta"||!H(Q.payload))return null;return x(Q.payload,"id")}function K0(Q){if(Q.type!=="event_msg"||!H(Q.payload))return null;if(Q.payload.type!=="token_count"||!H(Q.payload.rate_limits))return null;return Q.payload.rate_limits}function s(Q,Z,$,G,J){return{path:Q,relativePath:Z,modifiedAtMs:$,hasSnapshot:G,error:J}}async function X0(Q){try{return(await oZ(Q)).isSymbolicLink()}catch{return!1}}class t extends Error{}import{opendir as H0}from"node:fs/promises";import{extname as V0,join as q0}from"node:path";import{constants as a}from"node:fs";import{lstat as PQ,open as Y0}from"node:fs/promises";class W extends Error{code;constructor(Q){super(Q);this.name="BoundedFileError",this.code=Q}}async function g(Q,Z){let $;try{let G=await PQ(Q);if(!G.isFile())throw new W("not-file");let J=a.O_NOFOLLOW,z=typeof J==="number"?a.O_RDONLY|J:a.O_RDONLY;$=await Y0(Q,z);let j=await $.stat(),K=await PQ(Q);if(!j.isFile()||!K.isFile()||!RQ(G,j)||!RQ(j,K))throw new W("not-file");if(j.size>Z)throw new W("too-large");let X=Buffer.allocUnsafe(Z+1),Y=0;while(Y<=Z){let V=await $.read(X,Y,Z+1-Y,null);if(V.bytesRead===0)break;Y+=V.bytesRead}if(Y>Z)throw new W("too-large");return X.subarray(0,Y).toString("utf8")}catch(G){if(G instanceof W)throw G;if(yQ(G)&&G.code==="ENOENT")throw new W("not-found");if(yQ(G)&&G.code==="ELOOP")throw new W("not-file");throw new W("read-error")}finally{await $?.close().catch(()=>{return})}}function RQ(Q,Z){return Q.dev===Z.dev&&Q.ino===Z.ino}function yQ(Q){return Q instanceof Error&&"code"in Q}var W0=2,e=25,QQ=100,M0=64,U0=500,N0=1e6,x0=/(?:auth|token|cookie|session|secret|credential|api[-_]?key|keychain)/i;async function fQ(Q){let Z={directories:0,files:[],hitDirectoryLimit:!1,hitEntryLimit:!1,hitFileLimit:!1,skippedSensitive:!1,skippedSymlink:!1,warnings:[]};if(await SQ(Q,Q,0,Z),Z.hitDirectoryLimit||Z.hitEntryLimit||Z.hitFileLimit)Z.warnings.push("Stopped local state discovery after reaching a safe inspection limit.");if(Z.skippedSensitive)Z.warnings.push("Skipped a sensitive-looking local file.");if(Z.skippedSymlink)Z.warnings.push("Skipped symbolic links while inspecting local Codex state.");let $=Z.files.sort((J,z)=>J.localeCompare(z)),G=[];for(let J of $.slice(0,e)){let z=B(Q,J);try{let j=await g(J,N0),K=C0(j,z,Z.warnings);G.push({path:J,relativePath:z,json:K.value,error:K.error})}catch(j){Z.warnings.push(j instanceof W&&j.code==="too-large"?`Skipped ${z} because it is too large to inspect safely.`:`Could not read ${z}.`)}}if($.length>e)Z.warnings.push(`Skipped ${$.length-e} extra files to keep inspection small.`);return{homePath:Q,files:G,warnings:Z.warnings}}async function SQ(Q,Z,$,G){if($>W0||G.files.length>=QQ){G.hitFileLimit||=G.files.length>=QQ;return}if(G.directories>=M0){G.hitDirectoryLimit=!0;return}G.directories+=1;let J=await B0(Q,Z,G);for(let z of J){if(G.files.length>=QQ){G.hitFileLimit=!0;return}if(x0.test(z.name)){G.skippedSensitive=!0;continue}let j=q0(Z,z.name);if(z.isSymbolicLink())G.skippedSymlink=!0;else if(z.isDirectory())await SQ(Q,j,$+1,G);else if(z.isFile()&&V0(z.name).toLowerCase()===".json")G.files.push(j)}}async function B0(Q,Z,$){try{let G=await H0(Z),J=[];for await(let z of G){if(J.length>=U0){$.hitEntryLimit=!0;break}J.push(z)}return J.sort((z,j)=>z.name.localeCompare(j.name))}catch{return $.warnings.push(`Could not inspect ${B(Q,Z)}.`),[]}}function C0(Q,Z,$){try{return{value:JSON.parse(Q),error:null}}catch{return $.push(`Could not parse JSON in ${Z}.`),{value:null,error:"invalid-json"}}}import{join as O0,normalize as D0}from"node:path";function N(Q,Z,$){return{code:Q,source:Z,severity:"warning",message:$}}function k(Q){return Q.map((Z)=>A(Z.message))}var T0=1e6;async function v(Q={}){let Z=T(Q.env),$=U(Z,"CODEX_LIMITS_ACCESS_TOKEN"),G=U(Z,"CODEX_LIMITS_ACCOUNT_ID");if($&&G)return{credentials:{accessToken:$,accountId:G},status:"configured",diagnostics:[]};if($||G)return{credentials:null,status:"partial",diagnostics:[N("auth.environment.partial","authentication","Codex authentication environment variables are incomplete.")]};let J=await A0(Q);return J?F0(J):{credentials:null,status:"missing",diagnostics:[]}}async function A0(Q){if(Q.authFile)return D0(Q.authFile);let Z=await E(Q);return Z.foundHome?O0(Z.foundHome,"auth.json"):null}async function F0(Q){let Z;try{Z=await g(Q,T0)}catch($){if($ instanceof W&&$.code==="not-found")return{credentials:null,status:"missing",diagnostics:[]};let G=$ instanceof W&&$.code==="too-large";return{credentials:null,status:"unreadable",diagnostics:[N(G?"auth.file.too-large":"auth.file.unreadable","authentication",G?"Codex auth.json is too large to inspect safely.":"Codex auth.json could not be read safely.")]}}try{let $=JSON.parse(Z);if(!H($))return ZQ();let G=H($.tokens)?$.tokens:$,J=x(G,"access_token"),z=x(G,"account_id");return J&&z?{credentials:{accessToken:J,accountId:z},status:"configured",diagnostics:[]}:ZQ()}catch{return ZQ()}}function ZQ(){return{credentials:null,status:"malformed",diagnostics:[N("auth.file.malformed","authentication","Codex auth.json is malformed or does not contain required credentials.")]}}import{request as I0}from"node:http";import{request as b0}from"node:https";var k0=new Set(["127.0.0.1","::1","[::1]","localhost"]);function _(Q){try{let Z=new URL(Q);return Z.username="",Z.password="",Z.search="",Z.hash="",Z.href}catch{return"[invalid endpoint]"}}async function m(Q){let Z=L0(Q.endpoint);if(!Z.ok)return Z;if(Q.signal?.aborted)return q("aborted");let $=Q.fetch??globalThis.fetch;if(!$)return EQ(Z.url,Q);let G=await _0(Z.url,Q,$);if(G.ok||G.code==="aborted"||!y0(G,Q.fallbackOnHttpError??!1))return G;let J=await EQ(Z.url,Q);if(J.ok||J.code==="http-error"||J.code==="invalid-json"||J.code==="response-too-large")return J;return G}function L0(Q){let Z;try{Z=new URL(Q)}catch{return q("invalid-url")}if(Z.username||Z.password)return q("invalid-url");if(Z.protocol==="https:")return{ok:!0,url:Z};if(Z.protocol==="http:"&&k0.has(Z.hostname.toLowerCase()))return{ok:!0,url:Z};return q("unsupported-protocol")}async function _0(Q,Z,$){let G=gQ(Z.timeoutMs,Z.signal);try{let J=await $(Q.href,{method:"GET",headers:Z.headers,redirect:"error",signal:G.signal});if(!J.ok)return await wQ(J),q("http-error",GQ(J.status));let z=await P0(J,Z.maxResponseBytes);return{ok:!0,status:GQ(J.status)??200,payload:z,transport:"fetch"}}catch(J){if(J instanceof L)return q("response-too-large");if(J instanceof h)return q("invalid-json");if(Z.signal?.aborted)return q("aborted");if(G.didTimeout())return q("timeout");return q("network-error")}finally{G.dispose()}}async function P0(Q,Z){let $=Number(Q.headers?.get("content-length"));if(Number.isFinite($)&&$>Z)throw await wQ(Q),new L;if(Q.body){let G=Q.body.getReader(),J=[],z=0;while(!0){let K=await G.read();if(K.done)break;if(!K.value)continue;if(z+=K.value.byteLength,z>Z)throw await G.cancel?.(),new L;J.push(K.value)}let j=Buffer.concat(J.map((K)=>Buffer.from(K)),z).toString("utf8");return $Q(j)}if(Q.text){let G=await Q.text();if(Buffer.byteLength(G,"utf8")>Z)throw new L;return $Q(G)}throw new h}async function wQ(Q){if(!Q.body)return;try{await Q.body.getReader().cancel?.()}catch{}}function EQ(Q,Z){return new Promise(($)=>{let G=gQ(Z.timeoutMs,Z.signal),J=!1,z=null,j=(X)=>{if(J)return;J=!0,G.dispose(),$(X)},K=()=>{z?.destroy(),j(q(Z.signal?.aborted?"aborted":"timeout"))};G.signal.addEventListener("abort",K,{once:!0});try{z=(Q.protocol==="http:"?I0:b0)(Q,{method:"GET",headers:Z.headers,signal:G.signal},(Y)=>R0(Y,Z.maxResponseBytes,j)),z.on("error",()=>{if(Z.signal?.aborted)j(q("aborted"));else if(G.didTimeout())j(q("timeout"));else j(q("network-error"))}),z.end()}catch{j(q("network-error"))}})}function R0(Q,Z,$){let G=GQ(Q.statusCode);if(G===null||G<200||G>=300){Q.resume(),$(q("http-error",G));return}let J=Number(Q.headers["content-length"]);if(Number.isFinite(J)&&J>Z){Q.destroy(),$(q("response-too-large"));return}let z=[],j=0;Q.on("data",(K)=>{let X=Buffer.isBuffer(K)?K:Buffer.from(K);if(j+=X.length,j>Z){Q.destroy(),$(q("response-too-large"));return}z.push(X)}),Q.on("end",()=>{try{let K=$Q(Buffer.concat(z,j).toString("utf8"));$({ok:!0,status:G,payload:K,transport:"node"})}catch{$(q("invalid-json"))}}),Q.on("error",()=>$(q("network-error")))}function gQ(Q,Z){let $=new AbortController,G=!1,J=Number.isFinite(Q)&&Q>0?Math.min(Math.floor(Q),2147483647):1,z=setTimeout(()=>{G=!0,$.abort()},J),j=()=>$.abort();if(Z?.addEventListener("abort",j,{once:!0}),Z?.aborted)j();return{signal:$.signal,didTimeout:()=>G,dispose:()=>{clearTimeout(z),Z?.removeEventListener("abort",j)}}}function y0(Q,Z){return Q.code==="network-error"||Q.code==="timeout"||Q.code==="invalid-json"||Z&&Q.code==="http-error"}function $Q(Q){try{return JSON.parse(Q)}catch{throw new h}}function GQ(Q){return typeof Q==="number"&&Number.isInteger(Q)&&Q>=0?Q:null}function q(Q,Z=null){return{ok:!1,code:Q,status:Z}}class L extends Error{}class h extends Error{}function c(Q,Z){switch(Q.code){case"aborted":return N("network.request.aborted","network",`${Z} lookup was cancelled.`);case"http-error":return N("network.response.http","network",Q.status===null?`${Z} endpoint returned an invalid HTTP status.`:`${Z} endpoint returned HTTP ${Q.status}.`);case"invalid-json":return N("network.response.invalid-json","network",`${Z} endpoint returned malformed JSON.`);case"invalid-url":return N("network.endpoint.invalid","network",`${Z} endpoint URL is invalid.`);case"response-too-large":return N("network.response.too-large","network",`${Z} endpoint response was too large.`);case"timeout":return N("network.request.timeout","network",`${Z} lookup timed out.`);case"unsupported-protocol":return N("network.endpoint.protocol","network",`${Z} endpoint must use HTTPS or loopback HTTP.`);case"network-error":return N("network.request.failed","network",`${Z} lookup failed.`)}}JQ();var pQ=["credits","reset_credits","items"],uQ=["available_count","availableCount","available"],dQ=["total_earned_count","earned_this_period","earnedThisPeriod","totalEarnedCount"];function lQ(Q,Z,$){if(!H(Q)||!g0(Q))return P(Z,["Live reset coupon endpoint returned an unexpected payload."]);let G=h0(Q,pQ);if(G.malformed)return P(Z,["Live reset coupon endpoint returned an unexpected payload."]);let J=G.value,z=J.map((V,M)=>v0(V,M+1,$)).filter((V)=>V!==null).sort(m0).map((V,M)=>({...V,index:M+1})),j=z.find((V)=>V.status?.toLowerCase()==="available")??z[0]??null,K=cQ(Q,uQ),X=cQ(Q,dQ),Y=[];if(J.length!==z.length)Y.push("Live reset coupon endpoint ignored malformed coupon entries.");if(K.malformed||X.malformed)Y.push("Live reset coupon endpoint ignored malformed summary fields.");return{status:Y.length>0?"partial":"available",available:K.value,earnedThisPeriod:X.value,nextExpirationDate:j?.expirationDate??null,nextExpirationIn:j?.expiresIn??null,items:z,warnings:Y,source:{live:!0,label:"live Codex reset-credit endpoint",endpoint:Z}}}function P(Q,Z=[]){return{status:"unavailable",available:null,earnedThisPeriod:null,nextExpirationDate:null,nextExpirationIn:null,items:[],warnings:Z,source:{live:!1,label:"live Codex reset-credit endpoint",endpoint:Q}}}function g0(Q){return[...pQ,...uQ,...dQ].some((Z)=>(Z in Q))}function v0(Q,Z,$){if(!H(Q))return null;let G=x(Q,"expires_at")??x(Q,"expiresAt"),J=x(Q,"granted_at")??x(Q,"grantedAt"),z=F(G),j=F(J),K=x(Q,"status"),X=K&&/^[a-z][a-z0-9_-]{0,63}$/i.test(K)?K:null,Y=j?J:null,V=z?G:null;if(!X&&!Y&&!V)return null;return{index:Z,status:X,grantedAt:Y,expiresAt:V,expirationDate:z?mQ(z):null,expiresIn:z?p(z.getTime()-$.getTime()):null}}function m0(Q,Z){return hQ(Q.expiresAt)-hQ(Z.expiresAt)}function hQ(Q){return F(Q)?.getTime()??Number.POSITIVE_INFINITY}function h0(Q,Z){let $=!1;for(let G of Z){if(!(G in Q))continue;$=!0;let J=Q[G];if(Array.isArray(J))return{value:J,malformed:!1}}return{value:[],malformed:$}}function cQ(Q,Z){let $=!1;for(let G of Z){if(!(G in Q))continue;$=!0;let J=Q[G];if(typeof J==="number"&&Number.isInteger(J)&&J>=0)return{value:J,malformed:!1}}return{value:null,malformed:$}}var oQ="https://chatgpt.com/backend-api/wham/rate-limit-reset-credits",c0=1e4,p0=1e6;async function iQ(Q={}){let Z=Q.endpoint??oQ,$=_(Z),G=await v(Q);if(!G.credentials){let z=k(G.diagnostics);return u0($,z.length>0?z:["Live reset coupons require a readable Codex auth.json file or CODEX_LIMITS_ACCESS_TOKEN and CODEX_LIMITS_ACCOUNT_ID."])}let J={endpoint:Z,headers:{Authorization:`Bearer ${G.credentials.accessToken}`,"ChatGPT-Account-ID":G.credentials.accountId,"OpenAI-Beta":"codex-1",originator:"Codex Desktop",Accept:"application/json"},timeoutMs:Q.timeoutMs??c0,maxResponseBytes:p0,...Q.fetch?{fetch:Q.fetch}:{},...Q.signal?{signal:Q.signal}:{}};try{let z=await(Q.transport??m)(J);return z.ok?lQ(z.payload,$,Q.now??new Date):rQ($,z)}catch{return rQ($,{ok:!1,code:"network-error",status:null})}}function u0(Q=oQ,Z=[]){return P(_(Q),Z)}function rQ(Q,Z){return P(Q,k([c(Z,"Live reset coupon")]))}JQ();var eQ=5,KQ="5-hour usage limit",XQ="Weekly usage limit",YQ=["fiveHour","five_hour","fiveHourWindow","five_hour_window","primary","primaryWindow","primary_window","main","mainWindow"],HQ=["weekly","week","weeklyWindow","weekly_window","secondary","secondaryWindow","secondary_window","backup","backupWindow"],d0=["used_percent","usedPercent","used","usage","percentUsed","usagePercent","usagePercentage","percent"],l0=["remaining_percent","remainingPercent","remaining","percentRemaining","availablePercent","available_percentage"],r0=["resets_at","resetsAt","resetAt","resetTime","reset_at","reset","ends_at","endsAt","windowEnd"],o0=["resetsIn","resetIn","resets_in","reset_in","timeUntilReset"],i0=["limit_window_seconds","limitWindowSeconds","window_seconds","windowSeconds","window_length_seconds","windowLengthSeconds"],n0=["window_minutes","windowMinutes","window_length_minutes","windowLengthMinutes"],s0=18000,t0=604800;function QZ(Q,Z=new Date){let $=Q.latestSnapshot;if(!$)return WQ(Q.warnings);return l(VQ($.rateLimits,Z),Q.warnings)}function VQ(Q,Z=new Date){return zZ(u(Q,YQ),u(Q,HQ),Z)}function ZZ(Q){return u(Q,YQ)!==null||u(Q,HQ)!==null}function R(Q){let Z=aQ(I(Q,i0,!1)),$=aQ(I(Q,n0,!1)),G=Z??($===null?null:$*60);if(G===s0)return"fiveHour";if(G===t0)return"weekly";return null}function $Z(Q,Z){return{...Q,source:Z}}function qQ(Q,Z,$=[]){return{...l(Q,$),source:Z}}function GZ(Q,Z=new Date){let $={fiveHour:null,weekly:null};for(let G of Q.files){if(!G.json)continue;let J=a0(G.json,Z);$=jZ($,J.windows)}return l($,Q.warnings)}function WQ(Q=[]){return{status:"unavailable",windows:{fiveHour:null,weekly:null},warnings:Q}}function JZ(Q,Z){let $=jZ(Q.windows,Z.windows);return l($,[...Q.warnings,...Z.warnings])}function a0(Q,Z){if(!H(Q))return{windows:{fiveHour:null,weekly:null}};let $=jQ(Q,YQ),G=jQ(Q,HQ);if($||G)return{windows:zZ($,G,Z)};let J=R(Q)??"fiveHour",z=zQ(Q,J==="weekly"?XQ:KQ,Z);return{windows:{fiveHour:J==="fiveHour"?z:null,weekly:J==="weekly"?z:null}}}function zZ(Q,Z,$){let G=[];if(Q)G.push({fallbackKind:"fiveHour",value:Q});if(Z)G.push({fallbackKind:"weekly",value:Z});return{fiveHour:zQ(nQ(G,"fiveHour"),KQ,$),weekly:zQ(nQ(G,"weekly"),XQ,$)}}function zQ(Q,Z,$){if(!Q)return null;let G=tQ(I(Q,d0,!0)),J=tQ(I(Q,l0,!0)),z=G??(J===null?null:d(100-J)),j=J??(G===null?null:d(100-G)),K=I(Q,r0,!0),X=F(K),Y=X?X.toISOString():null,V=Z1(I(Q,o0,!0)),M=X?p(X.getTime()-$.getTime()):V&&V.length<=100?A(V):null,C={label:Z,remainingPercent:j,usedPercent:z,resetsAt:Y,resetsIn:M};return KZ(C)?C:null}function l(Q,Z){return{status:e0(Q),windows:Q,warnings:Z}}function e0(Q){let Z=[Q.fiveHour,Q.weekly].filter(($)=>KZ($));if(Z.length===0)return"unavailable";return Z.every(Q1)?"available":"partial"}function nQ(Q,Z){let $=Q.find((G)=>R(G.value)===Z);if($)return $.value;return Q.find((G)=>G.fallbackKind===Z&&R(G.value)===null)?.value??null}function jZ(Q,Z){return{fiveHour:sQ(Q.fiveHour,Z.fiveHour,KQ),weekly:sQ(Q.weekly,Z.weekly,XQ)}}function sQ(Q,Z,$){if(!Q&&!Z)return null;return{label:$,remainingPercent:Q?.remainingPercent??Z?.remainingPercent??null,usedPercent:Q?.usedPercent??Z?.usedPercent??null,resetsAt:Q?.resetsAt??Z?.resetsAt??null,resetsIn:Q?.resetsIn??Z?.resetsIn??null}}function KZ(Q){return Q!==null&&(Q.remainingPercent!==null||Q.usedPercent!==null||Q.resetsAt!==null||Q.resetsIn!==null)}function Q1(Q){return Q!==null&&Q.remainingPercent!==null&&Q.usedPercent!==null&&(Q.resetsAt!==null||Q.resetsIn!==null)}function jQ(Q,Z,$=0){if($>eQ)return null;for(let G of Z){let J=Q[G];if(H(J))return J}for(let G of Object.values(Q)){if(!H(G))continue;let J=jQ(G,Z,$+1);if(J)return J}return null}function u(Q,Z){for(let $ of Z){let G=Q[$];if(H(G))return G}return null}function I(Q,Z,$,G=0){if(G>eQ)return;for(let J of Z)if(J in Q)return Q[J];if(!$)return;for(let J of Object.values(Q)){if(!H(J))continue;let z=I(J,Z,$,G+1);if(z!==void 0)return z}return}function tQ(Q){if(typeof Q==="number"&&Number.isFinite(Q))return d(Q);if(typeof Q==="string"&&Q.trim().length>0){let Z=Q.trim(),$=Z.endsWith("%")?Z.slice(0,-1):Z,G=Number($);return Number.isFinite(G)?d(G):null}return null}function d(Q){return Math.round(Math.min(Math.max(Q,0),100)*10)/10}function aQ(Q){if(typeof Q==="number"&&Number.isFinite(Q))return Q;if(typeof Q==="string"&&Q.trim().length>0){let Z=Number(Q);return Number.isFinite(Z)?Z:null}return null}function Z1(Q){if(typeof Q==="string"&&Q.trim().length>0)return Q.trim();return null}var $1=5,G1=1000,J1={kind:"unavailable",label:"Unavailable"};function YZ(Q,Z,$){let G=z1(Q)??j1(Q);if(!G)return y(["Live usage endpoint returned an unexpected payload."]);let J=qQ(VQ(G,$),{kind:"api",label:"API",endpoint:Z});if(J.status==="unavailable")return y(["Live usage endpoint returned an unexpected payload."]);if(J.status==="partial")return{...J,warnings:["Live usage endpoint returned incomplete usage data."]};return J}function y(Q){return qQ({fiveHour:null,weekly:null},J1,Q)}function z1(Q){return HZ(Q,(Z)=>{if(!H(Z))return null;let $=Z.rate_limits??Z.rateLimits??Z.rate_limit??Z.rateLimit;if(H($))return $;return ZZ(Z)?Z:null})}function j1(Q){return HZ(Q,(Z)=>{if(!Array.isArray(Z))return null;let $=Z.find((J)=>XZ(J,"fiveHour")),G=Z.find((J)=>XZ(J,"weekly"));return $||G?{fiveHour:$,weekly:G}:null})}function HZ(Q,Z){let $=[{value:Q,depth:0}],G=new WeakSet;for(let J=0;J<$.length;J+=1){let z=$[J];if(!z)break;let j=Z(z.value);if(j)return j;if(z.depth>=$1||typeof z.value!=="object"||!z.value)continue;if(G.has(z.value))continue;G.add(z.value);let K=Array.isArray(z.value)?z.value:H(z.value)?Object.values(z.value):[];for(let X of K){if($.length>=G1)break;$.push({value:X,depth:z.depth+1})}}return null}function XZ(Q,Z){if(!H(Q))return!1;let $=R(Q);if($)return $===Z;let G=String(Q.type??Q.kind??Q.name??Q.label??Q.window??"").toLowerCase();return Z==="fiveHour"?G.includes("primary")||G.includes("5-hour")||G.includes("five"):G.includes("secondary")||G.includes("weekly")||G.includes("week")}var K1="https://chatgpt.com/backend-api/codex/usage",X1=1e4,Y1=1e6;async function VZ(Q={}){return(await H1(Q)).usage}async function H1(Q={}){let Z=q1(Q),$=_(Z),G=await v(Q);if(!G.credentials){let j=k(G.diagnostics);return{usage:y(j.length>0?j:["Live usage requires Codex authentication."]),authenticationFound:!1,endpointStatus:"not-checked"}}let J={endpoint:Z,headers:V1(G.credentials),timeoutMs:Q.timeoutMs??X1,maxResponseBytes:Y1,fallbackOnHttpError:!0,...Q.fetch?{fetch:Q.fetch}:{},...Q.signal?{signal:Q.signal}:{}},z;try{z=await(Q.transport??m)(J)}catch{z={ok:!1,code:"network-error",status:null}}return{usage:z.ok?YZ(z.payload,$,Q.now??new Date):M1(z),authenticationFound:!0,endpointStatus:W1(z)}}function V1(Q){return{Authorization:`Bearer ${Q.accessToken}`,"ChatGPT-Account-ID":Q.accountId,"OpenAI-Beta":"codex-1",originator:"Codex Desktop",Accept:"application/json","User-Agent":"Codex Desktop",Referer:"https://chatgpt.com/codex/cloud/settings/analytics",Origin:"https://chatgpt.com"}}function q1(Q){let Z=T(Q.env);return Q.usageEndpoint??U(Z,"CODEX_LIMITS_USAGE_ENDPOINT")??K1}function W1(Q){if(Q.ok)return"reachable";switch(Q.code){case"http-error":case"invalid-json":case"response-too-large":return"reachable";case"aborted":return"not-checked";case"invalid-url":case"network-error":case"timeout":case"unsupported-protocol":return"unreachable"}}function M1(Q){return y(k([c(Q,"Live usage")]))}var U1={kind:"local",label:"Local"};async function qZ(Q={}){let Z=await N1(Q),$=Q.includeCoupons===!1?null:await iQ(Q),G=$?{...$,warnings:i($.warnings)}:null;return{windows:Z.windows,usageSource:Z.source,coupons:G,warnings:i([...Z.warnings,...G?.warnings??[]])}}async function N1(Q={}){let Z=await VZ(Q);if(Z.status!=="unavailable")return Z;let $=$Z(await B1(Q),U1);return x1(Z,$)}function x1(Q,Z){if(Q.status!=="unavailable")return Q;if(Z.status!=="unavailable")return Z;return{...Z,warnings:[...Q.warnings,...Z.warnings]}}async function B1(Q={}){let Z=Q.now??new Date,$=await E(Q);if(!$.foundHome)return WQ(["No readable local Codex home directory was found."]);let[G,J]=await Promise.all([LQ($.foundHome),fQ($.foundHome)]);return JZ(QZ(G,Z),GZ(J,Z))}var MQ="Codex Limits",C1="Check Codex limits, resets, and credits.",WZ="Could not load Codex limits.";function O1(Q={}){let Z=new WeakSet,$=Q.getLimits??qZ,G=Q.nextFrame??(()=>new Promise((J)=>setTimeout(J,0)));return{id:"codex-limits",tui:async(J)=>{if(Z.has(J))return;let z=D1(J,$,G),j;try{let X=T1(J,z);j=typeof X==="function"?X:void 0}catch{throw Error("Could not register the codex-limits OpenCode command.")}let K=()=>{if(!Z.delete(J))return;try{j?.()}catch{}};Z.add(J);try{J.lifecycle.onDispose(K)}catch{throw K(),Error("Could not register the codex-limits OpenCode lifecycle.")}}}}function D1(Q,Z,$){let G=0;return{title:MQ,value:"codex-limits.show",description:C1,category:"Codex",slash:{name:"codex-limits"},onSelect:async(J)=>{let z=++G;J?.clear(),Q.ui.dialog.clear(),await $();let j=Q.ui.dialog,K=(X)=>j.replace(()=>Q.ui.DialogAlert({title:MQ,message:X}));j.setSize("large"),K("Loading Codex limits...");try{let X=await Z();if(z===G)K(BQ(X))}catch{if(z===G)Q.ui.toast({variant:"error",title:MQ,message:WZ}),K(WZ)}}}}function T1(Q,Z){let $=Q;if(typeof $.keymap?.registerLayer==="function")return $.keymap.registerLayer({commands:[{namespace:"palette",name:Z.value,title:Z.title,desc:Z.description,category:Z.category,slashName:Z.slash?.name,slashAliases:Z.slash?.aliases,run:()=>Z.onSelect?.()}],bindings:[]});if(typeof $.command?.register==="function")return $.command.register(()=>[Z]);throw Error("No supported OpenCode command API is available.")}var MZ=O1(),UZ=MZ,$3=MZ.tui;var NZ=UZ,K3=NZ,z3=NZ.tui;export{z3 as tui,K3 as default};