@howaboua/opencode-usage-plugin 0.0.9 → 0.1.1

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 CHANGED
@@ -6,6 +6,7 @@ Track AI provider rate limits and quotas in real-time.
6
6
 
7
7
  - **Live rate limits** – See Codex/OpenAI hourly/weekly limits at a glance
8
8
  - **Proxy quota stats** – Monitor Mirrowel Proxy credentials and tier usage
9
+ - **Copilot usage** – Track GitHub Copilot chat + completions quotas
9
10
  - **Inline status** – Results appear directly in your chat, no context switching
10
11
  - **Zero setup** – Auto-detects providers from your existing config
11
12
 
@@ -18,7 +19,7 @@ Add to your `opencode.json`:
18
19
  ```json
19
20
  {
20
21
  "$schema": "https://opencode.ai/config.json",
21
- "plugins": ["@howaboua/opencode-usage-plugin"]
22
+ "plugin": ["@howaboua/opencode-usage-plugin"]
22
23
  }
23
24
  ```
24
25
 
@@ -37,6 +38,7 @@ OpenCode installs dependencies automatically on next launch.
37
38
  ```
38
39
  /usage codex
39
40
  /usage proxy
41
+ /usage copilot
40
42
  ```
41
43
 
42
44
  ### Support the proxy
@@ -51,6 +53,7 @@ OpenCode installs dependencies automatically on next launch.
51
53
  |----------|--------|
52
54
  | **Codex / OpenAI** | Auth tokens + `/wham/usage` endpoint |
53
55
  | **Mirrowel Proxy** | Local `/v1/quota-stats` endpoint |
56
+ | **GitHub Copilot** | GitHub internal usage APIs |
54
57
 
55
58
  ## Configuration
56
59
 
@@ -70,21 +73,20 @@ Optional config at `~/.config/opencode/usage-config.jsonc`:
70
73
  // Show/hide providers in /usage output
71
74
  "providers": {
72
75
  "openai": true,
73
- "proxy": true
76
+ "proxy": true,
77
+ "copilot": true
74
78
  }
75
79
  }
76
80
  ```
77
81
 
78
82
  If missing, the plugin creates a default template on first run.
79
83
 
80
- ## Development
84
+ ### Copilot auth
81
85
 
82
- ```bash
83
- # Check DB contents
84
- bun run debug-db.ts
86
+ Copilot is detected from either of these locations:
85
87
 
86
- # Verify path resolution
87
- bun run debug-path.ts
88
- ```
88
+ - `~/.local/share/opencode/copilot-usage-token.json`
89
+ - `~/.local/share/opencode/auth.json` with a `github-copilot` entry
90
+ - `~/.config/opencode/copilot-quota-token.json` (optional override)
89
91
 
90
92
  See `AGENTS.md` for internal architecture.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAWjD,eAAO,MAAM,WAAW,EAAE,MAoDzB,CAAA;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAMjD,eAAO,MAAM,WAAW,EAAE,MAoCzB,CAAA;AAED,eAAe,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -4,29 +4,14 @@
4
4
  */
5
5
  import { commandHooks, sessionHooks, proxyHooks } from "./hooks";
6
6
  import { createUsageState } from "./state";
7
- import { loadAuths } from "./usage/fetch";
8
7
  import { loadUsageConfig } from "./usage/config";
9
- import { existsSync } from "fs";
10
- import { getQuotaConfigPath, getUsageTokenPath } from "./providers/copilot/auth";
11
8
  export const UsagePlugin = async ({ client }) => {
12
9
  const state = createUsageState();
13
10
  try {
14
- const [auths, usageConfig] = await Promise.all([
15
- loadAuths().catch(() => ({})),
16
- loadUsageConfig().catch(() => ({})),
17
- ]);
18
- state.availableProviders.codex =
19
- usageConfig?.providers?.openai !== undefined
20
- ? usageConfig.providers.openai
21
- : Boolean(("codex" in auths && auths["codex"]) || ("openai" in auths && auths["openai"]));
22
- state.availableProviders.proxy =
23
- usageConfig?.providers?.proxy !== undefined ? usageConfig.providers.proxy : Boolean(usageConfig?.endpoint);
24
- const authRecord = auths;
25
- const hasCopilotAuth = Boolean(authRecord["github-copilot"] ||
26
- authRecord["copilot"] ||
27
- existsSync(getQuotaConfigPath()) ||
28
- existsSync(getUsageTokenPath()));
29
- state.availableProviders.copilot = usageConfig?.providers?.copilot === false ? false : hasCopilotAuth;
11
+ const usageConfig = await loadUsageConfig().catch(() => ({}));
12
+ state.availableProviders.codex = usageConfig?.providers?.openai !== false;
13
+ state.availableProviders.proxy = usageConfig?.providers?.proxy !== false;
14
+ state.availableProviders.copilot = usageConfig?.providers?.copilot !== false;
30
15
  }
31
16
  catch (err) { }
32
17
  async function sendStatusMessage(sessionID, text) {
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/usage/fetch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAG7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAe5C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAWlD,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED,wBAAsB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAoCnF;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC,CASrD"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/usage/fetch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAI7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAe5C,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAWlD,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED,wBAAsB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA6CnF;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC,CASrD"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import z from "zod";
6
6
  import { providers } from "../providers";
7
+ import { loadUsageConfig } from "./config";
7
8
  import { getAuthFilePath } from "../utils";
8
9
  import { resolveProviderAuths } from "./registry";
9
10
  const authEntrySchema = z
@@ -36,12 +37,24 @@ export function resolveProviderFilter(filter) {
36
37
  }
37
38
  export async function fetchUsageSnapshots(filter) {
38
39
  const targetProvider = resolveProviderFilter(filter);
40
+ const usageConfig = await loadUsageConfig().catch(() => null);
41
+ const providerToggles = usageConfig?.providers ?? {};
42
+ const isProviderEnabled = (id) => {
43
+ if (id === "codex")
44
+ return providerToggles.openai !== false;
45
+ if (id === "proxy")
46
+ return providerToggles.proxy !== false;
47
+ if (id === "copilot")
48
+ return providerToggles.copilot !== false;
49
+ return true;
50
+ };
39
51
  const auths = await loadAuths();
40
52
  const entries = resolveProviderAuths(auths, null);
41
53
  const snapshots = [];
42
54
  // Fetch from auth-based providers
43
55
  const fetches = entries
44
56
  .filter((entry) => !targetProvider || entry.providerID === targetProvider)
57
+ .filter((entry) => isProviderEnabled(entry.providerID))
45
58
  .map(async (entry) => {
46
59
  const provider = providers[entry.providerID];
47
60
  if (!provider?.fetchUsage)
@@ -53,7 +66,7 @@ export async function fetchUsageSnapshots(filter) {
53
66
  // Always include special providers (no auth entries needed) if no filter or filter matches
54
67
  const specialProviders = ["proxy", "copilot"];
55
68
  for (const id of specialProviders) {
56
- if (!targetProvider || targetProvider === id) {
69
+ if ((!targetProvider || targetProvider === id) && isProviderEnabled(id)) {
57
70
  const provider = providers[id];
58
71
  if (provider?.fetchUsage) {
59
72
  fetches.push(provider
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howaboua/opencode-usage-plugin",
3
- "version": "0.0.9",
3
+ "version": "0.1.1",
4
4
  "description": "opencode plugin for tracking AI provider usage, rate limits, and quotas",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",