opencode-codex-usage-tui 1.3.2 → 1.4.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 +3 -3
- package/dist/tui.js +19 -19
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# opencode-codex-usage
|
|
1
|
+
# opencode-codex-usage-tui
|
|
2
2
|
|
|
3
3
|
OpenCode TUI plugin that shows remaining ChatGPT Codex usage limits in the sidebar.
|
|
4
4
|
|
|
@@ -23,7 +23,7 @@ Add the published package to OpenCode's TUI configuration:
|
|
|
23
23
|
```json
|
|
24
24
|
{
|
|
25
25
|
"$schema": "https://opencode.ai/tui.json",
|
|
26
|
-
"plugin": ["opencode-codex-usage-tui@1.
|
|
26
|
+
"plugin": ["opencode-codex-usage-tui@1.4.0"]
|
|
27
27
|
}
|
|
28
28
|
```
|
|
29
29
|
|
|
@@ -44,7 +44,7 @@ For example, a local file URL can be used when supported:
|
|
|
44
44
|
```json
|
|
45
45
|
{
|
|
46
46
|
"$schema": "https://opencode.ai/tui.json",
|
|
47
|
-
"plugin": ["file:///absolute/path/to/opencode-codex-usage-tui-1.
|
|
47
|
+
"plugin": ["file:///absolute/path/to/opencode-codex-usage-tui-1.4.0.tgz"]
|
|
48
48
|
}
|
|
49
49
|
```
|
|
50
50
|
|
package/dist/tui.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TextAttributes } from "@opentui/core";
|
|
1
2
|
import { createElement, insert, setProp } from "@opentui/solid";
|
|
2
3
|
import { fetchUsage, normalizePayload } from "./core.js";
|
|
3
4
|
const DASHBOARD_URL = "https://chatgpt.com/codex/settings/usage";
|
|
@@ -12,7 +13,9 @@ let collapsedInitialized = false;
|
|
|
12
13
|
async function fetchOnce() {
|
|
13
14
|
if (!fetchRequested || inflight) return;
|
|
14
15
|
inflight = true;
|
|
15
|
-
|
|
16
|
+
if (state.status !== "ok" || state.windows.length === 0)
|
|
17
|
+
state = { ...state, status: "loading", message: void 0 };
|
|
18
|
+
else state = { ...state, message: void 0 };
|
|
16
19
|
try {
|
|
17
20
|
const result = await fetchUsage();
|
|
18
21
|
if (!result.ok) {
|
|
@@ -62,7 +65,11 @@ function fmtReset(resetSec) {
|
|
|
62
65
|
return `${mins}m`;
|
|
63
66
|
}
|
|
64
67
|
function fmtPct(pct) {
|
|
65
|
-
return `${
|
|
68
|
+
return `${Math.round(pct)}%`;
|
|
69
|
+
}
|
|
70
|
+
function fmtCredits(balance) {
|
|
71
|
+
const amount = Number(balance);
|
|
72
|
+
return Number.isFinite(amount) ? amount.toFixed(2) : balance;
|
|
66
73
|
}
|
|
67
74
|
function liveResetSeconds(usage) {
|
|
68
75
|
if (!state.lastFetch) return usage.resetInSec;
|
|
@@ -113,24 +120,19 @@ const tui = async (api) => {
|
|
|
113
120
|
slots: {
|
|
114
121
|
sidebar_content() {
|
|
115
122
|
const theme = api.theme.current;
|
|
116
|
-
const muted = theme.textMuted;
|
|
117
123
|
const normal = theme.text;
|
|
118
|
-
const valStyle = { fg:
|
|
119
|
-
const headerTitle = valNode({
|
|
124
|
+
const valStyle = { fg: normal };
|
|
125
|
+
const headerTitle = valNode({ attributes: TextAttributes.BOLD, fg: normal });
|
|
120
126
|
const headerSummary = valNode(valStyle);
|
|
121
127
|
const planVal = valNode(valStyle);
|
|
122
128
|
const creditsVal = valNode(valStyle);
|
|
123
129
|
const resetsVal = valNode(valStyle);
|
|
124
130
|
const statusVal = valNode(valStyle);
|
|
125
131
|
function usageBlock() {
|
|
126
|
-
const label = valNode({ fg:
|
|
132
|
+
const label = valNode({ fg: normal });
|
|
127
133
|
const pct = valNode(valStyle);
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [label, pct]),
|
|
131
|
-
box({ flexDirection: "row", width: "100%", justifyContent: "flex-end" }, [reset])
|
|
132
|
-
]);
|
|
133
|
-
return { block, label, pct, reset };
|
|
134
|
+
const block = box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [label, pct]);
|
|
135
|
+
return { block, label, pct };
|
|
134
136
|
}
|
|
135
137
|
const primary = usageBlock();
|
|
136
138
|
const secondary = usageBlock();
|
|
@@ -141,13 +143,11 @@ const tui = async (api) => {
|
|
|
141
143
|
if (!usage) {
|
|
142
144
|
setNodeText(view.label, "");
|
|
143
145
|
setNodeText(view.pct, "");
|
|
144
|
-
setNodeText(view.reset, "");
|
|
145
146
|
return;
|
|
146
147
|
}
|
|
147
148
|
const reset = fmtReset(liveResetSeconds(usage));
|
|
148
149
|
setNodeText(view.label, usage.label);
|
|
149
|
-
setNodeText(view.pct, `${fmtPct(remainingPercent(usage.usedPercent))}
|
|
150
|
-
setNodeText(view.reset, reset ? `reset ${reset}` : "");
|
|
150
|
+
setNodeText(view.pct, `${fmtPct(remainingPercent(usage.usedPercent))}${reset ? ` (${reset})` : ""}${usage.limitReached ? " exhausted" : ""}`);
|
|
151
151
|
}
|
|
152
152
|
function clearWindows() {
|
|
153
153
|
setUsageWindow(primary, void 0);
|
|
@@ -170,7 +170,7 @@ const tui = async (api) => {
|
|
|
170
170
|
setUsageWindow(extraTwo, windows[3]);
|
|
171
171
|
setNodeText(
|
|
172
172
|
creditsVal,
|
|
173
|
-
state.credits ? state.credits.hasCredits ? state.credits.unlimited ? "unlimited" : state.credits.balance : "none" : ""
|
|
173
|
+
state.credits ? state.credits.hasCredits ? state.credits.unlimited ? "unlimited" : fmtCredits(state.credits.balance) : "none" : ""
|
|
174
174
|
);
|
|
175
175
|
setNodeText(resetsVal, state.resetCredits === void 0 ? "" : `${state.resetCredits} available`);
|
|
176
176
|
setNodeText(statusVal, "");
|
|
@@ -210,13 +210,13 @@ ${DASHBOARD_URL}`);
|
|
|
210
210
|
}
|
|
211
211
|
const header = box({ flexDirection: "row", width: "100%", onMouseUp: () => toggle() }, [headerTitle, headerSummary]);
|
|
212
212
|
const body = box({ flexDirection: "column", width: "100%", visible: !collapsed }, [
|
|
213
|
-
box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg:
|
|
213
|
+
box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg: normal }, ["Plan"]), planVal]),
|
|
214
214
|
primary.block,
|
|
215
215
|
secondary.block,
|
|
216
216
|
extraOne.block,
|
|
217
217
|
extraTwo.block,
|
|
218
|
-
box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg:
|
|
219
|
-
box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg:
|
|
218
|
+
box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg: normal }, ["Credits"]), creditsVal]),
|
|
219
|
+
box({ flexDirection: "row", width: "100%", justifyContent: "space-between" }, [txt({ fg: normal }, ["Resets"]), resetsVal]),
|
|
220
220
|
statusVal
|
|
221
221
|
]);
|
|
222
222
|
const root = box({ flexDirection: "column", width: "100%" }, [header, body]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-codex-usage-tui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "OpenCode TUI plugin that shows remaining ChatGPT Codex usage limits in the sidebar",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"limits",
|
|
44
44
|
"sidebar"
|
|
45
45
|
],
|
|
46
|
-
"homepage": "https://github.com/ahmadmcer/opencode-codex-usage",
|
|
46
|
+
"homepage": "https://github.com/ahmadmcer/opencode-codex-usage-tui",
|
|
47
47
|
"repository": {
|
|
48
48
|
"type": "git",
|
|
49
|
-
"url": "git+https://github.com/ahmadmcer/opencode-codex-usage.git"
|
|
49
|
+
"url": "git+https://github.com/ahmadmcer/opencode-codex-usage-tui.git"
|
|
50
50
|
}
|
|
51
51
|
}
|