opencode-minimax-quota 0.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.
Files changed (2) hide show
  1. package/dist/tui.js +131 -0
  2. package/package.json +30 -0
package/dist/tui.js ADDED
@@ -0,0 +1,131 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src.tsx
9
+ import { createSignal, createRoot } from "solid-js";
10
+ import { effect, createElement, setProp, insert } from "@opentui/solid";
11
+ var QUOTA_API = "https://www.minimaxi.com/v1/token_plan/remains";
12
+ var REFRESH_INTERVAL = 5 * 60 * 1e3;
13
+ function getAuthPath() {
14
+ return `${process.env.HOME || "/root"}/.local/share/opencode/auth.json`;
15
+ }
16
+ function loadApiKey() {
17
+ try {
18
+ const { existsSync, readFileSync } = __require("fs");
19
+ const authPath = getAuthPath();
20
+ if (!existsSync(authPath)) return null;
21
+ const auth = JSON.parse(readFileSync(authPath, "utf8"));
22
+ const entry = auth["minimax-cn-coding-plan"];
23
+ if (!entry) return null;
24
+ return typeof entry === "string" ? entry : entry.key;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+ function parseQuota(data) {
30
+ const general = data.model_remains?.find((m) => m.model_name === "general");
31
+ if (!general) return null;
32
+ return {
33
+ intervalPct: general.current_interval_remaining_percent ?? 0,
34
+ weeklyPct: general.current_weekly_remaining_percent ?? 0,
35
+ intervalResetMs: general.end_time ?? 0,
36
+ weeklyResetMs: general.weekly_end_time ?? 0
37
+ };
38
+ }
39
+ async function fetchQuota() {
40
+ const key = loadApiKey();
41
+ if (!key) return null;
42
+ try {
43
+ const res = await fetch(QUOTA_API, {
44
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" }
45
+ });
46
+ const data = await res.json();
47
+ if (data.base_resp?.status_code !== 0) return null;
48
+ return parseQuota(data);
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+ function fmtResetTime(ms) {
54
+ if (!ms) return "?";
55
+ return new Intl.DateTimeFormat("zh-CN", {
56
+ timeZone: "Asia/Shanghai",
57
+ weekday: "short",
58
+ hour: "2-digit",
59
+ minute: "2-digit",
60
+ hour12: false
61
+ }).format(ms);
62
+ }
63
+ function makeBox(props, children) {
64
+ const el = createElement("box");
65
+ for (const [k, v] of Object.entries(props || {})) setProp(el, k, v);
66
+ for (const c of children || []) insert(el, c);
67
+ return el;
68
+ }
69
+ function makeText(props, ...children) {
70
+ const el = createElement("text");
71
+ for (const [k, v] of Object.entries(props || {})) setProp(el, k, v);
72
+ for (const c of children || []) insert(el, c);
73
+ return el;
74
+ }
75
+ function makeSpan(props, ...children) {
76
+ const el = createElement("span");
77
+ for (const [k, v] of Object.entries(props || {})) setProp(el, k, v);
78
+ for (const c of children || []) insert(el, c);
79
+ return el;
80
+ }
81
+ function renderQuotaBadge(theme, q) {
82
+ if (!q) return makeText({ fg: theme.textMuted }, "MiniMax \u52A0\u8F7D...");
83
+ const iColor = q.intervalPct > 20 ? theme.success : theme.error;
84
+ const wColor = q.weeklyPct > 20 ? theme.success : theme.error;
85
+ return makeBox({ flexDirection: "row", gap: 1, alignItems: "center" }, [
86
+ makeText({ fg: theme.textMuted }, "MiniMax"),
87
+ makeText(
88
+ { fg: theme.text },
89
+ "5h:",
90
+ makeSpan({ style: { fg: iColor } }, `${q.intervalPct}%`)
91
+ ),
92
+ makeText({ fg: theme.textMuted }, fmtResetTime(q.intervalResetMs)),
93
+ makeText({ fg: theme.textMuted }, "\xB7"),
94
+ makeText(
95
+ { fg: theme.text },
96
+ "\u5468:",
97
+ makeSpan({ style: { fg: wColor } }, `${q.weeklyPct}%`)
98
+ ),
99
+ makeText({ fg: theme.textMuted }, fmtResetTime(q.weeklyResetMs))
100
+ ]);
101
+ }
102
+ var tui = async (api) => {
103
+ createRoot((dispose) => {
104
+ const [quota, setQuota] = createSignal(null);
105
+ effect(() => {
106
+ const q = quota();
107
+ const theme = api.theme.current;
108
+ const el = renderQuotaBadge(theme, q);
109
+ api.slots.register({
110
+ slots: {
111
+ session_prompt_right() {
112
+ return el;
113
+ }
114
+ }
115
+ });
116
+ });
117
+ const load = async () => {
118
+ const q = await fetchQuota();
119
+ setQuota(q);
120
+ };
121
+ api.lifecycle.onDispose(dispose);
122
+ load();
123
+ setInterval(load, REFRESH_INTERVAL);
124
+ api.event.on("session.created", () => load());
125
+ api.event.on("session.next.prompted", () => load());
126
+ });
127
+ };
128
+ var src_default = { id: "opencode-minimax-quota", tui };
129
+ export {
130
+ src_default as default
131
+ };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "opencode-minimax-quota",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode TUI plugin to display MiniMax token plan quota (5h + weekly) in the prompt bar",
5
+ "type": "module",
6
+ "main": "./dist/tui.js",
7
+ "exports": {
8
+ "./tui": "./dist/tui.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "esbuild src.tsx --bundle --outfile=dist/tui.js --format=esm --platform=browser --external:fs --external:path --external:@opentui/solid --external:@opentui/core --external:solid-js"
15
+ },
16
+ "peerDependencies": {
17
+ "@opentui/solid": "*",
18
+ "@opentui/core": "*",
19
+ "solid-js": "*"
20
+ },
21
+ "keywords": [
22
+ "opencode",
23
+ "plugin",
24
+ "tui",
25
+ "quota",
26
+ "minimax",
27
+ "MiniMax"
28
+ ],
29
+ "license": "MIT"
30
+ }