opencode-usage-plugin 0.0.1-dev
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/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/command/usage.md +24 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +672 -0
- package/dist/index.js.map +7 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 zenobi.us
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# opencode-usage-plugin
|
|
2
|
+
|
|
3
|
+
OpenCode plugin that fetches subscription usage for OpenAI, Google, and z.ai.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- `/usage` command for all providers
|
|
8
|
+
- `/usage <provider>` for a single provider
|
|
9
|
+
- Returns normalized JSON for provider usage
|
|
10
|
+
- Reads tokens from OpenCode and auth plugins
|
|
11
|
+
|
|
12
|
+
## Installation in OpenCode
|
|
13
|
+
|
|
14
|
+
Create or edit `~/.config/opencode/opencode.json`:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"$schema": "https://opencode.ai/config.json",
|
|
19
|
+
"plugin": ["opencode-usage-plugin@0.0.1"]
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For local development, use a relative path:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"plugin": ["./path/to/opencode-usage-plugin"]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Provider Setup
|
|
32
|
+
|
|
33
|
+
### OpenAI
|
|
34
|
+
|
|
35
|
+
- Install the `opencode-openai-codex-auth` plugin, or ensure `openai` auth exists in
|
|
36
|
+
`~/.local/share/opencode/auth.json` (aliases: `codex`, `chatgpt`).
|
|
37
|
+
- The plugin also checks `~/.opencode/auth/openai.json` as a fallback.
|
|
38
|
+
|
|
39
|
+
### Google
|
|
40
|
+
|
|
41
|
+
- Install an Antigravity auth plugin (for example, `opencode-antigravity-auth`) or ensure
|
|
42
|
+
`google` auth exists in `~/.local/share/opencode/auth.json` (alias: `antigravity`).
|
|
43
|
+
- Ensure accounts are stored in `~/.config/opencode/antigravity-accounts.json`.
|
|
44
|
+
- The `activeIndex` account is used for usage checks (falls back to the first account).
|
|
45
|
+
|
|
46
|
+
### z.ai
|
|
47
|
+
|
|
48
|
+
- Add a `zai-coding-plan` entry in `~/.local/share/opencode/auth.json` with the API key
|
|
49
|
+
(aliases: `zai`, `z.ai`), or set `ZAI_API_KEY`.
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
/usage
|
|
55
|
+
/usage openai
|
|
56
|
+
/usage google
|
|
57
|
+
/usage zai-coding-plan
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Output
|
|
61
|
+
|
|
62
|
+
- `usage` tool returns a JSON array (string) of provider usage results
|
|
63
|
+
- `command/usage.md` formats a human-readable summary
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install
|
|
69
|
+
mise run build
|
|
70
|
+
npm test
|
|
71
|
+
mise run lint
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Testing
|
|
75
|
+
|
|
76
|
+
Tests use **vitest** with a provider-specific auth configuration system.
|
|
77
|
+
|
|
78
|
+
**Run all tests** (mocks only, default):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm test
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Run tests with real auth**:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Edit .env.test and set desired provider(s) to 1
|
|
88
|
+
TEST_REAL_OPENAI_AUTH=1
|
|
89
|
+
TEST_REAL_GOOGLE_AUTH=1
|
|
90
|
+
TEST_REAL_ZAI_CODING_PLAN_AUTH=1
|
|
91
|
+
npm test
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Run specific provider tests**:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm test -- src/providers/openai/fetch.test.ts
|
|
98
|
+
npm test -- src/providers/google/fetch.test.ts
|
|
99
|
+
npm test -- src/providers/zai-coding-plan/fetch.test.ts
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Test structure**:
|
|
103
|
+
|
|
104
|
+
- `src/providers/common/test-helpers.ts` - Shared test utilities and fixtures
|
|
105
|
+
- `src/providers/*/*.test.ts` - Provider-specific tests with mocks and real auth
|
|
106
|
+
- `.env.test` - Auth configuration flags (committed to repo)
|
|
107
|
+
- `vitest.config.ts` - Test configuration
|
|
108
|
+
|
|
109
|
+
Tests are organized into two categories per provider:
|
|
110
|
+
|
|
111
|
+
- **Mock tests** - Run by default, test all edge cases and error handling
|
|
112
|
+
- **Real auth tests** - Skipped by default, enabled via `.env.test` flags
|
|
113
|
+
|
|
114
|
+
**Adding new providers**:
|
|
115
|
+
|
|
116
|
+
1. Implement provider function in `src/providers/`
|
|
117
|
+
2. Add `TEST_REAL_NEWPROVIDER_AUTH=0` to `.env.test`
|
|
118
|
+
3. Create `src/providers/newprovider.test.ts` following existing patterns
|
|
119
|
+
4. Add helpers to `src/test-helpers.ts` if needed
|
|
120
|
+
|
|
121
|
+
## Author
|
|
122
|
+
|
|
123
|
+
Nelson Pires <nelsonpires.sn@gmail.com>
|
|
124
|
+
|
|
125
|
+
## Repository
|
|
126
|
+
|
|
127
|
+
https://github.com/nelsonPires5/opencode-usage-plugin
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT License. See the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show remaining usage for AI coding providers (OpenAI, Google, z.ai)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Call the `usage` tool.
|
|
6
|
+
|
|
7
|
+
If $ARGUMENTS is empty, fetch usage for all configured providers.
|
|
8
|
+
If $ARGUMENTS is provided, it should be one of: openai, google, zai-coding-plan (aliases: codex, antigravity, zai).
|
|
9
|
+
|
|
10
|
+
The tool returns a JSON string with a list of provider results:
|
|
11
|
+
|
|
12
|
+
- `provider`, `ok`, `configured`, `error`
|
|
13
|
+
- `usage.windows` for global windows
|
|
14
|
+
- `usage.models[model].windows` for per-model windows
|
|
15
|
+
|
|
16
|
+
Each window includes:
|
|
17
|
+
|
|
18
|
+
- `remainingPercent` - remaining usage percentage
|
|
19
|
+
- `resetAfterFormatted` - human-readable time remaining (e.g., "2w 3d 5h 10m 30s")
|
|
20
|
+
- `resetAtFormatted` - exact reset datetime (e.g., "Thursday, January 16, 2026, 2:30:45 PM EST")
|
|
21
|
+
- `windowSeconds` - size of the usage window
|
|
22
|
+
|
|
23
|
+
Parse the JSON, then return a short markdown summary per provider.
|
|
24
|
+
Include remaining percent, reset time remaining, reset datetime, window size, and any errors.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { tool } from "@opencode-ai/plugin";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { readdir, readFile as readFile2 } from "node:fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
|
|
7
|
+
// src/providers/common/time.ts
|
|
8
|
+
var calculateResetAfterSeconds = (resetAt, now = Date.now()) => {
|
|
9
|
+
if (!resetAt) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const diffMs = resetAt - now;
|
|
13
|
+
if (diffMs <= 0) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
return Math.floor(diffMs / 1e3);
|
|
17
|
+
};
|
|
18
|
+
var formatDuration = (seconds) => {
|
|
19
|
+
if (seconds <= 0) {
|
|
20
|
+
return "0s";
|
|
21
|
+
}
|
|
22
|
+
const weeks = Math.floor(seconds / 604800);
|
|
23
|
+
const days = Math.floor(seconds % 604800 / 86400);
|
|
24
|
+
const hours = Math.floor(seconds % 86400 / 3600);
|
|
25
|
+
const minutes = Math.floor(seconds % 3600 / 60);
|
|
26
|
+
const secs = seconds % 60;
|
|
27
|
+
const parts = [];
|
|
28
|
+
if (weeks > 0) parts.push(`${weeks}w`);
|
|
29
|
+
if (days > 0) parts.push(`${days}d`);
|
|
30
|
+
if (hours > 0) parts.push(`${hours}h`);
|
|
31
|
+
if (minutes > 0) parts.push(`${minutes}m`);
|
|
32
|
+
if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
|
|
33
|
+
return parts.join(" ");
|
|
34
|
+
};
|
|
35
|
+
var formatResetAt = (resetAtMs) => {
|
|
36
|
+
return new Date(resetAtMs).toLocaleString(void 0, {
|
|
37
|
+
weekday: "long",
|
|
38
|
+
year: "numeric",
|
|
39
|
+
month: "long",
|
|
40
|
+
day: "numeric",
|
|
41
|
+
hour: "numeric",
|
|
42
|
+
minute: "numeric",
|
|
43
|
+
second: "numeric",
|
|
44
|
+
timeZoneName: "short"
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/providers/common/files.ts
|
|
49
|
+
import { readFile } from "node:fs/promises";
|
|
50
|
+
import { homedir } from "node:os";
|
|
51
|
+
import { join } from "node:path";
|
|
52
|
+
var xdgDataHome = () => process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share");
|
|
53
|
+
var xdgConfigHome = () => process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
|
|
54
|
+
var AUTH_PATHS = {
|
|
55
|
+
opencode: () => join(xdgDataHome(), "opencode", "auth.json"),
|
|
56
|
+
openaiPlugin: () => join(homedir(), ".opencode", "auth", "openai.json"),
|
|
57
|
+
antigravityConfig: () => join(xdgConfigHome(), "opencode", "antigravity-accounts.json"),
|
|
58
|
+
antigravityData: () => join(xdgDataHome(), "opencode", "antigravity-accounts.json")
|
|
59
|
+
};
|
|
60
|
+
var readJson = async (filePath) => {
|
|
61
|
+
try {
|
|
62
|
+
const content = await readFile(filePath, "utf-8");
|
|
63
|
+
return JSON.parse(content);
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var loadOpenCodeAuth = async () => {
|
|
69
|
+
return readJson(AUTH_PATHS.opencode());
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// src/providers/common/registry.ts
|
|
73
|
+
var PROVIDER_ALIASES = {
|
|
74
|
+
openai: ["openai", "codex", "chatgpt"],
|
|
75
|
+
google: ["google", "antigravity"],
|
|
76
|
+
"zai-coding-plan": ["zai-coding-plan", "zai", "z.ai"]
|
|
77
|
+
};
|
|
78
|
+
var parseProvider = (input) => {
|
|
79
|
+
if (!input) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const normalized = input.trim().toLowerCase();
|
|
83
|
+
for (const [providerId, aliases] of Object.entries(PROVIDER_ALIASES)) {
|
|
84
|
+
if (aliases.includes(normalized)) {
|
|
85
|
+
return providerId;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
};
|
|
90
|
+
var getProviderAliases = (provider) => {
|
|
91
|
+
return PROVIDER_ALIASES[provider];
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// src/providers/google/auth.ts
|
|
95
|
+
var toAuthData = (entry) => {
|
|
96
|
+
if (!entry) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
if (typeof entry === "string") {
|
|
100
|
+
return { token: entry };
|
|
101
|
+
}
|
|
102
|
+
if (typeof entry === "object") {
|
|
103
|
+
return entry;
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
};
|
|
107
|
+
var loadOpenCodeAuthEntry = async () => {
|
|
108
|
+
const auth = await loadOpenCodeAuth();
|
|
109
|
+
if (!auth) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
for (const alias of getProviderAliases("google")) {
|
|
113
|
+
const entry = toAuthData(auth[alias]);
|
|
114
|
+
if (entry) {
|
|
115
|
+
return entry;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
};
|
|
120
|
+
var toAuthContext = (entry) => {
|
|
121
|
+
if (!entry) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const accessToken = entry.access ?? entry.token;
|
|
125
|
+
const refreshToken = entry.refresh;
|
|
126
|
+
if (!accessToken && !refreshToken) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
accessToken,
|
|
131
|
+
refreshToken,
|
|
132
|
+
expires: entry.expires
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
var selectAccount = (accounts) => {
|
|
136
|
+
if (!accounts?.accounts?.length) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const candidateIndex = accounts.activeIndex ?? 0;
|
|
140
|
+
const account = accounts.accounts[candidateIndex] ?? accounts.accounts[0];
|
|
141
|
+
return account ?? null;
|
|
142
|
+
};
|
|
143
|
+
var loadAuthFromAccounts = async () => {
|
|
144
|
+
const configAccounts = await readJson(AUTH_PATHS.antigravityConfig());
|
|
145
|
+
const account = selectAccount(configAccounts);
|
|
146
|
+
if (account) {
|
|
147
|
+
return {
|
|
148
|
+
refreshToken: account.refreshToken,
|
|
149
|
+
projectId: account.projectId ?? account.managedProjectId,
|
|
150
|
+
email: account.email
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const dataAccounts = await readJson(AUTH_PATHS.antigravityData());
|
|
154
|
+
const fallbackAccount = selectAccount(dataAccounts);
|
|
155
|
+
if (!fallbackAccount) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
refreshToken: fallbackAccount.refreshToken,
|
|
160
|
+
projectId: fallbackAccount.projectId ?? fallbackAccount.managedProjectId,
|
|
161
|
+
email: fallbackAccount.email
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
var getGoogleAuth = async () => {
|
|
165
|
+
const openCodeAuth = await loadOpenCodeAuthEntry();
|
|
166
|
+
const authContext = toAuthContext(openCodeAuth);
|
|
167
|
+
if (authContext) {
|
|
168
|
+
return authContext;
|
|
169
|
+
}
|
|
170
|
+
return loadAuthFromAccounts();
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/providers/google/fetch.ts
|
|
174
|
+
var GOOGLE_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
|
175
|
+
var GOOGLE_CLIENT_SECRET = "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";
|
|
176
|
+
var DEFAULT_PROJECT_ID = "rising-fact-p41fc";
|
|
177
|
+
var WINDOW_SECONDS = 5 * 60 * 60;
|
|
178
|
+
var ENDPOINTS = [
|
|
179
|
+
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
|
180
|
+
"https://autopush-cloudcode-pa.sandbox.googleapis.com",
|
|
181
|
+
"https://cloudcode-pa.googleapis.com"
|
|
182
|
+
];
|
|
183
|
+
var HEADERS = {
|
|
184
|
+
"User-Agent": "antigravity/1.11.5 windows/amd64",
|
|
185
|
+
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
|
186
|
+
"Client-Metadata": '{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSPECIFIED","pluginType":"GEMINI"}'
|
|
187
|
+
};
|
|
188
|
+
var refreshAccessToken = async (refreshToken) => {
|
|
189
|
+
try {
|
|
190
|
+
const response = await fetch("https://oauth2.googleapis.com/token", {
|
|
191
|
+
method: "POST",
|
|
192
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
193
|
+
body: new URLSearchParams({
|
|
194
|
+
client_id: GOOGLE_CLIENT_ID,
|
|
195
|
+
client_secret: GOOGLE_CLIENT_SECRET,
|
|
196
|
+
refresh_token: refreshToken,
|
|
197
|
+
grant_type: "refresh_token"
|
|
198
|
+
})
|
|
199
|
+
});
|
|
200
|
+
if (!response.ok) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
return await response.json();
|
|
204
|
+
} catch {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
var fetchModels = async (accessToken, projectId) => {
|
|
209
|
+
const body = projectId ? { project: projectId } : {};
|
|
210
|
+
for (const endpoint of ENDPOINTS) {
|
|
211
|
+
try {
|
|
212
|
+
const response = await fetch(`${endpoint}/v1internal:fetchAvailableModels`, {
|
|
213
|
+
method: "POST",
|
|
214
|
+
headers: {
|
|
215
|
+
Authorization: `Bearer ${accessToken}`,
|
|
216
|
+
"Content-Type": "application/json",
|
|
217
|
+
...HEADERS
|
|
218
|
+
},
|
|
219
|
+
body: JSON.stringify(body),
|
|
220
|
+
signal: AbortSignal.timeout(15e3)
|
|
221
|
+
});
|
|
222
|
+
if (response.ok) {
|
|
223
|
+
return await response.json();
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
};
|
|
231
|
+
var toWindow = (remainingFraction, resetTime) => {
|
|
232
|
+
const remainingPercent = remainingFraction !== void 0 ? Math.round(remainingFraction * 100) : null;
|
|
233
|
+
const usedPercent = remainingPercent !== null ? Math.max(0, 100 - remainingPercent) : null;
|
|
234
|
+
const resetAt = resetTime ? new Date(resetTime).getTime() : null;
|
|
235
|
+
const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
|
|
236
|
+
return {
|
|
237
|
+
usedPercent,
|
|
238
|
+
remainingPercent,
|
|
239
|
+
windowSeconds: WINDOW_SECONDS,
|
|
240
|
+
resetAfterSeconds,
|
|
241
|
+
resetAt,
|
|
242
|
+
resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
|
|
243
|
+
resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
var buildUsage = (data) => {
|
|
247
|
+
const models = {};
|
|
248
|
+
for (const [modelName, modelData] of Object.entries(data.models ?? {})) {
|
|
249
|
+
const window = toWindow(modelData.quotaInfo?.remainingFraction, modelData.quotaInfo?.resetTime);
|
|
250
|
+
models[modelName] = {
|
|
251
|
+
windows: {
|
|
252
|
+
"5h": window
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
windows: {},
|
|
258
|
+
models: Object.keys(models).length ? models : void 0
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
var resolveAccessToken = async (refreshToken, accessToken, expires) => {
|
|
262
|
+
const now = Date.now();
|
|
263
|
+
if (accessToken && (!expires || expires > now)) {
|
|
264
|
+
return accessToken;
|
|
265
|
+
}
|
|
266
|
+
if (!refreshToken) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
const refreshed = await refreshAccessToken(refreshToken);
|
|
270
|
+
return refreshed?.access_token ?? null;
|
|
271
|
+
};
|
|
272
|
+
var fetchGoogleUsage = async () => {
|
|
273
|
+
const auth = await getGoogleAuth();
|
|
274
|
+
if (!auth) {
|
|
275
|
+
return {
|
|
276
|
+
provider: "google",
|
|
277
|
+
ok: false,
|
|
278
|
+
configured: false,
|
|
279
|
+
error: "Not configured - no accounts found",
|
|
280
|
+
usage: null
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
const accessToken = await resolveAccessToken(auth.refreshToken, auth.accessToken, auth.expires);
|
|
284
|
+
if (!accessToken) {
|
|
285
|
+
return {
|
|
286
|
+
provider: "google",
|
|
287
|
+
ok: false,
|
|
288
|
+
configured: true,
|
|
289
|
+
error: "Failed to refresh OAuth token",
|
|
290
|
+
usage: null
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const projectId = auth.projectId ?? DEFAULT_PROJECT_ID;
|
|
294
|
+
const modelsData = await fetchModels(accessToken, projectId);
|
|
295
|
+
if (!modelsData) {
|
|
296
|
+
return {
|
|
297
|
+
provider: "google",
|
|
298
|
+
ok: false,
|
|
299
|
+
configured: true,
|
|
300
|
+
error: "Failed to fetch models from API",
|
|
301
|
+
usage: null
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
provider: "google",
|
|
306
|
+
ok: true,
|
|
307
|
+
configured: true,
|
|
308
|
+
usage: buildUsage(modelsData)
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// src/providers/openai/auth.ts
|
|
313
|
+
var toAuthData2 = (entry) => {
|
|
314
|
+
if (!entry) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
if (typeof entry === "string") {
|
|
318
|
+
return { token: entry };
|
|
319
|
+
}
|
|
320
|
+
if (typeof entry === "object") {
|
|
321
|
+
return entry;
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
};
|
|
325
|
+
var hasAccessToken = (auth) => {
|
|
326
|
+
return Boolean(auth?.access || auth?.token);
|
|
327
|
+
};
|
|
328
|
+
var loadOpenCodeAuthEntry2 = async () => {
|
|
329
|
+
const auth = await loadOpenCodeAuth();
|
|
330
|
+
if (!auth) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
for (const alias of getProviderAliases("openai")) {
|
|
334
|
+
const entry = toAuthData2(auth[alias]);
|
|
335
|
+
if (entry && hasAccessToken(entry)) {
|
|
336
|
+
return entry;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
340
|
+
};
|
|
341
|
+
var getOpenaiAuth = async () => {
|
|
342
|
+
const openCodeAuth = await loadOpenCodeAuthEntry2();
|
|
343
|
+
if (openCodeAuth && hasAccessToken(openCodeAuth)) {
|
|
344
|
+
return openCodeAuth;
|
|
345
|
+
}
|
|
346
|
+
const pluginAuth = await readJson(AUTH_PATHS.openaiPlugin());
|
|
347
|
+
if (pluginAuth && hasAccessToken(pluginAuth)) {
|
|
348
|
+
return pluginAuth;
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// src/providers/openai/fetch.ts
|
|
354
|
+
var toWindow2 = (window) => {
|
|
355
|
+
if (!window) {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
const usedPercent = window.used_percent;
|
|
359
|
+
const resetAt = window.reset_at ? window.reset_at * 1e3 : null;
|
|
360
|
+
const resetAfterSeconds = window.reset_after_seconds ?? calculateResetAfterSeconds(resetAt);
|
|
361
|
+
return {
|
|
362
|
+
usedPercent,
|
|
363
|
+
remainingPercent: Math.max(0, 100 - usedPercent),
|
|
364
|
+
windowSeconds: window.limit_window_seconds ?? null,
|
|
365
|
+
resetAfterSeconds,
|
|
366
|
+
resetAt,
|
|
367
|
+
resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
|
|
368
|
+
resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
|
|
369
|
+
};
|
|
370
|
+
};
|
|
371
|
+
var fetchOpenaiUsage = async () => {
|
|
372
|
+
const auth = await getOpenaiAuth();
|
|
373
|
+
if (!auth) {
|
|
374
|
+
return {
|
|
375
|
+
provider: "openai",
|
|
376
|
+
ok: false,
|
|
377
|
+
configured: false,
|
|
378
|
+
error: "Not configured - no OAuth token found",
|
|
379
|
+
usage: null
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
const accessToken = auth.access ?? auth.token;
|
|
383
|
+
if (!accessToken) {
|
|
384
|
+
return {
|
|
385
|
+
provider: "openai",
|
|
386
|
+
ok: false,
|
|
387
|
+
configured: false,
|
|
388
|
+
error: "Not configured - access token missing",
|
|
389
|
+
usage: null
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
const response = await fetch("https://chatgpt.com/backend-api/wham/usage", {
|
|
394
|
+
method: "GET",
|
|
395
|
+
headers: {
|
|
396
|
+
Authorization: `Bearer ${accessToken}`,
|
|
397
|
+
"Content-Type": "application/json"
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
if (!response.ok) {
|
|
401
|
+
return {
|
|
402
|
+
provider: "openai",
|
|
403
|
+
ok: false,
|
|
404
|
+
configured: true,
|
|
405
|
+
error: `API error: ${response.status}`,
|
|
406
|
+
usage: null
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
const payload = await response.json();
|
|
410
|
+
const primary = toWindow2(payload.rate_limit.primary_window);
|
|
411
|
+
const secondary = toWindow2(payload.rate_limit.secondary_window);
|
|
412
|
+
const windows = {};
|
|
413
|
+
if (primary) {
|
|
414
|
+
windows["5h"] = primary;
|
|
415
|
+
}
|
|
416
|
+
if (secondary) {
|
|
417
|
+
windows["weekly"] = secondary;
|
|
418
|
+
}
|
|
419
|
+
const usage = {
|
|
420
|
+
windows
|
|
421
|
+
};
|
|
422
|
+
return {
|
|
423
|
+
provider: "openai",
|
|
424
|
+
ok: true,
|
|
425
|
+
configured: true,
|
|
426
|
+
usage
|
|
427
|
+
};
|
|
428
|
+
} catch (error) {
|
|
429
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
430
|
+
return {
|
|
431
|
+
provider: "openai",
|
|
432
|
+
ok: false,
|
|
433
|
+
configured: true,
|
|
434
|
+
error: `Request failed: ${message}`,
|
|
435
|
+
usage: null
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// src/providers/zai-coding-plan/auth.ts
|
|
441
|
+
var resolveAuthValue = (entry) => {
|
|
442
|
+
if (!entry) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
if (typeof entry === "string") {
|
|
446
|
+
return entry;
|
|
447
|
+
}
|
|
448
|
+
if (typeof entry === "object") {
|
|
449
|
+
return entry.api_key ?? entry.token ?? entry.key ?? null;
|
|
450
|
+
}
|
|
451
|
+
return null;
|
|
452
|
+
};
|
|
453
|
+
var getZaiApiKey = async () => {
|
|
454
|
+
if (process.env.ZAI_API_KEY) {
|
|
455
|
+
return process.env.ZAI_API_KEY;
|
|
456
|
+
}
|
|
457
|
+
const auth = await loadOpenCodeAuth();
|
|
458
|
+
if (!auth) {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
for (const alias of getProviderAliases("zai-coding-plan")) {
|
|
462
|
+
const value = resolveAuthValue(auth[alias]);
|
|
463
|
+
if (value) {
|
|
464
|
+
return value;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return null;
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// src/providers/zai-coding-plan/fetch.ts
|
|
471
|
+
var normalizeTimestamp = (value) => {
|
|
472
|
+
return value < 1e12 ? value * 1e3 : value;
|
|
473
|
+
};
|
|
474
|
+
var TOKEN_WINDOW_SECONDS = {
|
|
475
|
+
3: 3600
|
|
476
|
+
};
|
|
477
|
+
var resolveWindowSeconds = (limit) => {
|
|
478
|
+
if (!limit) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
if (!limit.number) {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
const unitSeconds = TOKEN_WINDOW_SECONDS[limit.unit];
|
|
485
|
+
if (!unitSeconds) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
return unitSeconds * limit.number;
|
|
489
|
+
};
|
|
490
|
+
var resolveWindowLabel = (windowSeconds) => {
|
|
491
|
+
if (!windowSeconds) {
|
|
492
|
+
return "tokens";
|
|
493
|
+
}
|
|
494
|
+
if (windowSeconds % 86400 === 0) {
|
|
495
|
+
const days = windowSeconds / 86400;
|
|
496
|
+
return days === 7 ? "weekly" : `${days}d`;
|
|
497
|
+
}
|
|
498
|
+
if (windowSeconds % 3600 === 0) {
|
|
499
|
+
return `${windowSeconds / 3600}h`;
|
|
500
|
+
}
|
|
501
|
+
return `${windowSeconds}s`;
|
|
502
|
+
};
|
|
503
|
+
var toWindow3 = (limit) => {
|
|
504
|
+
if (!limit) {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
const usedPercent = limit.percentage ?? null;
|
|
508
|
+
const remainingPercent = usedPercent !== null ? Math.max(0, 100 - usedPercent) : null;
|
|
509
|
+
const resetAt = limit.nextResetTime ? normalizeTimestamp(limit.nextResetTime) : null;
|
|
510
|
+
const resetAfterSeconds = calculateResetAfterSeconds(resetAt);
|
|
511
|
+
return {
|
|
512
|
+
usedPercent,
|
|
513
|
+
remainingPercent,
|
|
514
|
+
windowSeconds: resolveWindowSeconds(limit),
|
|
515
|
+
resetAfterSeconds,
|
|
516
|
+
resetAt,
|
|
517
|
+
resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,
|
|
518
|
+
resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null
|
|
519
|
+
};
|
|
520
|
+
};
|
|
521
|
+
var fetchZaiUsage = async () => {
|
|
522
|
+
const apiKey = await getZaiApiKey();
|
|
523
|
+
if (!apiKey) {
|
|
524
|
+
return {
|
|
525
|
+
provider: "zai-coding-plan",
|
|
526
|
+
ok: false,
|
|
527
|
+
configured: false,
|
|
528
|
+
error: "Not configured - no API key found",
|
|
529
|
+
usage: null
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
try {
|
|
533
|
+
const response = await fetch("https://api.z.ai/api/monitor/usage/quota/limit", {
|
|
534
|
+
method: "GET",
|
|
535
|
+
headers: {
|
|
536
|
+
Authorization: `Bearer ${apiKey}`,
|
|
537
|
+
"Content-Type": "application/json"
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
if (!response.ok) {
|
|
541
|
+
return {
|
|
542
|
+
provider: "zai-coding-plan",
|
|
543
|
+
ok: false,
|
|
544
|
+
configured: true,
|
|
545
|
+
error: `API error: ${response.status}`,
|
|
546
|
+
usage: null
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
const payload = await response.json();
|
|
550
|
+
const limits = payload.data?.limits ?? [];
|
|
551
|
+
const tokensLimit = limits.find((limit) => limit.type === "TOKENS_LIMIT");
|
|
552
|
+
const windows = {};
|
|
553
|
+
const window = toWindow3(tokensLimit);
|
|
554
|
+
if (window) {
|
|
555
|
+
const label = resolveWindowLabel(window.windowSeconds);
|
|
556
|
+
windows[label] = window;
|
|
557
|
+
}
|
|
558
|
+
const usage = {
|
|
559
|
+
windows
|
|
560
|
+
};
|
|
561
|
+
return {
|
|
562
|
+
provider: "zai-coding-plan",
|
|
563
|
+
ok: true,
|
|
564
|
+
configured: true,
|
|
565
|
+
usage
|
|
566
|
+
};
|
|
567
|
+
} catch (error) {
|
|
568
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
569
|
+
return {
|
|
570
|
+
provider: "zai-coding-plan",
|
|
571
|
+
ok: false,
|
|
572
|
+
configured: true,
|
|
573
|
+
error: `Request failed: ${message}`,
|
|
574
|
+
usage: null
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// src/types.ts
|
|
580
|
+
var PROVIDERS = ["openai", "google", "zai-coding-plan"];
|
|
581
|
+
|
|
582
|
+
// src/index.ts
|
|
583
|
+
var parseFrontmatter = (content) => {
|
|
584
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
585
|
+
if (!match) {
|
|
586
|
+
return { frontmatter: {}, body: content.trim() };
|
|
587
|
+
}
|
|
588
|
+
const [, yamlContent, body] = match;
|
|
589
|
+
const frontmatter = {};
|
|
590
|
+
for (const line of yamlContent.split("\n")) {
|
|
591
|
+
const colonIndex = line.indexOf(":");
|
|
592
|
+
if (colonIndex === -1) {
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
const key = line.slice(0, colonIndex).trim();
|
|
596
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
597
|
+
if (key === "description") {
|
|
598
|
+
frontmatter.description = value;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return { frontmatter, body: body.trim() };
|
|
602
|
+
};
|
|
603
|
+
var loadCommands = async () => {
|
|
604
|
+
const commands = [];
|
|
605
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
606
|
+
const __dirname = path.dirname(__filename);
|
|
607
|
+
const commandDir = path.join(__dirname, "command");
|
|
608
|
+
const walkDir = async (dir, baseDir = dir) => {
|
|
609
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
610
|
+
for (const entry of entries) {
|
|
611
|
+
const fullPath = path.join(dir, entry.name);
|
|
612
|
+
if (entry.isDirectory()) {
|
|
613
|
+
await walkDir(fullPath, baseDir);
|
|
614
|
+
} else if (entry.name.endsWith(".md")) {
|
|
615
|
+
const content = await readFile2(fullPath, "utf-8");
|
|
616
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
617
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
618
|
+
const name = relativePath.replace(/\.md$/, "").replace(/\//g, "-");
|
|
619
|
+
commands.push({ name, frontmatter, template: body });
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
await walkDir(commandDir);
|
|
624
|
+
return commands;
|
|
625
|
+
};
|
|
626
|
+
var fetchUsage = async (provider) => {
|
|
627
|
+
switch (provider) {
|
|
628
|
+
case "openai":
|
|
629
|
+
return fetchOpenaiUsage();
|
|
630
|
+
case "google":
|
|
631
|
+
return fetchGoogleUsage();
|
|
632
|
+
case "zai-coding-plan":
|
|
633
|
+
return fetchZaiUsage();
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
var UsagePlugin = async () => {
|
|
637
|
+
const commands = await loadCommands();
|
|
638
|
+
const usageTool = tool({
|
|
639
|
+
description: "Fetch subscription usage for OpenAI, Google, and z.ai providers.",
|
|
640
|
+
args: {
|
|
641
|
+
provider: tool.schema.string().optional().describe(
|
|
642
|
+
"Provider to check: openai, google, or zai-coding-plan. Aliases: codex, antigravity, zai."
|
|
643
|
+
)
|
|
644
|
+
},
|
|
645
|
+
async execute(args) {
|
|
646
|
+
const targetProvider = parseProvider(args.provider);
|
|
647
|
+
const providers = targetProvider ? [targetProvider] : PROVIDERS;
|
|
648
|
+
const results = await Promise.all(providers.map(fetchUsage));
|
|
649
|
+
return JSON.stringify(results, null, 2);
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
return {
|
|
653
|
+
tool: {
|
|
654
|
+
usage: usageTool
|
|
655
|
+
},
|
|
656
|
+
async config(config) {
|
|
657
|
+
config.command = config.command ?? {};
|
|
658
|
+
for (const cmd of commands) {
|
|
659
|
+
config.command[cmd.name] = {
|
|
660
|
+
template: cmd.template,
|
|
661
|
+
description: cmd.frontmatter.description
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
};
|
|
667
|
+
var index_default = UsagePlugin;
|
|
668
|
+
export {
|
|
669
|
+
UsagePlugin,
|
|
670
|
+
index_default as default
|
|
671
|
+
};
|
|
672
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/providers/common/time.ts", "../src/providers/common/files.ts", "../src/providers/common/registry.ts", "../src/providers/google/auth.ts", "../src/providers/google/fetch.ts", "../src/providers/openai/auth.ts", "../src/providers/openai/fetch.ts", "../src/providers/zai-coding-plan/auth.ts", "../src/providers/zai-coding-plan/fetch.ts", "../src/types.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Plugin } from '@opencode-ai/plugin';\nimport { tool } from '@opencode-ai/plugin';\nimport { fileURLToPath } from 'node:url';\nimport { readdir, readFile } from 'node:fs/promises';\nimport path from 'path';\n\nimport { fetchGoogleUsage } from './providers/google/fetch.ts';\nimport { fetchOpenaiUsage } from './providers/openai/fetch.ts';\nimport { fetchZaiUsage } from './providers/zai-coding-plan/fetch.ts';\nimport { parseProvider } from './providers/common/registry.ts';\nimport { PROVIDERS, type ProviderId, type ProviderResult } from './types.ts';\n\ninterface CommandFrontmatter {\n description?: string;\n}\n\ninterface ParsedCommand {\n name: string;\n frontmatter: CommandFrontmatter;\n template: string;\n}\n\ninterface UsageArgs {\n provider?: string;\n}\n\nconst parseFrontmatter = (content: string): { frontmatter: CommandFrontmatter; body: string } => {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/);\n if (!match) {\n return { frontmatter: {}, body: content.trim() };\n }\n\n const [, yamlContent, body] = match;\n const frontmatter: CommandFrontmatter = {};\n\n for (const line of yamlContent.split('\\n')) {\n const colonIndex = line.indexOf(':');\n if (colonIndex === -1) {\n continue;\n }\n\n const key = line.slice(0, colonIndex).trim();\n const value = line.slice(colonIndex + 1).trim();\n if (key === 'description') {\n frontmatter.description = value;\n }\n }\n\n return { frontmatter, body: body.trim() };\n};\n\nconst loadCommands = async (): Promise<ParsedCommand[]> => {\n const commands: ParsedCommand[] = [];\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const commandDir = path.join(__dirname, 'command');\n\n const walkDir = async (dir: string, baseDir: string = dir): Promise<void> => {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n await walkDir(fullPath, baseDir);\n } else if (entry.name.endsWith('.md')) {\n const content = await readFile(fullPath, 'utf-8');\n const { frontmatter, body } = parseFrontmatter(content);\n const relativePath = path.relative(baseDir, fullPath);\n const name = relativePath.replace(/\\.md$/, '').replace(/\\//g, '-');\n commands.push({ name, frontmatter, template: body });\n }\n }\n };\n\n await walkDir(commandDir);\n return commands;\n};\n\nconst fetchUsage = async (provider: ProviderId): Promise<ProviderResult> => {\n switch (provider) {\n case 'openai':\n return fetchOpenaiUsage();\n case 'google':\n return fetchGoogleUsage();\n case 'zai-coding-plan':\n return fetchZaiUsage();\n }\n};\n\nexport const UsagePlugin: Plugin = async () => {\n const commands = await loadCommands();\n\n const usageTool = tool({\n description: 'Fetch subscription usage for OpenAI, Google, and z.ai providers.',\n args: {\n provider: tool.schema\n .string()\n .optional()\n .describe(\n 'Provider to check: openai, google, or zai-coding-plan. Aliases: codex, antigravity, zai.'\n ),\n },\n async execute(args: UsageArgs) {\n const targetProvider = parseProvider(args.provider);\n const providers: ProviderId[] = targetProvider ? [targetProvider] : PROVIDERS;\n const results = await Promise.all(providers.map(fetchUsage));\n\n return JSON.stringify(results, null, 2);\n },\n });\n\n return {\n tool: {\n usage: usageTool,\n },\n async config(config) {\n config.command = config.command ?? {};\n\n for (const cmd of commands) {\n config.command[cmd.name] = {\n template: cmd.template,\n description: cmd.frontmatter.description,\n };\n }\n },\n };\n};\n\nexport default UsagePlugin;\n", "export const calculateResetAfterSeconds = (\n resetAt: number | null,\n now: number = Date.now()\n): number | null => {\n if (!resetAt) {\n return null;\n }\n\n const diffMs = resetAt - now;\n if (diffMs <= 0) {\n return 0;\n }\n\n return Math.floor(diffMs / 1000);\n};\n\nexport const calculateResetAt = (\n resetAfterSeconds: number | null,\n now: number = Date.now()\n): number | null => {\n if (resetAfterSeconds === null || resetAfterSeconds === undefined) {\n return null;\n }\n\n return now + resetAfterSeconds * 1000;\n};\n\nexport const formatDuration = (seconds: number): string => {\n if (seconds <= 0) {\n return '0s';\n }\n\n const weeks = Math.floor(seconds / 604800);\n const days = Math.floor((seconds % 604800) / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n const secs = seconds % 60;\n\n const parts: string[] = [];\n if (weeks > 0) parts.push(`${weeks}w`);\n if (days > 0) parts.push(`${days}d`);\n if (hours > 0) parts.push(`${hours}h`);\n if (minutes > 0) parts.push(`${minutes}m`);\n if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);\n\n return parts.join(' ');\n};\n\nexport const formatResetAt = (resetAtMs: number): string => {\n return new Date(resetAtMs).toLocaleString(undefined, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: 'numeric',\n minute: 'numeric',\n second: 'numeric',\n timeZoneName: 'short',\n });\n};\n", "import { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport type { OpenCodeAuth } from '../../types.ts';\n\nexport const xdgDataHome = (): string =>\n process.env.XDG_DATA_HOME ?? join(homedir(), '.local', 'share');\n\nexport const xdgConfigHome = (): string =>\n process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config');\n\nexport const AUTH_PATHS = {\n opencode: (): string => join(xdgDataHome(), 'opencode', 'auth.json'),\n openaiPlugin: (): string => join(homedir(), '.opencode', 'auth', 'openai.json'),\n antigravityConfig: (): string => join(xdgConfigHome(), 'opencode', 'antigravity-accounts.json'),\n antigravityData: (): string => join(xdgDataHome(), 'opencode', 'antigravity-accounts.json'),\n} as const;\n\nexport const readJson = async <T>(filePath: string): Promise<T | null> => {\n try {\n const content = await readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n};\n\nexport const loadOpenCodeAuth = async (): Promise<OpenCodeAuth | null> => {\n return readJson<OpenCodeAuth>(AUTH_PATHS.opencode());\n};\n", "import type { ProviderAlias, ProviderId } from '../../types.ts';\n\nexport const PROVIDER_ALIASES: Record<ProviderId, ProviderAlias[]> = {\n openai: ['openai', 'codex', 'chatgpt'],\n google: ['google', 'antigravity'],\n 'zai-coding-plan': ['zai-coding-plan', 'zai', 'z.ai'],\n};\n\nexport const parseProvider = (input?: string): ProviderId | null => {\n if (!input) {\n return null;\n }\n\n const normalized = input.trim().toLowerCase();\n for (const [providerId, aliases] of Object.entries(PROVIDER_ALIASES)) {\n if (aliases.includes(normalized as ProviderAlias)) {\n return providerId as ProviderId;\n }\n }\n\n return null;\n};\n\nexport const getProviderAliases = (provider: ProviderId): ProviderAlias[] => {\n return PROVIDER_ALIASES[provider];\n};\n", "import type {\n AntigravityAccount,\n AntigravityAccountsFile,\n OpenCodeAuth,\n ProviderAuthData,\n} from '../../types.ts';\nimport { AUTH_PATHS, loadOpenCodeAuth, readJson } from '../common/files.ts';\nimport { getProviderAliases } from '../common/registry.ts';\n\nexport interface GoogleAuthContext {\n refreshToken?: string;\n accessToken?: string;\n expires?: number;\n projectId?: string;\n email?: string;\n}\n\nconst toAuthData = (entry: OpenCodeAuth[string]): ProviderAuthData | null => {\n if (!entry) {\n return null;\n }\n\n if (typeof entry === 'string') {\n return { token: entry };\n }\n\n if (typeof entry === 'object') {\n return entry as ProviderAuthData;\n }\n\n return null;\n};\n\nconst loadOpenCodeAuthEntry = async (): Promise<ProviderAuthData | null> => {\n const auth = await loadOpenCodeAuth();\n if (!auth) {\n return null;\n }\n\n for (const alias of getProviderAliases('google')) {\n const entry = toAuthData(auth[alias]);\n if (entry) {\n return entry;\n }\n }\n\n return null;\n};\n\nconst toAuthContext = (entry: ProviderAuthData | null): GoogleAuthContext | null => {\n if (!entry) {\n return null;\n }\n\n const accessToken = entry.access ?? entry.token;\n const refreshToken = entry.refresh;\n\n if (!accessToken && !refreshToken) {\n return null;\n }\n\n return {\n accessToken,\n refreshToken,\n expires: entry.expires,\n };\n};\n\nconst selectAccount = (accounts: AntigravityAccountsFile | null): AntigravityAccount | null => {\n if (!accounts?.accounts?.length) {\n return null;\n }\n\n const candidateIndex = accounts.activeIndex ?? 0;\n const account = accounts.accounts[candidateIndex] ?? accounts.accounts[0];\n return account ?? null;\n};\n\nconst loadAuthFromAccounts = async (): Promise<GoogleAuthContext | null> => {\n const configAccounts = await readJson<AntigravityAccountsFile>(AUTH_PATHS.antigravityConfig());\n const account = selectAccount(configAccounts);\n if (account) {\n return {\n refreshToken: account.refreshToken,\n projectId: account.projectId ?? account.managedProjectId,\n email: account.email,\n };\n }\n\n const dataAccounts = await readJson<AntigravityAccountsFile>(AUTH_PATHS.antigravityData());\n const fallbackAccount = selectAccount(dataAccounts);\n if (!fallbackAccount) {\n return null;\n }\n\n return {\n refreshToken: fallbackAccount.refreshToken,\n projectId: fallbackAccount.projectId ?? fallbackAccount.managedProjectId,\n email: fallbackAccount.email,\n };\n};\n\nexport const getGoogleAuth = async (): Promise<GoogleAuthContext | null> => {\n const openCodeAuth = await loadOpenCodeAuthEntry();\n const authContext = toAuthContext(openCodeAuth);\n if (authContext) {\n return authContext;\n }\n\n return loadAuthFromAccounts();\n};\n", "import type { ProviderResult, ProviderUsage, UsageWindow } from '../../types.ts';\nimport { calculateResetAfterSeconds, formatDuration, formatResetAt } from '../common/time.ts';\nimport { getGoogleAuth } from './auth.ts';\n\nconst GOOGLE_CLIENT_ID =\n '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com';\nconst GOOGLE_CLIENT_SECRET = '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';\nconst DEFAULT_PROJECT_ID = 'rising-fact-p41fc';\nconst WINDOW_SECONDS = 5 * 60 * 60;\n\nconst ENDPOINTS: readonly string[] = [\n 'https://daily-cloudcode-pa.sandbox.googleapis.com',\n 'https://autopush-cloudcode-pa.sandbox.googleapis.com',\n 'https://cloudcode-pa.googleapis.com',\n];\n\nconst HEADERS = {\n 'User-Agent': 'antigravity/1.11.5 windows/amd64',\n 'X-Goog-Api-Client': 'google-cloud-sdk vscode_cloudshelleditor/0.1',\n 'Client-Metadata':\n '{\"ideType\":\"IDE_UNSPECIFIED\",\"platform\":\"PLATFORM_UNSPECIFIED\",\"pluginType\":\"GEMINI\"}',\n} as const;\n\ninterface TokenResponse {\n access_token: string;\n expires_in?: number;\n}\n\ninterface ModelUsageInfoResponse {\n displayName?: string;\n quotaInfo?: {\n remainingFraction?: number;\n resetTime?: string;\n };\n}\n\ninterface ModelsResponse {\n models?: Record<string, ModelUsageInfoResponse>;\n}\n\nconst refreshAccessToken = async (refreshToken: string): Promise<TokenResponse | null> => {\n try {\n const response = await fetch('https://oauth2.googleapis.com/token', {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n refresh_token: refreshToken,\n grant_type: 'refresh_token',\n }),\n });\n\n if (!response.ok) {\n return null;\n }\n\n return (await response.json()) as TokenResponse;\n } catch {\n return null;\n }\n};\n\nconst fetchModels = async (\n accessToken: string,\n projectId?: string\n): Promise<ModelsResponse | null> => {\n const body = projectId ? { project: projectId } : {};\n\n for (const endpoint of ENDPOINTS) {\n try {\n const response = await fetch(`${endpoint}/v1internal:fetchAvailableModels`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n ...HEADERS,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(15000),\n });\n\n if (response.ok) {\n return (await response.json()) as ModelsResponse;\n }\n } catch {\n continue;\n }\n }\n\n return null;\n};\n\nconst toWindow = (remainingFraction?: number, resetTime?: string): UsageWindow => {\n const remainingPercent =\n remainingFraction !== undefined ? Math.round(remainingFraction * 100) : null;\n const usedPercent = remainingPercent !== null ? Math.max(0, 100 - remainingPercent) : null;\n const resetAt = resetTime ? new Date(resetTime).getTime() : null;\n const resetAfterSeconds = calculateResetAfterSeconds(resetAt);\n\n return {\n usedPercent,\n remainingPercent,\n windowSeconds: WINDOW_SECONDS,\n resetAfterSeconds,\n resetAt,\n resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,\n resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null,\n };\n};\n\nconst buildUsage = (data: ModelsResponse): ProviderUsage => {\n const models: Record<string, { windows: Record<string, UsageWindow> }> = {};\n\n for (const [modelName, modelData] of Object.entries(data.models ?? {})) {\n const window = toWindow(modelData.quotaInfo?.remainingFraction, modelData.quotaInfo?.resetTime);\n models[modelName] = {\n windows: {\n '5h': window,\n },\n };\n }\n\n return {\n windows: {},\n models: Object.keys(models).length ? models : undefined,\n };\n};\n\nconst resolveAccessToken = async (\n refreshToken?: string,\n accessToken?: string,\n expires?: number\n): Promise<string | null> => {\n const now = Date.now();\n\n if (accessToken && (!expires || expires > now)) {\n return accessToken;\n }\n\n if (!refreshToken) {\n return null;\n }\n\n const refreshed = await refreshAccessToken(refreshToken);\n return refreshed?.access_token ?? null;\n};\n\nexport const fetchGoogleUsage = async (): Promise<ProviderResult> => {\n const auth = await getGoogleAuth();\n\n if (!auth) {\n return {\n provider: 'google',\n ok: false,\n configured: false,\n error: 'Not configured - no accounts found',\n usage: null,\n };\n }\n\n const accessToken = await resolveAccessToken(auth.refreshToken, auth.accessToken, auth.expires);\n\n if (!accessToken) {\n return {\n provider: 'google',\n ok: false,\n configured: true,\n error: 'Failed to refresh OAuth token',\n usage: null,\n };\n }\n\n const projectId = auth.projectId ?? DEFAULT_PROJECT_ID;\n const modelsData = await fetchModels(accessToken, projectId);\n\n if (!modelsData) {\n return {\n provider: 'google',\n ok: false,\n configured: true,\n error: 'Failed to fetch models from API',\n usage: null,\n };\n }\n\n return {\n provider: 'google',\n ok: true,\n configured: true,\n usage: buildUsage(modelsData),\n };\n};\n", "import type { OpenCodeAuth, ProviderAuthData } from '../../types.ts';\nimport { AUTH_PATHS, loadOpenCodeAuth, readJson } from '../common/files.ts';\nimport { getProviderAliases } from '../common/registry.ts';\n\nconst toAuthData = (entry: OpenCodeAuth[string]): ProviderAuthData | null => {\n if (!entry) {\n return null;\n }\n\n if (typeof entry === 'string') {\n return { token: entry };\n }\n\n if (typeof entry === 'object') {\n return entry as ProviderAuthData;\n }\n\n return null;\n};\n\nconst hasAccessToken = (auth: ProviderAuthData | null): auth is ProviderAuthData => {\n return Boolean(auth?.access || auth?.token);\n};\n\nconst loadOpenCodeAuthEntry = async (): Promise<ProviderAuthData | null> => {\n const auth = await loadOpenCodeAuth();\n if (!auth) {\n return null;\n }\n\n for (const alias of getProviderAliases('openai')) {\n const entry = toAuthData(auth[alias]);\n if (entry && hasAccessToken(entry)) {\n return entry;\n }\n }\n\n return null;\n};\n\nexport const getOpenaiAuth = async (): Promise<ProviderAuthData | null> => {\n const openCodeAuth = await loadOpenCodeAuthEntry();\n if (openCodeAuth && hasAccessToken(openCodeAuth)) {\n return openCodeAuth;\n }\n\n const pluginAuth = await readJson<ProviderAuthData>(AUTH_PATHS.openaiPlugin());\n if (pluginAuth && hasAccessToken(pluginAuth)) {\n return pluginAuth;\n }\n\n return null;\n};\n", "import type { ProviderResult, ProviderUsage, UsageWindow } from '../../types.ts';\nimport { calculateResetAfterSeconds, formatDuration, formatResetAt } from '../common/time.ts';\nimport { getOpenaiAuth } from './auth.ts';\n\ninterface OpenaiBackendWindow {\n used_percent: number;\n limit_window_seconds: number;\n reset_after_seconds: number;\n reset_at: number;\n}\n\ninterface OpenaiBackendResponse {\n plan_type: string;\n rate_limit: {\n allowed: boolean;\n limit_reached: boolean;\n primary_window?: OpenaiBackendWindow;\n secondary_window?: OpenaiBackendWindow;\n };\n}\n\nconst toWindow = (window?: OpenaiBackendWindow): UsageWindow | null => {\n if (!window) {\n return null;\n }\n\n const usedPercent = window.used_percent;\n const resetAt = window.reset_at ? window.reset_at * 1000 : null;\n const resetAfterSeconds = window.reset_after_seconds ?? calculateResetAfterSeconds(resetAt);\n\n return {\n usedPercent,\n remainingPercent: Math.max(0, 100 - usedPercent),\n windowSeconds: window.limit_window_seconds ?? null,\n resetAfterSeconds,\n resetAt,\n resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,\n resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null,\n };\n};\n\nexport const fetchOpenaiUsage = async (): Promise<ProviderResult> => {\n const auth = await getOpenaiAuth();\n\n if (!auth) {\n return {\n provider: 'openai',\n ok: false,\n configured: false,\n error: 'Not configured - no OAuth token found',\n usage: null,\n };\n }\n\n const accessToken = auth.access ?? auth.token;\n if (!accessToken) {\n return {\n provider: 'openai',\n ok: false,\n configured: false,\n error: 'Not configured - access token missing',\n usage: null,\n };\n }\n\n try {\n const response = await fetch('https://chatgpt.com/backend-api/wham/usage', {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n return {\n provider: 'openai',\n ok: false,\n configured: true,\n error: `API error: ${response.status}`,\n usage: null,\n };\n }\n\n const payload = (await response.json()) as OpenaiBackendResponse;\n const primary = toWindow(payload.rate_limit.primary_window);\n const secondary = toWindow(payload.rate_limit.secondary_window);\n\n const windows: Record<string, UsageWindow> = {};\n if (primary) {\n windows['5h'] = primary;\n }\n if (secondary) {\n windows['weekly'] = secondary;\n }\n\n const usage: ProviderUsage = {\n windows,\n };\n\n return {\n provider: 'openai',\n ok: true,\n configured: true,\n usage,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n provider: 'openai',\n ok: false,\n configured: true,\n error: `Request failed: ${message}`,\n usage: null,\n };\n }\n};\n", "import type { OpenCodeAuth } from '../../types.ts';\nimport { loadOpenCodeAuth } from '../common/files.ts';\nimport { getProviderAliases } from '../common/registry.ts';\n\nconst resolveAuthValue = (entry: OpenCodeAuth[string]): string | null => {\n if (!entry) {\n return null;\n }\n\n if (typeof entry === 'string') {\n return entry;\n }\n\n if (typeof entry === 'object') {\n return entry.api_key ?? entry.token ?? entry.key ?? null;\n }\n\n return null;\n};\n\nexport const getZaiApiKey = async (): Promise<string | null> => {\n if (process.env.ZAI_API_KEY) {\n return process.env.ZAI_API_KEY;\n }\n\n const auth = await loadOpenCodeAuth();\n if (!auth) {\n return null;\n }\n\n for (const alias of getProviderAliases('zai-coding-plan')) {\n const value = resolveAuthValue(auth[alias]);\n if (value) {\n return value;\n }\n }\n\n return null;\n};\n", "import type { ProviderResult, ProviderUsage, UsageWindow } from '../../types.ts';\nimport { calculateResetAfterSeconds, formatDuration, formatResetAt } from '../common/time.ts';\nimport { getZaiApiKey } from './auth.ts';\n\ninterface ZaiLimit {\n type: 'TIME_LIMIT' | 'TOKENS_LIMIT';\n unit: number;\n number: number;\n usage: number;\n currentValue: number;\n remaining: number;\n percentage: number;\n nextResetTime?: number;\n}\n\ninterface ZaiUsageResponse {\n code: number;\n msg: string;\n data?: {\n limits?: ZaiLimit[];\n };\n success: boolean;\n}\n\nconst normalizeTimestamp = (value: number): number => {\n return value < 1_000_000_000_000 ? value * 1000 : value;\n};\n\nconst TOKEN_WINDOW_SECONDS: Record<number, number> = {\n 3: 3600,\n};\n\nconst resolveWindowSeconds = (limit?: ZaiLimit): number | null => {\n if (!limit) {\n return null;\n }\n\n if (!limit.number) {\n return null;\n }\n\n const unitSeconds = TOKEN_WINDOW_SECONDS[limit.unit];\n if (!unitSeconds) {\n return null;\n }\n\n return unitSeconds * limit.number;\n};\n\nconst resolveWindowLabel = (windowSeconds: number | null): string => {\n if (!windowSeconds) {\n return 'tokens';\n }\n\n if (windowSeconds % 86400 === 0) {\n const days = windowSeconds / 86400;\n return days === 7 ? 'weekly' : `${days}d`;\n }\n\n if (windowSeconds % 3600 === 0) {\n return `${windowSeconds / 3600}h`;\n }\n\n return `${windowSeconds}s`;\n};\n\nconst toWindow = (limit?: ZaiLimit): UsageWindow | null => {\n if (!limit) {\n return null;\n }\n\n const usedPercent = limit.percentage ?? null;\n const remainingPercent = usedPercent !== null ? Math.max(0, 100 - usedPercent) : null;\n const resetAt = limit.nextResetTime ? normalizeTimestamp(limit.nextResetTime) : null;\n const resetAfterSeconds = calculateResetAfterSeconds(resetAt);\n\n return {\n usedPercent,\n remainingPercent,\n windowSeconds: resolveWindowSeconds(limit),\n resetAfterSeconds,\n resetAt,\n resetAtFormatted: resetAt ? formatResetAt(resetAt) : null,\n resetAfterFormatted: resetAfterSeconds !== null ? formatDuration(resetAfterSeconds) : null,\n };\n};\n\nexport const fetchZaiUsage = async (): Promise<ProviderResult> => {\n const apiKey = await getZaiApiKey();\n\n if (!apiKey) {\n return {\n provider: 'zai-coding-plan',\n ok: false,\n configured: false,\n error: 'Not configured - no API key found',\n usage: null,\n };\n }\n\n try {\n const response = await fetch('https://api.z.ai/api/monitor/usage/quota/limit', {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n return {\n provider: 'zai-coding-plan',\n ok: false,\n configured: true,\n error: `API error: ${response.status}`,\n usage: null,\n };\n }\n\n const payload = (await response.json()) as ZaiUsageResponse;\n const limits = payload.data?.limits ?? [];\n const tokensLimit = limits.find((limit) => limit.type === 'TOKENS_LIMIT');\n\n const windows: Record<string, UsageWindow> = {};\n const window = toWindow(tokensLimit);\n if (window) {\n const label = resolveWindowLabel(window.windowSeconds);\n windows[label] = window;\n }\n\n const usage: ProviderUsage = {\n windows,\n };\n\n return {\n provider: 'zai-coding-plan',\n ok: true,\n configured: true,\n usage,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n provider: 'zai-coding-plan',\n ok: false,\n configured: true,\n error: `Request failed: ${message}`,\n usage: null,\n };\n }\n};\n", "export type ProviderId = 'openai' | 'google' | 'zai-coding-plan';\n\nexport type ProviderAlias = ProviderId | 'codex' | 'antigravity' | 'zai' | 'z.ai' | 'chatgpt';\n\nexport const PROVIDERS: ProviderId[] = ['openai', 'google', 'zai-coding-plan'];\n\nexport interface UsageWindow {\n usedPercent: number | null;\n remainingPercent: number | null;\n windowSeconds: number | null;\n resetAfterSeconds: number | null;\n resetAt: number | null;\n resetAtFormatted: string | null;\n resetAfterFormatted: string | null;\n}\n\nexport interface UsageWindows {\n windows: Record<string, UsageWindow>;\n}\n\nexport interface ProviderUsage extends UsageWindows {\n models?: Record<string, UsageWindows>;\n}\n\nexport interface ProviderResult {\n provider: ProviderId;\n ok: boolean;\n configured: boolean;\n error?: string;\n usage: ProviderUsage | null;\n}\n\nexport type OpenCodeAuth = Record<string, string | ProviderAuthData>;\n\nexport interface ProviderAuthData {\n type?: 'oauth' | 'api' | string;\n access?: string;\n refresh?: string;\n expires?: number;\n api_key?: string;\n token?: string;\n key?: string;\n accountId?: string;\n}\n\nexport interface AntigravityAccount {\n email: string;\n refreshToken: string;\n projectId?: string;\n addedAt: number | string;\n lastUsed?: number;\n rateLimitResetTimes?: Record<string, number>;\n managedProjectId?: string;\n}\n\nexport interface AntigravityAccountsFile {\n version?: number;\n accounts: AntigravityAccount[];\n activeIndex?: number;\n activeIndexByFamily?: Record<string, number>;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["readFile", "toAuthData", "loadOpenCodeAuthEntry", "toWindow", "toWindow", "readFile"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-usage-plugin",
|
|
3
|
+
"version": "0.0.1-dev",
|
|
4
|
+
"description": "OpenCode plugin to display subscription usages for OpenAI, Google, and z.ai",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Nelson Pires",
|
|
7
|
+
"email": "nelsonpires.sn@gmail.com"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/nelsonPires5/opencode-usage-plugin.git"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"opencode",
|
|
23
|
+
"plugin",
|
|
24
|
+
"usage",
|
|
25
|
+
"chatgpt",
|
|
26
|
+
"codex",
|
|
27
|
+
"antigravity",
|
|
28
|
+
"zai",
|
|
29
|
+
"usage",
|
|
30
|
+
"monitoring"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist/index.js",
|
|
37
|
+
"dist/index.js.map",
|
|
38
|
+
"dist/index.d.ts",
|
|
39
|
+
"dist/command"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "npx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@opencode-ai/plugin --external:path --external:@types/node --sourcemap && npx tsc --emitDeclarationOnly && cp -r src/command dist/",
|
|
43
|
+
"prepublishOnly": "npm run build",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"test:watch": "vitest"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@opencode-ai/plugin": "1.0.85"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@eslint/js": "^9.39.1",
|
|
52
|
+
"@types/node": "^20.11.5",
|
|
53
|
+
"@typescript-eslint/eslint-plugin": "8.47.0",
|
|
54
|
+
"@typescript-eslint/parser": "8.47.0",
|
|
55
|
+
"eslint": "^9.39.1",
|
|
56
|
+
"eslint-config-prettier": "10.1.8",
|
|
57
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
58
|
+
"prettier": "^3.2.4",
|
|
59
|
+
"typescript-eslint": "^8.47.0",
|
|
60
|
+
"vitest": "^3.2.4"
|
|
61
|
+
}
|
|
62
|
+
}
|