@cardinal4/opencode-tavily-quota 1.0.2 → 1.1.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 +11 -3
- package/dist/tui.js +6 -4
- package/dist/usage.js +38 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ session sidebar.
|
|
|
5
5
|
|
|
6
6
|
```
|
|
7
7
|
Tavily
|
|
8
|
+
resets in 6d 12h
|
|
8
9
|
█████████████████████████ 84% left
|
|
9
10
|
```
|
|
10
11
|
|
|
@@ -20,8 +21,10 @@ Or declare it in `cli.json` to pass the API key option (see
|
|
|
20
21
|
the package so the plugin is not loaded twice.
|
|
21
22
|
|
|
22
23
|
The sidebar follows the built-in quota styling — the heading uses the default
|
|
23
|
-
text color and the body (bar included) uses the muted/subdued text color.
|
|
24
|
-
|
|
24
|
+
text color and the body (bar included) uses the muted/subdued text color. A
|
|
25
|
+
countdown between the heading and the bar shows how long until the monthly
|
|
26
|
+
credits reset. It polls every 10 minutes and can be refreshed on demand with the
|
|
27
|
+
command below.
|
|
25
28
|
|
|
26
29
|
## Requirements
|
|
27
30
|
|
|
@@ -114,7 +117,7 @@ reported in the sidebar instead of being used as the key.
|
|
|
114
117
|
## Commands
|
|
115
118
|
|
|
116
119
|
- `/tavily-quota` — slash command that re-fetches and reports the current quota
|
|
117
|
-
in a toast.
|
|
120
|
+
and reset countdown in a toast.
|
|
118
121
|
- "Refresh Tavily quota" — the same action from the command palette (`Ctrl+P`).
|
|
119
122
|
|
|
120
123
|
## Data source
|
|
@@ -125,6 +128,11 @@ e.g. `Researcher` with 1000 monthly credits) and falls back to the per-key limit
|
|
|
125
128
|
when the plan limit is absent. A `null` per-key limit (unlimited key) is
|
|
126
129
|
therefore handled.
|
|
127
130
|
|
|
131
|
+
The response has no reset timestamp, so the sidebar countdown is derived from
|
|
132
|
+
the calendar: Tavily resets credits on the first day of each month (not the
|
|
133
|
+
billing date), and the plugin assumes `00:00 UTC` for that reset. If Tavily
|
|
134
|
+
later exposes an explicit reset time, prefer it over this derivation.
|
|
135
|
+
|
|
128
136
|
## Development
|
|
129
137
|
|
|
130
138
|
`usage.ts` is deliberately free of OpenCode and OpenTUI imports so it can be
|
package/dist/tui.js
CHANGED
|
@@ -25,7 +25,7 @@ import { createElement as _$createElement } from "@opentui/solid";
|
|
|
25
25
|
|
|
26
26
|
import { Plugin } from "@opencode/plugin/tui";
|
|
27
27
|
import { createSignal } from "solid-js";
|
|
28
|
-
import { fetchQuotaWithFallback, formatPercent, formatQuotaLine, formatRemaining } from "./usage.js";
|
|
28
|
+
import { fetchQuotaWithFallback, formatPercent, formatQuotaLine, formatRemaining, formatResetLine } from "./usage.js";
|
|
29
29
|
import { TavilyQuotaRpc } from "./rpc.js";
|
|
30
30
|
export const PLUGIN_ID = "opencode-tavily-quota";
|
|
31
31
|
// Tavily's `/usage` endpoint allows at most 10 requests per 10 minutes, so poll
|
|
@@ -43,7 +43,7 @@ function readThemeColors(context) {
|
|
|
43
43
|
textMuted: text.subdued ?? text.muted
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
export function buildLines(state) {
|
|
46
|
+
export function buildLines(state, now = Date.now()) {
|
|
47
47
|
if (state.status === "loading") {
|
|
48
48
|
return ["loading…"];
|
|
49
49
|
}
|
|
@@ -51,7 +51,9 @@ export function buildLines(state) {
|
|
|
51
51
|
return [`⚠ ${state.message}`];
|
|
52
52
|
}
|
|
53
53
|
const snapshot = state.snapshot;
|
|
54
|
-
|
|
54
|
+
// Reset countdown sits between the heading and the bar, matching the sidebar
|
|
55
|
+
// order used by the built-in quota section.
|
|
56
|
+
return [formatResetLine(now), formatQuotaLine(snapshot)];
|
|
55
57
|
}
|
|
56
58
|
function TavilyQuotaSidebar(props) {
|
|
57
59
|
const colors = () => readThemeColors(props.context);
|
|
@@ -100,7 +102,7 @@ function TavilyQuotaCommands(props) {
|
|
|
100
102
|
},
|
|
101
103
|
run: async () => {
|
|
102
104
|
const next = await props.refresh();
|
|
103
|
-
const message = next.status === "ready" ? `${formatRemaining(next.snapshot)} · ${formatPercent(next.snapshot.percentRemaining)}` : next.status === "error" ? next.message : "still loading…";
|
|
105
|
+
const message = next.status === "ready" ? `${formatRemaining(next.snapshot)} · ${formatPercent(next.snapshot.percentRemaining)} · ${formatResetLine()}` : next.status === "error" ? next.message : "still loading…";
|
|
104
106
|
props.context.ui.toast.show({
|
|
105
107
|
title: "Tavily quota",
|
|
106
108
|
message,
|
package/dist/usage.js
CHANGED
|
@@ -185,6 +185,44 @@ export function formatQuotaLine(snapshot, width = DEFAULT_BAR_WIDTH) {
|
|
|
185
185
|
export function formatRemaining(snapshot) {
|
|
186
186
|
return `${snapshot.remaining} credits left`;
|
|
187
187
|
}
|
|
188
|
+
const MS_PER_MINUTE = 60_000;
|
|
189
|
+
const MS_PER_HOUR = 3_600_000;
|
|
190
|
+
const MS_PER_DAY = 86_400_000;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* When the credit quota next resets, as epoch milliseconds.
|
|
194
|
+
*
|
|
195
|
+
* Tavily resets credits on the first day of each month (calendar-based, not the
|
|
196
|
+
* billing date) and `/usage` does not report when. Derive it as `00:00 UTC` on
|
|
197
|
+
* the 1st of the following month. Tavily does not document a reset timezone;
|
|
198
|
+
* UTC is assumed (see the README).
|
|
199
|
+
*/
|
|
200
|
+
export function nextMonthlyResetAt(now = Date.now()) {
|
|
201
|
+
const date = new Date(now);
|
|
202
|
+
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1, 0, 0, 0, 0);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Countdown to `resetAt`, e.g. `12d 4h`, `5h 12m`, or `45m`.
|
|
207
|
+
*
|
|
208
|
+
* Days and hours are floored so the value never overstates the time left; a
|
|
209
|
+
* sub-minute remainder rounds up to `1m`, and a past reset reads `0m`.
|
|
210
|
+
*/
|
|
211
|
+
export function formatResetCountdown(resetAt, now = Date.now()) {
|
|
212
|
+
const diff = resetAt - now;
|
|
213
|
+
if (!Number.isFinite(diff) || diff <= 0) return "0m";
|
|
214
|
+
const days = Math.floor(diff / MS_PER_DAY);
|
|
215
|
+
const hours = Math.floor(diff % MS_PER_DAY / MS_PER_HOUR);
|
|
216
|
+
const minutes = Math.floor(diff % MS_PER_HOUR / MS_PER_MINUTE);
|
|
217
|
+
if (days > 0) return `${days}d ${hours}h`;
|
|
218
|
+
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
219
|
+
return `${Math.max(1, minutes)}m`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Sidebar line for the next reset, e.g. `resets in 12d 4h`. */
|
|
223
|
+
export function formatResetLine(now = Date.now()) {
|
|
224
|
+
return `resets in ${formatResetCountdown(nextMonthlyResetAt(now), now)}`;
|
|
225
|
+
}
|
|
188
226
|
|
|
189
227
|
/**
|
|
190
228
|
* Fetch and derive the current Tavily quota.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@cardinal4/opencode-tavily-quota",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"description": "OpenCode 2 TUI plugin that shows remaining Tavily API credits in the sidebar.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|