@howaboua/opencode-usage-plugin 0.1.4 → 0.1.6

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 (41) hide show
  1. package/README.md +83 -30
  2. package/dist/providers/index.d.ts +1 -0
  3. package/dist/providers/index.d.ts.map +1 -1
  4. package/dist/providers/index.js +3 -0
  5. package/dist/providers/proxy/fetch.js +2 -2
  6. package/dist/providers/proxy/format.d.ts +2 -1
  7. package/dist/providers/proxy/format.d.ts.map +1 -1
  8. package/dist/providers/proxy/format.js +11 -21
  9. package/dist/providers/proxy/index.d.ts +6 -0
  10. package/dist/providers/proxy/index.d.ts.map +1 -1
  11. package/dist/providers/proxy/index.js +90 -28
  12. package/dist/providers/proxy/types.d.ts +1 -0
  13. package/dist/providers/proxy/types.d.ts.map +1 -1
  14. package/dist/providers/zai/fetch.d.ts +32 -0
  15. package/dist/providers/zai/fetch.d.ts.map +1 -0
  16. package/dist/providers/zai/fetch.js +40 -0
  17. package/dist/providers/zai/index.d.ts +7 -0
  18. package/dist/providers/zai/index.d.ts.map +1 -0
  19. package/dist/providers/zai/index.js +39 -0
  20. package/dist/providers/zai/types.d.ts +61 -0
  21. package/dist/providers/zai/types.d.ts.map +1 -0
  22. package/dist/providers/zai/types.js +4 -0
  23. package/dist/types.d.ts +30 -0
  24. package/dist/types.d.ts.map +1 -1
  25. package/dist/ui/formatters/proxy.d.ts.map +1 -1
  26. package/dist/ui/formatters/proxy.js +10 -4
  27. package/dist/ui/formatters/shared.d.ts.map +1 -1
  28. package/dist/ui/formatters/shared.js +20 -12
  29. package/dist/ui/formatters/zai.d.ts +6 -0
  30. package/dist/ui/formatters/zai.d.ts.map +1 -0
  31. package/dist/ui/formatters/zai.js +29 -0
  32. package/dist/ui/status.d.ts.map +1 -1
  33. package/dist/ui/status.js +3 -0
  34. package/dist/usage/config.d.ts.map +1 -1
  35. package/dist/usage/config.js +17 -3
  36. package/dist/usage/fetch.d.ts.map +1 -1
  37. package/dist/usage/fetch.js +5 -2
  38. package/dist/usage/registry.d.ts +4 -0
  39. package/dist/usage/registry.d.ts.map +1 -1
  40. package/dist/usage/registry.js +8 -0
  41. package/package.json +1 -1
package/README.md CHANGED
@@ -25,6 +25,75 @@ Add to your `opencode.json`:
25
25
 
26
26
  OpenCode installs dependencies automatically on next launch.
27
27
 
28
+ ## Configuration
29
+
30
+ The plugin creates a default config file on first run at:
31
+
32
+ **Linux/macOS**: `~/.config/opencode/usage-config.jsonc`
33
+ **Windows**: `%APPDATA%\opencode\usage-config.jsonc`
34
+
35
+ ```jsonc
36
+ {
37
+ // REQUIRED: Proxy server endpoint (default: "http://localhost:8000")
38
+ // Leave empty ONLY if you don't use the proxy
39
+ "endpoint": "http://localhost:8000",
40
+
41
+ // REQUIRED: API key for proxy auth (default: "VerysecretKey")
42
+ // Leave empty if your proxy doesn't require authentication
43
+ "apiKey": "VerysecretKey",
44
+
45
+ // Optional: Request timeout in milliseconds (default: 10000)
46
+ "timeout": 10000,
47
+
48
+ // Optional: Show/hide providers in /usage output
49
+ "providers": {
50
+ "openai": true,
51
+ "proxy": true,
52
+ "copilot": true
53
+ },
54
+
55
+ // Model group display configuration (optional)
56
+ "modelGroups": {
57
+ // Show all model groups from proxy (default: true)
58
+ // When true: auto-discovers all groups, uses displayNames as overrides
59
+ // When false: only shows groups listed in displayNames (whitelist mode)
60
+ "showAll": true,
61
+
62
+ // Override display names for specific groups (optional)
63
+ // Groups not listed here use their original name from the proxy
64
+ "displayNames": {
65
+ "g3-pro": "Gemini Pro",
66
+ "g3-flash": "Gemini Flash",
67
+ "claude": "Claude"
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ > **⚠️ Important**: If using the Mirrowel Proxy, both `endpoint` and `apiKey` must be set. The proxy defaults to `endpoint: http://localhost:8000` and `apiKey: VerysecretKey`. If you changed these during your proxy setup, you MUST update your config file to match.
74
+
75
+ ### Model Group Configuration
76
+
77
+ The `modelGroups` section controls how quota groups are displayed:
78
+
79
+ | `showAll` | `displayNames` | Behavior |
80
+ |-----------|----------------|----------|
81
+ | `true` (default) | empty/missing | Show all groups with original names |
82
+ | `true` | provided | Show all groups, apply display name overrides |
83
+ | `false` | provided | Only show groups in displayNames (whitelist mode) |
84
+ | `false` | empty/missing | Shows no groups (all filtered out) |
85
+ | missing section | — | Legacy behavior (hardcoded group whitelist) |
86
+
87
+ If missing, the plugin creates a default template on first run.
88
+
89
+ ### Copilot auth
90
+
91
+ Copilot is detected from either of these locations:
92
+
93
+ - `~/.local/share/opencode/copilot-usage-token.json`
94
+ - `~/.local/share/opencode/auth.json` with a `github-copilot` entry
95
+ - `~/.config/opencode/copilot-quota-token.json` (optional override)
96
+
28
97
  ## Usage
29
98
 
30
99
  ### Check all providers
@@ -55,38 +124,22 @@ OpenCode installs dependencies automatically on next launch.
55
124
  | **Mirrowel Proxy** | Local `/v1/quota-stats` endpoint |
56
125
  | **GitHub Copilot** | GitHub internal usage APIs |
57
126
 
58
- ## Configuration
59
-
60
- Optional config at `~/.config/opencode/usage-config.jsonc`:
61
-
62
- ```jsonc
63
- {
64
- // Proxy server endpoint
65
- "endpoint": "http://localhost:8000",
66
-
67
- // API key for proxy auth
68
- "apiKey": "your-key",
69
-
70
- // Request timeout (ms)
71
- "timeout": 10000,
72
-
73
- // Show/hide providers in /usage output
74
- "providers": {
75
- "openai": true,
76
- "proxy": true,
77
- "copilot": true
78
- }
79
- }
80
- ```
127
+ ## Troubleshooting
81
128
 
82
- If missing, the plugin creates a default template on first run.
83
-
84
- ### Copilot auth
129
+ **Proxy shows "not configured" error**
130
+ - Ensure `endpoint` and `apiKey` are set in `usage-config.jsonc`
131
+ - Default values: `endpoint: http://localhost:8000`, `apiKey: VerysecretKey`
132
+ - If you changed these during proxy setup, update your config file to match
133
+ - Verify your proxy is running at the specified endpoint
85
134
 
86
- Copilot is detected from either of these locations:
135
+ **Missing provider data**
136
+ - Use `providers: { ... }` in config to disable unused providers
137
+ - For Codex: Ensure you have valid auth tokens
138
+ - For Copilot: Check token file locations in Configuration section above
87
139
 
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)
140
+ **Config file not found**
141
+ - The plugin auto-creates `usage-config.jsonc` on first run
142
+ - Check the path in Configuration section above
143
+ - Manually create the file if needed
91
144
 
92
145
  See `AGENTS.md` for internal architecture.
@@ -3,5 +3,6 @@ export declare const providers: Record<string, UsageProvider<unknown>>;
3
3
  export { CodexProvider } from "./codex";
4
4
  export { ProxyProvider } from "./proxy";
5
5
  export { CopilotProvider } from "./copilot";
6
+ export { ZaiProvider } from "./zai";
6
7
  export type { UsageProvider } from "./base";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AAK3C,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAI5D,CAAA;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,YAAY,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AAM3C,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAK5D,CAAA;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AACnC,YAAY,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA"}
@@ -1,11 +1,14 @@
1
1
  import { CodexProvider } from "./codex";
2
2
  import { ProxyProvider } from "./proxy";
3
3
  import { CopilotProvider } from "./copilot";
4
+ import { ZaiProvider } from "./zai";
4
5
  export const providers = {
5
6
  [CodexProvider.id]: CodexProvider,
6
7
  [ProxyProvider.id]: ProxyProvider,
7
8
  [CopilotProvider.id]: CopilotProvider,
9
+ [ZaiProvider.id]: ZaiProvider,
8
10
  };
9
11
  export { CodexProvider } from "./codex";
10
12
  export { ProxyProvider } from "./proxy";
11
13
  export { CopilotProvider } from "./copilot";
14
+ export { ZaiProvider } from "./zai";
@@ -4,7 +4,7 @@
4
4
  export async function fetchProxyLimits(config) {
5
5
  const { endpoint, apiKey, timeout = 10000 } = config;
6
6
  if (!endpoint) {
7
- throw new Error("Proxy endpoint not configured");
7
+ throw new Error("Proxy endpoint not configured. Set 'endpoint' in ~/.config/opencode/usage-config.jsonc (Windows: %APPDATA%\\opencode\\usage-config.jsonc)\n\nDefault: http://localhost:8000\nIf you changed this during proxy setup, update your config.");
8
8
  }
9
9
  const headers = {
10
10
  "Content-Type": "application/json",
@@ -23,7 +23,7 @@ export async function fetchProxyLimits(config) {
23
23
  signal: controller.signal,
24
24
  });
25
25
  if (!response.ok) {
26
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
26
+ throw new Error(`Proxy request failed: HTTP ${response.status}. Check your usage-config.jsonc:\n\n - Default endpoint: http://localhost:8000\n - Default apiKey: VerysecretKey\n\nIf you changed these during proxy setup, you MUST update your config file to match.`);
27
27
  }
28
28
  return (await response.json());
29
29
  }
@@ -2,5 +2,6 @@
2
2
  * Display formatting utilities for proxy limits.
3
3
  */
4
4
  import type { ProxyResponse } from "./types";
5
- export declare function formatProxyLimits(data: ProxyResponse): string;
5
+ import type { UsageConfig } from "../../types";
6
+ export declare function formatProxyLimits(data: ProxyResponse, config: UsageConfig | null): string;
6
7
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA8B,MAAM,SAAS,CAAA;AAoHxE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAoC7D"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA8B,MAAM,SAAS,CAAA;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA0G9C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,MAAM,CAqCzF"}
@@ -1,22 +1,11 @@
1
1
  /**
2
2
  * Display formatting utilities for proxy limits.
3
3
  */
4
- const GROUP_MAPPING = {
5
- "claude": "claude",
6
- "g3-pro": "g3-pro",
7
- "g3-flash": "g3-flash",
8
- "g25-flash": "25-flash",
9
- "g25-lite": "25-lite",
10
- "pro": "g3-pro",
11
- "3-flash": "g3-flash",
12
- "25-flash": "25-flash",
13
- "25-lite": "25-lite"
14
- };
15
- const GROUP_ORDER = ["claude", "g3-pro", "g3-flash", "25-flash", "25-lite"];
16
- function sortGroupNames(groups) {
4
+ import { resolveDisplayName } from "./index";
5
+ function sortGroupNames(groups, groupOrder) {
17
6
  return Array.from(groups.keys()).sort((a, b) => {
18
- const aIndex = GROUP_ORDER.indexOf(a);
19
- const bIndex = GROUP_ORDER.indexOf(b);
7
+ const aIndex = groupOrder.indexOf(a);
8
+ const bIndex = groupOrder.indexOf(b);
20
9
  if (aIndex !== -1 && bIndex !== -1)
21
10
  return aIndex - bIndex;
22
11
  if (aIndex !== -1)
@@ -59,7 +48,7 @@ function formatResetTime(isoString) {
59
48
  return "";
60
49
  }
61
50
  }
62
- function aggregateCredentialsByTier(credentials) {
51
+ function aggregateCredentialsByTier(credentials, config) {
63
52
  const result = {
64
53
  paid: new Map(),
65
54
  free: new Map(),
@@ -68,9 +57,9 @@ function aggregateCredentialsByTier(credentials) {
68
57
  const tier = normalizeTier(cred.tier);
69
58
  const groupUsage = cred.group_usage ?? {};
70
59
  for (const [name, groupData] of Object.entries(groupUsage)) {
71
- if (!(name in GROUP_MAPPING))
60
+ const mappedName = resolveDisplayName(name, config);
61
+ if (mappedName === null)
72
62
  continue;
73
- const mappedName = GROUP_MAPPING[name];
74
63
  const windows = groupData.windows || {};
75
64
  let bestWindow = null;
76
65
  const windowPriority = ["daily", "5h", "1h", "15m"];
@@ -107,8 +96,9 @@ function aggregateCredentialsByTier(credentials) {
107
96
  }
108
97
  return result;
109
98
  }
110
- export function formatProxyLimits(data) {
99
+ export function formatProxyLimits(data, config) {
111
100
  const lines = [];
101
+ const groupOrder = ["claude", "g3-pro", "g3-flash", "25-flash", "25-lite"];
112
102
  lines.push("[Google] Mirrowel Proxy");
113
103
  lines.push("");
114
104
  if (!data.providers || Object.keys(data.providers).length === 0) {
@@ -118,13 +108,13 @@ export function formatProxyLimits(data) {
118
108
  for (const [providerName, provider] of Object.entries(data.providers)) {
119
109
  lines.push(`${providerName}:`);
120
110
  const credentialsArray = Object.values(provider.credentials ?? {});
121
- const tierData = aggregateCredentialsByTier(credentialsArray);
111
+ const tierData = aggregateCredentialsByTier(credentialsArray, config);
122
112
  for (const [tierName, quotas] of Object.entries(tierData)) {
123
113
  if (quotas.size === 0)
124
114
  continue;
125
115
  const tierLabel = tierName === "paid" ? "Paid" : "Free";
126
116
  lines.push(` ${tierLabel}:`);
127
- const sortedNames = sortGroupNames(quotas);
117
+ const sortedNames = sortGroupNames(quotas, groupOrder);
128
118
  for (const groupName of sortedNames) {
129
119
  const quota = quotas.get(groupName);
130
120
  const remainingPct = quota.max > 0 ? (quota.remaining / quota.max) * 100 : 0;
@@ -3,8 +3,14 @@
3
3
  * Fetches quota stats from a local/remote proxy server.
4
4
  */
5
5
  import type { UsageProvider } from "../base";
6
+ import type { UsageConfig } from "../../types";
6
7
  export type { ProxyResponse } from "./types";
7
8
  export { fetchProxyLimits } from "./fetch";
8
9
  export { formatProxyLimits } from "./format";
10
+ /**
11
+ * Resolve the display name for a model group based on config.
12
+ * Returns null if the group should be filtered out.
13
+ */
14
+ export declare function resolveDisplayName(groupName: string, config: UsageConfig | null): string | null;
9
15
  export declare const ProxyProvider: UsageProvider<void>;
10
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAM5C,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAkM5C,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,IAAI,CAwB7C,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,KAAK,EAAgF,WAAW,EAAE,MAAM,aAAa,CAAA;AAK5H,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AA4B5C;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,GAAG,IAAI,GACzB,MAAM,GAAG,IAAI,CAoBf;AAyND,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,IAAI,CAwB7C,CAAA"}
@@ -6,7 +6,8 @@ import { loadUsageConfig } from "../../usage/config";
6
6
  import { fetchProxyLimits } from "./fetch";
7
7
  export { fetchProxyLimits } from "./fetch";
8
8
  export { formatProxyLimits } from "./format";
9
- const GROUP_MAPPING = {
9
+ /** Default mapping for backward compatibility when no modelGroups config is present */
10
+ const DEFAULT_GROUP_MAPPING = {
10
11
  "claude": "claude",
11
12
  "g3-pro": "g3-pro",
12
13
  "g3-flash": "g3-flash",
@@ -31,6 +32,29 @@ function sortQuotaGroups(groups) {
31
32
  return a.name.localeCompare(b.name);
32
33
  });
33
34
  }
35
+ /**
36
+ * Resolve the display name for a model group based on config.
37
+ * Returns null if the group should be filtered out.
38
+ */
39
+ export function resolveDisplayName(groupName, config) {
40
+ const modelGroupsConfig = config?.modelGroups;
41
+ // No config section → backward compat: use hardcoded whitelist
42
+ if (!modelGroupsConfig) {
43
+ return groupName in DEFAULT_GROUP_MAPPING
44
+ ? DEFAULT_GROUP_MAPPING[groupName]
45
+ : null; // filter out
46
+ }
47
+ const showAll = modelGroupsConfig.showAll ?? true; // Default to auto-discovery
48
+ const displayNames = modelGroupsConfig.displayNames ?? {};
49
+ if (showAll) {
50
+ // Auto-discovery mode: show all, apply overrides
51
+ return displayNames[groupName] ?? groupName;
52
+ }
53
+ else {
54
+ // Whitelist mode: only show configured groups
55
+ return groupName in displayNames ? displayNames[groupName] : null;
56
+ }
57
+ }
34
58
  function normalizeTier(tier) {
35
59
  if (!tier)
36
60
  return "free";
@@ -39,7 +63,26 @@ function normalizeTier(tier) {
39
63
  return "paid";
40
64
  return "free";
41
65
  }
42
- function parseQuotaGroupsFromAggregation(quotaGroups) {
66
+ function pickPreferredResetTime(current, incoming) {
67
+ if (!incoming)
68
+ return current ?? null;
69
+ if (!current)
70
+ return incoming;
71
+ const now = Date.now();
72
+ const currentTs = new Date(current).getTime();
73
+ const incomingTs = new Date(incoming).getTime();
74
+ const currentFuture = currentTs > now;
75
+ const incomingFuture = incomingTs > now;
76
+ if (currentFuture && incomingFuture) {
77
+ return incomingTs < currentTs ? incoming : current;
78
+ }
79
+ if (incomingFuture)
80
+ return incoming;
81
+ if (currentFuture)
82
+ return current;
83
+ return incomingTs > currentTs ? incoming : current;
84
+ }
85
+ function parseQuotaGroupsFromAggregation(quotaGroups, config) {
43
86
  if (!quotaGroups)
44
87
  return [];
45
88
  const tiers = {
@@ -47,8 +90,8 @@ function parseQuotaGroupsFromAggregation(quotaGroups) {
47
90
  free: new Map(),
48
91
  };
49
92
  for (const [groupName, groupData] of Object.entries(quotaGroups)) {
50
- const mappedName = GROUP_MAPPING[groupName];
51
- if (!mappedName)
93
+ const displayName = resolveDisplayName(groupName, config);
94
+ if (displayName === null)
52
95
  continue;
53
96
  const windows = groupData.windows || {};
54
97
  const windowPriority = ["daily", "5h", "1h", "15m"];
@@ -68,11 +111,18 @@ function parseQuotaGroupsFromAggregation(quotaGroups) {
68
111
  // Since these are already aggregated by provider, we split by tier if possible.
69
112
  // However, the aggregate API provides tier-agnostic totals.
70
113
  // For now, we put them into "paid" as the default high-level view.
71
- tiers.paid.set(mappedName, {
72
- name: mappedName,
73
- remaining: window.total_remaining,
74
- max: window.total_max,
75
- remainingPct: Math.round(window.remaining_pct),
114
+ const max = window.total_max ?? window.total_remaining;
115
+ const remaining = window.total_remaining;
116
+ const remainingPct = typeof window.remaining_pct === "number"
117
+ ? Math.round(window.remaining_pct)
118
+ : max > 0
119
+ ? Math.round((remaining / max) * 100)
120
+ : 0;
121
+ tiers.paid.set(displayName, {
122
+ name: displayName,
123
+ remaining,
124
+ max,
125
+ remainingPct,
76
126
  resetTime: null, // Aggregated windows don't have a single reset_at
77
127
  });
78
128
  }
@@ -82,13 +132,14 @@ function parseQuotaGroupsFromAggregation(quotaGroups) {
82
132
  }
83
133
  return result;
84
134
  }
85
- function parseQuotaGroupsFromCredential(groupUsage) {
135
+ function parseQuotaGroupsFromCredential(groupUsage, config) {
86
136
  if (!groupUsage)
87
137
  return [];
88
138
  const result = new Map();
89
139
  for (const [groupName, groupData] of Object.entries(groupUsage)) {
90
- const mappedName = GROUP_MAPPING[groupName];
91
- if (!mappedName)
140
+ // Apply config-based filtering and display name resolution
141
+ const displayName = resolveDisplayName(groupName, config);
142
+ if (displayName === null)
92
143
  continue;
93
144
  const windows = groupData.windows || {};
94
145
  let bestWindow = null;
@@ -104,8 +155,8 @@ function parseQuotaGroupsFromCredential(groupUsage) {
104
155
  }
105
156
  if (!bestWindow)
106
157
  continue;
107
- result.set(mappedName, {
108
- name: mappedName,
158
+ result.set(displayName, {
159
+ name: displayName,
109
160
  remaining: bestWindow.remaining,
110
161
  max: bestWindow.limit || bestWindow.remaining,
111
162
  remainingPct: bestWindow.limit ? Math.round((bestWindow.remaining / bestWindow.limit) * 100) : 0,
@@ -114,11 +165,10 @@ function parseQuotaGroupsFromCredential(groupUsage) {
114
165
  }
115
166
  return Array.from(result.values());
116
167
  }
117
- function aggregateByProvider(provider) {
118
- // Try aggregated quota groups first
119
- if (provider.quota_groups && Object.keys(provider.quota_groups).length > 0) {
120
- return parseQuotaGroupsFromAggregation(provider.quota_groups);
121
- }
168
+ function aggregateByProvider(provider, config) {
169
+ const aggregated = provider.quota_groups && Object.keys(provider.quota_groups).length > 0
170
+ ? parseQuotaGroupsFromAggregation(provider.quota_groups, config)
171
+ : [];
122
172
  // Fallback to manual aggregation of credentials
123
173
  const tiers = {
124
174
  paid: new Map(),
@@ -127,15 +177,13 @@ function aggregateByProvider(provider) {
127
177
  if (provider.credentials) {
128
178
  for (const cred of Object.values(provider.credentials)) {
129
179
  const tier = normalizeTier(cred.tier);
130
- const groups = parseQuotaGroupsFromCredential(cred.group_usage);
180
+ const groups = parseQuotaGroupsFromCredential(cred.group_usage, config);
131
181
  for (const group of groups) {
132
182
  const existing = tiers[tier].get(group.name);
133
183
  if (existing) {
134
184
  existing.remaining += group.remaining;
135
185
  existing.max += group.max;
136
- if (group.resetTime && (!existing.resetTime || new Date(group.resetTime) > new Date(existing.resetTime))) {
137
- existing.resetTime = group.resetTime;
138
- }
186
+ existing.resetTime = pickPreferredResetTime(existing.resetTime, group.resetTime);
139
187
  }
140
188
  else {
141
189
  tiers[tier].set(group.name, { ...group });
@@ -143,6 +191,20 @@ function aggregateByProvider(provider) {
143
191
  }
144
192
  }
145
193
  }
194
+ if (aggregated.length > 0) {
195
+ const resetLookup = new Map();
196
+ for (const tierInfo of Object.values(tiers)) {
197
+ for (const group of tierInfo.values()) {
198
+ resetLookup.set(group.name, pickPreferredResetTime(resetLookup.get(group.name), group.resetTime));
199
+ }
200
+ }
201
+ for (const tier of aggregated) {
202
+ for (const group of tier.quotaGroups) {
203
+ group.resetTime = pickPreferredResetTime(group.resetTime, resetLookup.get(group.name));
204
+ }
205
+ }
206
+ return aggregated;
207
+ }
146
208
  for (const tierGroups of Object.values(tiers)) {
147
209
  for (const group of tierGroups.values()) {
148
210
  group.remainingPct = group.max > 0 ? Math.round((group.remaining / group.max) * 100) : 0;
@@ -157,20 +219,20 @@ function aggregateByProvider(provider) {
157
219
  }
158
220
  return result;
159
221
  }
160
- function parseProviders(data) {
222
+ function parseProviders(data, config) {
161
223
  if (!data.providers)
162
224
  return [];
163
225
  return Object.entries(data.providers).map(([name, provider]) => {
164
226
  return {
165
227
  name,
166
- tiers: aggregateByProvider(provider),
228
+ tiers: aggregateByProvider(provider, config),
167
229
  };
168
230
  });
169
231
  }
170
- function parseProxyQuota(data) {
232
+ function parseProxyQuota(data, config) {
171
233
  const summary = data.summary;
172
234
  return {
173
- providers: parseProviders(data),
235
+ providers: parseProviders(data, config),
174
236
  totalCredentials: summary?.total_credentials ?? 0,
175
237
  activeCredentials: summary?.active_credentials ?? 0,
176
238
  dataSource: data.data_source,
@@ -191,7 +253,7 @@ export const ProxyProvider = {
191
253
  secondary: null,
192
254
  codeReview: null,
193
255
  credits: null,
194
- proxyQuota: parseProxyQuota(data),
256
+ proxyQuota: parseProxyQuota(data, config),
195
257
  updatedAt: Date.now(),
196
258
  };
197
259
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Type definitions for the Antigravity proxy provider.
3
3
  */
4
+ /** Token statistics from the proxy */
4
5
  export type TokenStats = {
5
6
  input_cached?: number;
6
7
  input_uncached?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/providers/proxy/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Fetch logic for Z.ai usage monitoring.
3
+ */
4
+ import type { ZaiAuth } from "./types";
5
+ export declare function fetchZaiUsage(auth: ZaiAuth): Promise<{
6
+ quota: {
7
+ limits: import("./types").ZaiQuotaLimit[];
8
+ };
9
+ model: {
10
+ x_time: string[];
11
+ modelCallCount: (number | null)[];
12
+ tokensUsage: (number | null)[];
13
+ totalUsage: {
14
+ totalModelCallCount: number;
15
+ totalTokensUsage: number;
16
+ };
17
+ } | undefined;
18
+ tool: {
19
+ x_time: string[];
20
+ networkSearchCount: (number | null)[];
21
+ webReadMcpCount: (number | null)[];
22
+ zreadMcpCount: (number | null)[];
23
+ totalUsage: {
24
+ totalNetworkSearchCount: number;
25
+ totalWebReadMcpCount: number;
26
+ totalZreadMcpCount: number;
27
+ totalSearchMcpCount: number;
28
+ toolDetails: unknown[];
29
+ };
30
+ } | undefined;
31
+ }>;
32
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../src/providers/zai/fetch.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAiE,MAAM,SAAS,CAAA;AAErG,wBAAsB,aAAa,CAAC,IAAI,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;GA4ChD"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Fetch logic for Z.ai usage monitoring.
3
+ */
4
+ import { loadUsageConfig } from "../../usage/config";
5
+ export async function fetchZaiUsage(auth) {
6
+ const config = await loadUsageConfig().catch(() => null);
7
+ const baseUrl = config?.zaiEndpoint?.replace(/\/$/, "") || "https://api.z.ai";
8
+ const monitorUrl = `${baseUrl}/api/monitor/usage`;
9
+ const now = new Date();
10
+ const endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), 0, 0, 0);
11
+ const startDate = new Date(endDate.getTime() - 24 * 60 * 60 * 1000);
12
+ const formatDateTime = (date) => {
13
+ const pad = (n) => String(n).padStart(2, "0");
14
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
15
+ };
16
+ const startTime = formatDateTime(startDate);
17
+ const endTime = formatDateTime(endDate);
18
+ const queryParams = `?startTime=${encodeURIComponent(startTime)}&endTime=${encodeURIComponent(endTime)}`;
19
+ const headers = {
20
+ "Authorization": auth.key,
21
+ "Accept-Language": "en-US,en",
22
+ "Content-Type": "application/json",
23
+ };
24
+ const [quotaRes, modelRes, toolRes] = await Promise.all([
25
+ fetch(`${monitorUrl}/quota/limit`, { headers }),
26
+ fetch(`${monitorUrl}/model-usage${queryParams}`, { headers }),
27
+ fetch(`${monitorUrl}/tool-usage${queryParams}`, { headers }),
28
+ ]);
29
+ if (!quotaRes.ok) {
30
+ throw new Error(`Z.ai quota query failed: ${quotaRes.status} ${await quotaRes.text()}`);
31
+ }
32
+ const quota = (await quotaRes.json());
33
+ const model = modelRes.ok ? (await modelRes.json()) : null;
34
+ const tool = toolRes.ok ? (await toolRes.json()) : null;
35
+ return {
36
+ quota: quota.data,
37
+ model: model?.data,
38
+ tool: tool?.data,
39
+ };
40
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Z.ai GLM Coding Plan provider.
3
+ */
4
+ import type { UsageProvider } from "../base";
5
+ import type { ZaiAuth } from "./types";
6
+ export declare const ZaiProvider: UsageProvider<ZaiAuth>;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/zai/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAG5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,OAAO,CAmC9C,CAAA"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Z.ai GLM Coding Plan provider.
3
+ */
4
+ import { fetchZaiUsage } from "./fetch";
5
+ export const ZaiProvider = {
6
+ id: "zai-coding-plan",
7
+ displayName: "Z.ai GLM Coding Plan",
8
+ async fetchUsage(auth) {
9
+ try {
10
+ const data = await fetchZaiUsage(auth);
11
+ return {
12
+ timestamp: Date.now(),
13
+ provider: "zai-coding-plan",
14
+ planType: null, // Don't assume pro, let quota numbers show tier
15
+ primary: null,
16
+ secondary: null,
17
+ codeReview: null,
18
+ credits: null,
19
+ zaiQuota: {
20
+ limits: data.quota.limits.map(l => ({
21
+ type: l.type,
22
+ usage: l.usage,
23
+ currentValue: l.currentValue,
24
+ remaining: l.remaining,
25
+ percentage: l.percentage,
26
+ nextResetTime: l.nextResetTime,
27
+ usageDetails: l.usageDetails
28
+ })),
29
+ modelUsage: data.model?.totalUsage,
30
+ toolUsage: data.tool?.totalUsage
31
+ },
32
+ updatedAt: Date.now(),
33
+ };
34
+ }
35
+ catch (e) {
36
+ return null;
37
+ }
38
+ },
39
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Type definitions for the Z.ai GLM Coding Plan provider.
3
+ */
4
+ export interface ZaiQuotaLimit {
5
+ type: string;
6
+ unit: number;
7
+ number: number;
8
+ usage: number;
9
+ currentValue: number;
10
+ remaining: number;
11
+ percentage: number;
12
+ usageDetails?: Array<{
13
+ modelCode: string;
14
+ usage: number;
15
+ }>;
16
+ nextResetTime?: number;
17
+ }
18
+ export interface ZaiQuotaResponse {
19
+ code: number;
20
+ msg: string;
21
+ data: {
22
+ limits: ZaiQuotaLimit[];
23
+ };
24
+ success: boolean;
25
+ }
26
+ export interface ZaiModelUsageResponse {
27
+ code: number;
28
+ msg: string;
29
+ data: {
30
+ x_time: string[];
31
+ modelCallCount: (number | null)[];
32
+ tokensUsage: (number | null)[];
33
+ totalUsage: {
34
+ totalModelCallCount: number;
35
+ totalTokensUsage: number;
36
+ };
37
+ };
38
+ success: boolean;
39
+ }
40
+ export interface ZaiToolUsageResponse {
41
+ code: number;
42
+ msg: string;
43
+ data: {
44
+ x_time: string[];
45
+ networkSearchCount: (number | null)[];
46
+ webReadMcpCount: (number | null)[];
47
+ zreadMcpCount: (number | null)[];
48
+ totalUsage: {
49
+ totalNetworkSearchCount: number;
50
+ totalWebReadMcpCount: number;
51
+ totalZreadMcpCount: number;
52
+ totalSearchMcpCount: number;
53
+ toolDetails: unknown[];
54
+ };
55
+ };
56
+ success: boolean;
57
+ }
58
+ export interface ZaiAuth {
59
+ key: string;
60
+ }
61
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/providers/zai/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE;QACJ,MAAM,EAAE,aAAa,EAAE,CAAA;KACxB,CAAA;IACD,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,EAAE,CAAA;QAChB,cAAc,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;QACjC,WAAW,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;QAC9B,UAAU,EAAE;YACV,mBAAmB,EAAE,MAAM,CAAA;YAC3B,gBAAgB,EAAE,MAAM,CAAA;SACzB,CAAA;KACF,CAAA;IACD,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,EAAE,CAAA;QAChB,kBAAkB,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;QACrC,eAAe,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;QAClC,aAAa,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;QAChC,UAAU,EAAE;YACV,uBAAuB,EAAE,MAAM,CAAA;YAC/B,oBAAoB,EAAE,MAAM,CAAA;YAC5B,kBAAkB,EAAE,MAAM,CAAA;YAC1B,mBAAmB,EAAE,MAAM,CAAA;YAC3B,WAAW,EAAE,OAAO,EAAE,CAAA;SACvB,CAAA;KACF,CAAA;IACD,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAA;CACZ"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Type definitions for the Z.ai GLM Coding Plan provider.
3
+ */
4
+ export {};
package/dist/types.d.ts CHANGED
@@ -45,11 +45,40 @@ export interface ProxyQuota {
45
45
  export interface UsageConfig {
46
46
  endpoint?: string;
47
47
  apiKey?: string;
48
+ zaiEndpoint?: string;
48
49
  timeout?: number;
49
50
  providers?: {
50
51
  openai?: boolean;
51
52
  proxy?: boolean;
52
53
  copilot?: boolean;
54
+ zai?: boolean;
55
+ };
56
+ modelGroups?: {
57
+ showAll?: boolean;
58
+ displayNames?: Record<string, string>;
59
+ };
60
+ }
61
+ export interface ZaiQuota {
62
+ limits: Array<{
63
+ type: string;
64
+ usage: number;
65
+ currentValue: number;
66
+ remaining: number;
67
+ percentage: number;
68
+ nextResetTime?: number;
69
+ usageDetails?: Array<{
70
+ modelCode: string;
71
+ usage: number;
72
+ }>;
73
+ }>;
74
+ modelUsage?: {
75
+ totalModelCallCount: number;
76
+ totalTokensUsage: number;
77
+ };
78
+ toolUsage?: {
79
+ totalNetworkSearchCount: number;
80
+ totalWebReadMcpCount: number;
81
+ totalZreadMcpCount: number;
53
82
  };
54
83
  }
55
84
  export interface UsageSnapshot {
@@ -62,6 +91,7 @@ export interface UsageSnapshot {
62
91
  credits: CreditsSnapshot | null;
63
92
  proxyQuota?: ProxyQuota;
64
93
  copilotQuota?: CopilotQuota;
94
+ zaiQuota?: ZaiQuota;
65
95
  updatedAt: number;
66
96
  isMissing?: boolean;
67
97
  missingReason?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../../src/ui/formatters/proxy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAcrE"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../../src/ui/formatters/proxy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAOhD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAcrE"}
@@ -22,10 +22,16 @@ function formatProxyProvider(provider) {
22
22
  if (!tier.quotaGroups?.length)
23
23
  continue;
24
24
  lines.push(` ${tier.tier === "paid" ? "Paid" : "Free"}:`);
25
- for (const group of tier.quotaGroups) {
26
- const reset = group.resetTime ? formatResetSuffixISO(group.resetTime) : "";
27
- lines.push(` ${group.name.padEnd(9)} ${formatBar(group.remainingPct)} ${group.remaining}/${group.max}${reset}`);
28
- }
25
+ lines.push(...formatTierGroups(tier.quotaGroups));
29
26
  }
30
27
  return lines;
31
28
  }
29
+ function formatTierGroups(groups) {
30
+ const nameWidth = Math.max(...groups.map(group => group.name.length), 9);
31
+ const quotaWidth = Math.max(...groups.map(group => `${group.remaining}/${group.max}`.length), 7);
32
+ return groups.map(group => {
33
+ const reset = group.resetTime ? formatResetSuffixISO(group.resetTime) : "";
34
+ const quota = `${group.remaining}/${group.max}`.padStart(quotaWidth);
35
+ return ` ${group.name.padEnd(nameWidth)} ${formatBar(group.remainingPct)} ${quota}${reset}`;
36
+ });
37
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/ui/formatters/shared.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK7C;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGhE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKxD;AAWD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAgBvE"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/ui/formatters/shared.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK7C;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAIhE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMxD;AAwBD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAgBvE"}
@@ -13,35 +13,43 @@ export function formatBar(pct) {
13
13
  export function formatResetSuffix(resetAt) {
14
14
  if (!resetAt)
15
15
  return "";
16
- return ` (resets in ${formatTimeDelta(resetAt)})`;
16
+ const delta = formatTimeDelta(resetAt);
17
+ return delta === "just refreshed" ? ` (${delta})` : ` (resets in ${delta})`;
17
18
  }
18
19
  export function formatResetSuffixISO(iso) {
19
20
  try {
20
21
  const at = Math.floor(new Date(iso).getTime() / 1000);
21
- return ` (resets in ${formatTimeDelta(at)})`;
22
+ const delta = formatTimeDelta(at);
23
+ return delta === "just refreshed" ? ` (${delta})` : ` (resets in ${delta})`;
22
24
  }
23
25
  catch {
24
26
  return "";
25
27
  }
26
28
  }
27
29
  function formatTimeDelta(at) {
28
- const diff = at - Math.floor(Date.now() / 1000);
30
+ const atSeconds = at > 1e11 ? Math.floor(at / 1000) : at;
31
+ const diff = atSeconds - Math.floor(Date.now() / 1000);
29
32
  if (diff <= 0)
30
- return "now";
31
- if (diff < 60)
32
- return `${diff}s`;
33
- if (diff < 3600)
34
- return `${Math.ceil(diff / 60)}m`;
35
- if (diff < 84400)
36
- return `${Math.round(diff / 3600)}h`;
37
- return `${Math.round(diff / 86400)}d`;
33
+ return "just refreshed";
34
+ const days = Math.floor(diff / 86400);
35
+ const hours = Math.floor((diff % 86400) / 3600);
36
+ const minutes = Math.floor((diff % 3600) / 60);
37
+ if (days > 0) {
38
+ return hours > 0 ? `${days}d${hours}hrs` : `${days}d`;
39
+ }
40
+ if (hours > 0) {
41
+ return minutes > 0 ? `${hours}hrs${minutes}m` : `${hours}hrs`;
42
+ }
43
+ if (minutes > 0)
44
+ return `${minutes}m`;
45
+ return `${diff}s`;
38
46
  }
39
47
  export function formatMissingSnapshot(snapshot) {
40
48
  const { provider } = snapshot;
41
49
  const configPath = getConfigPath();
42
50
  const instructions = {
43
51
  codex: "if you dont have codex oauth, please set your usage-config.jsonc to openai: false",
44
- proxy: "if you are not running Mirrowel's proxy, please set your usage-config.jsonc to proxy: false",
52
+ proxy: "check your usage-config.jsonc. Default: endpoint http://localhost:8000, apiKey VerysecretKey. If you changed these during proxy setup, update your config to match. Or set proxy: false to disable.",
45
53
  copilot: "if you are not running GitHub Copilot, please set your usage-config.jsonc to copilot: false"
46
54
  };
47
55
  const lines = [`→ [${provider.toUpperCase()}] - ${instructions[provider] || ""}`];
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Formats Z.ai GLM Coding Plan usage snapshots.
3
+ */
4
+ import type { UsageSnapshot } from "../../types";
5
+ export declare function formatZaiSnapshot(snapshot: UsageSnapshot): string[];
6
+ //# sourceMappingURL=zai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zai.d.ts","sourceRoot":"","sources":["../../../src/ui/formatters/zai.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CA8BnE"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Formats Z.ai GLM Coding Plan usage snapshots.
3
+ */
4
+ import { formatBar, formatMissingSnapshot, formatResetSuffix } from "./shared";
5
+ export function formatZaiSnapshot(snapshot) {
6
+ const zai = snapshot.zaiQuota;
7
+ if (!zai?.limits?.length)
8
+ return formatMissingSnapshot(snapshot);
9
+ const lines = ["→ [Z.ai] GLM Coding Plan"];
10
+ for (const limit of zai.limits) {
11
+ const isTokens = limit.type === "TOKENS_LIMIT";
12
+ const label = isTokens ? "5 Hours Quota" : "Total Monthly Web Search / Reader / Zread Quota";
13
+ const remainingPct = 100 - limit.percentage;
14
+ const reset = isTokens
15
+ ? (limit.nextResetTime ? formatResetSuffix(limit.nextResetTime) : "")
16
+ : " (resets on the 1st)";
17
+ lines.push(` ${label}`);
18
+ lines.push(` ${formatBar(remainingPct)} ${remainingPct.toFixed(0)}% left${reset}`);
19
+ const unit = isTokens ? "Tokens" : "Times";
20
+ lines.push(` Used: ${limit.currentValue.toLocaleString()} / ${limit.usage.toLocaleString()} ${unit}`);
21
+ lines.push("");
22
+ }
23
+ if (zai.modelUsage) {
24
+ lines.push(` 24h Activity:`);
25
+ lines.push(` Tokens: ${zai.modelUsage.totalTokensUsage.toLocaleString()}`);
26
+ lines.push(` Calls: ${zai.modelUsage.totalModelCallCount.toLocaleString()}`);
27
+ }
28
+ return lines;
29
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/ui/status.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAK1C,KAAK,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;AAExC,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBhB;AAiCD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAahB"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/ui/status.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAM1C,KAAK,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;AAExC,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBhB;AAkCD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAahB"}
package/dist/ui/status.js CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { formatProxySnapshot } from "./formatters/proxy";
6
6
  import { formatCopilotSnapshot } from "./formatters/copilot";
7
+ import { formatZaiSnapshot } from "./formatters/zai";
7
8
  import { formatBar, formatResetSuffix, formatMissingSnapshot } from "./formatters/shared";
8
9
  export async function sendStatusMessage(options) {
9
10
  const bus = options.client.bus;
@@ -32,6 +33,8 @@ function formatSnapshot(snapshot) {
32
33
  return formatProxySnapshot(snapshot);
33
34
  if (snapshot.provider === "copilot")
34
35
  return formatCopilotSnapshot(snapshot);
36
+ if (snapshot.provider === "zai-coding-plan")
37
+ return formatZaiSnapshot(snapshot);
35
38
  const plan = snapshot.planType ? ` (${snapshot.planType.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase())})` : "";
36
39
  const lines = [`→ [${snapshot.provider.toUpperCase()}]${plan}`];
37
40
  const metrics = [
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/usage/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAiB3C,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CA0C5D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/usage/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAiB3C,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CA0D5D"}
@@ -18,13 +18,26 @@ export async function loadUsageConfig() {
18
18
  const file = Bun.file(CONFIG_PATH);
19
19
  if (!(await file.exists())) {
20
20
  const content = `{
21
- "endpoint": "",
22
- "apiKey": "",
21
+ // REQUIRED: Proxy server endpoint (default: "http://localhost:8000")
22
+ // Leave empty ONLY if you don't use the proxy
23
+ "endpoint": "http://localhost:8000",
24
+
25
+ // REQUIRED: API key for proxy auth (default: "VerysecretKey")
26
+ // Leave empty if your proxy doesn't require authentication
27
+ "apiKey": "VerysecretKey",
28
+
29
+ // Optional: Z.ai API endpoint (default: "https://api.z.ai")
30
+ "zaiEndpoint": "https://api.z.ai",
31
+
32
+ // Optional: Request timeout in milliseconds (default: 10000)
23
33
  "timeout": 10000,
34
+
35
+ // Optional: Show/hide providers in /usage output
24
36
  "providers": {
25
37
  "openai": true,
26
38
  "proxy": true,
27
- "copilot": true
39
+ "copilot": true,
40
+ "zai": true
28
41
  }
29
42
  }
30
43
  `;
@@ -37,6 +50,7 @@ export async function loadUsageConfig() {
37
50
  openai: true,
38
51
  proxy: true,
39
52
  copilot: true,
53
+ zai: true,
40
54
  },
41
55
  };
42
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/usage/fetch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAQ7C,wBAAsB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA2CnF;AAWD,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEzE;AA6BD,wBAAsB,SAAS,gDAG9B"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/usage/fetch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAQ7C,wBAAsB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CA4CnF;AAYD,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEzE;AA8BD,wBAAsB,SAAS,gDAG9B"}
@@ -6,7 +6,7 @@ import { providers } from "../providers";
6
6
  import { loadUsageConfig } from "./config";
7
7
  import { loadMergedAuths } from "./auth/loader";
8
8
  import { resolveProviderAuths } from "./registry";
9
- const CORE_PROVIDERS = ["codex", "proxy", "copilot"];
9
+ const CORE_PROVIDERS = ["codex", "proxy", "copilot", "zai-coding-plan"];
10
10
  export async function fetchUsageSnapshots(filter) {
11
11
  const target = resolveFilter(filter);
12
12
  const config = await loadUsageConfig().catch(() => null);
@@ -14,6 +14,8 @@ export async function fetchUsageSnapshots(filter) {
14
14
  const isEnabled = (id) => {
15
15
  if (id === "codex")
16
16
  return toggles.openai !== false;
17
+ if (id === "zai-coding-plan")
18
+ return toggles.zai !== false;
17
19
  return toggles[id] !== false;
18
20
  };
19
21
  const { auths, codexDiagnostics } = await loadMergedAuths();
@@ -51,7 +53,8 @@ function resolveFilter(f) {
51
53
  const aliases = {
52
54
  codex: "codex", openai: "codex", gpt: "codex",
53
55
  proxy: "proxy", agy: "proxy", gemini: "proxy",
54
- copilot: "copilot", github: "copilot"
56
+ copilot: "copilot", github: "copilot",
57
+ zai: "zai-coding-plan", glm: "zai-coding-plan"
55
58
  };
56
59
  return f ? aliases[f.toLowerCase().trim()] : undefined;
57
60
  }
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { CodexAuth } from "../providers/codex";
5
5
  import type { CopilotAuthData } from "../providers/copilot/types";
6
+ import type { ZaiAuth } from "../providers/zai/types";
6
7
  export type AuthEntry = {
7
8
  type?: string;
8
9
  access?: string;
@@ -18,6 +19,9 @@ type ProviderAuthEntry = {
18
19
  } | {
19
20
  providerID: "copilot";
20
21
  auth: CopilotAuthData;
22
+ } | {
23
+ providerID: "zai-coding-plan";
24
+ auth: ZaiAuth;
21
25
  };
22
26
  export declare function resolveProviderAuths(auths: AuthRecord, usageToken: string | null): ProviderAuthEntry[];
23
27
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/usage/registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAEjE,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AAElD,KAAK,iBAAiB,GAClB;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GACxC;IAAE,UAAU,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE,CAAA;AA8BpD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,EAAE,CActG"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/usage/registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAErD,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AAElD,KAAK,iBAAiB,GAClB;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GACxC;IAAE,UAAU,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE,GAChD;IAAE,UAAU,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAsCpD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,EAAE,CActG"}
@@ -20,6 +20,14 @@ const providerDescriptors = [
20
20
  refresh: entry.refresh,
21
21
  }),
22
22
  },
23
+ {
24
+ id: "zai-coding-plan",
25
+ authKeys: ["zai-coding-plan", "zai", "glm"],
26
+ requiresOAuth: false,
27
+ buildAuth: (entry) => ({
28
+ key: entry.key || entry.access || "",
29
+ }),
30
+ },
23
31
  ];
24
32
  export function resolveProviderAuths(auths, usageToken) {
25
33
  const entries = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howaboua/opencode-usage-plugin",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "opencode plugin for tracking AI provider usage, rate limits, and quotas",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",