dsh-subscriptions 0.1.1 → 0.1.2

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 (38) hide show
  1. package/README.md +97 -4
  2. package/icon.svg +28 -0
  3. package/lib/client/CursorCard.js +15 -11
  4. package/lib/client/ExternalUsageCards.js +11 -11
  5. package/lib/client/SubscriptionUsageBadge.d.ts +6 -2
  6. package/lib/client/SubscriptionUsageBadge.js +56 -24
  7. package/lib/client/SubscriptionsSection.d.ts +7 -2
  8. package/lib/client/SubscriptionsSection.js +9 -13
  9. package/lib/client/UsageBadgeDisplaySetting.js +18 -1
  10. package/lib/client/UsageMeter.d.ts +8 -0
  11. package/lib/client/UsageMeter.js +51 -0
  12. package/lib/client/host-contract.d.ts +1 -1
  13. package/lib/client/host-contract.js +1 -0
  14. package/lib/client/locales.d.ts +48 -0
  15. package/lib/client/locales.js +48 -0
  16. package/lib/client/subscription-card-styles.js +0 -5
  17. package/lib/client/usage-color-preferences.d.ts +6 -0
  18. package/lib/client/usage-color-preferences.js +38 -0
  19. package/lib/client/usage-pace.d.ts +29 -0
  20. package/lib/client/usage-pace.js +58 -0
  21. package/lib/client.js +396 -114
  22. package/lib/client.js.map +1 -1
  23. package/lib/index.js +225 -35
  24. package/lib/providers/claude.js +7 -0
  25. package/lib/providers/codex.js +5 -0
  26. package/lib/providers/common.d.ts +10 -0
  27. package/lib/providers/cursor-usage.js +6 -2
  28. package/lib/providers/external-usage-controller.d.ts +1 -1
  29. package/lib/providers/external-usage-controller.js +8 -3
  30. package/lib/providers/external-usage.d.ts +1 -1
  31. package/lib/providers/external-usage.js +62 -2
  32. package/lib/providers/grok.js +3 -1
  33. package/lib/providers/minimax-usage.d.ts +3 -0
  34. package/lib/providers/minimax-usage.js +55 -0
  35. package/lib/providers/pool-usage.js +6 -4
  36. package/locale/en.json +6 -0
  37. package/locale/zh.json +6 -0
  38. package/package.json +7 -3
@@ -0,0 +1,3 @@
1
+ import type { ProviderUsage } from './common.js';
2
+ /** MiniMax subscription quota, not the balance API for sk-api-* inference keys. */
3
+ export declare function fetchMiniMaxUsage(key: string, region: 'global' | 'cn', http?: typeof fetch, signal?: AbortSignal): Promise<ProviderUsage>;
@@ -0,0 +1,55 @@
1
+ /** MiniMax subscription quota, not the balance API for sk-api-* inference keys. */
2
+ export async function fetchMiniMaxUsage(key, region, http = fetch, signal) {
3
+ if (key.startsWith('sk-api-'))
4
+ throw new Error('MiniMax requires a subscription key, not a pay-as-you-go key');
5
+ const response = await http(`https://www.minimax.${region === 'cn' ? 'cn' : 'io'}/v1/token_plan/remains`, {
6
+ headers: { authorization: `Bearer ${key}`, 'content-type': 'application/json' },
7
+ ...(signal === undefined ? {} : { signal }),
8
+ });
9
+ if (!response.ok)
10
+ throw new Error(`MiniMax usage endpoint returned HTTP ${response.status}`);
11
+ const body = await response.json();
12
+ if ((body?.base_resp?.status_code !== undefined && body.base_resp.status_code !== 0) || !Array.isArray(body?.model_remains)) {
13
+ throw new Error('MiniMax usage response failed or has no quota records');
14
+ }
15
+ const windows = [];
16
+ for (const raw of body.model_remains) {
17
+ if (raw === null || typeof raw !== 'object')
18
+ continue;
19
+ const row = raw;
20
+ if (typeof row.model_name !== 'string')
21
+ continue;
22
+ for (const [prefix, startKey, endKey, fallback] of [
23
+ ['current_interval', 'start_time', 'end_time', 'session'],
24
+ ['current_weekly', 'weekly_start_time', 'weekly_end_time', 'weekly'],
25
+ ]) {
26
+ const status = row[`${prefix}_status`];
27
+ if (status === 3)
28
+ continue; // Unlimited pools cannot have a finite percentage.
29
+ const remaining = row[`${prefix}_remaining_percent`];
30
+ const boost = prefix === 'current_weekly' ? row.weekly_boost_permille ?? 1000 : 1000;
31
+ if (typeof boost !== 'number' || !Number.isFinite(boost) || boost <= 0)
32
+ continue;
33
+ const total = row[`${prefix}_total_count`];
34
+ const count = row[`${prefix}_usage_count`];
35
+ if (typeof total !== 'number' || !Number.isFinite(total) || total <= 0)
36
+ continue;
37
+ // Official CLI compatibility: absent percentages use legacy remaining counts.
38
+ const usedPercent = typeof remaining === 'number' ? 100 - remaining * boost / 1000
39
+ : typeof total === 'number' && total > 0 && typeof count === 'number' ? (1 - count / total) * 100 : undefined;
40
+ if (usedPercent === undefined || !Number.isFinite(usedPercent) || usedPercent < 0 || usedPercent > 100)
41
+ continue;
42
+ const start = row[startKey];
43
+ const end = row[endKey];
44
+ const validBounds = typeof start === 'number' && Number.isFinite(start) && start > 0
45
+ && typeof end === 'number' && Number.isFinite(end) && end > start;
46
+ windows.push({ kind: fallback === 'session' && validBounds && end - start !== 18_000_000 ? 'other' : fallback,
47
+ scope: row.model_name, usedPercent,
48
+ ...(validBounds ? { startsAt: start, resetsAt: end } : {}),
49
+ });
50
+ }
51
+ }
52
+ if (!windows.length)
53
+ throw new Error('MiniMax has no supported finite quota windows');
54
+ return { supported: true, plan: 'MiniMax', windows };
55
+ }
@@ -107,7 +107,7 @@ export class PoolUsageTracker {
107
107
  // every display surface — this is what was previously showing, so
108
108
  // keep showing it (even through a forced refresh: retrying past the
109
109
  // cooldown is exactly the retry storm `cooldownMs` exists to avoid).
110
- return entry.lastSnapshot;
110
+ return { ...entry.lastSnapshot, stale: true };
111
111
  }
112
112
  else {
113
113
  throw entry.error;
@@ -122,7 +122,7 @@ export class PoolUsageTracker {
122
122
  // snapshot was on record before it ran onto the new failure entry.
123
123
  const failed = this.entries.get(key);
124
124
  if (failed?.snapshot === undefined && failed?.lastSnapshot !== undefined)
125
- return failed.lastSnapshot;
125
+ return { ...failed.lastSnapshot, stale: true };
126
126
  throw error;
127
127
  }
128
128
  }
@@ -155,8 +155,10 @@ export class PoolUsageTracker {
155
155
  // consecutive failure, even though nothing newer ever replaced it.
156
156
  const prior = this.entries.get(key);
157
157
  const lastSnapshot = prior?.snapshot ?? prior?.lastSnapshot;
158
- pending = fetcher().then((snapshot) => {
159
- this.entries.set(key, { snapshot, at: Date.now() });
158
+ pending = fetcher().then((value) => {
159
+ const at = Date.now();
160
+ const snapshot = { ...value, observedAt: at, stale: false };
161
+ this.entries.set(key, { snapshot, at });
160
162
  return snapshot;
161
163
  }, (error) => {
162
164
  if (!isMissingOrInvalidCredential(error)) {
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "DSH Subscriptions",
4
+ "description": "Unified subscription providers and usage dashboard for DeepSeek Harness"
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "订阅中心",
4
+ "description": "将 ChatGPT、Claude、Grok 等订阅制模型接入 DeepSeek Harness,附带用量看板"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "dsh-subscriptions",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Unified subscription providers and usage dashboard for DeepSeek Harness",
5
+ "icon": "./icon.svg",
5
6
  "license": "MIT",
6
7
  "repository": {
7
8
  "type": "git",
@@ -43,12 +44,15 @@
43
44
  "types": "./vendor/cursor/index.d.ts",
44
45
  "default": "./vendor/cursor/index.js"
45
46
  },
46
- "./package.json": "./package.json"
47
+ "./package.json": "./package.json",
48
+ "./locale/*.json": "./locale/*.json"
47
49
  },
48
50
  "files": [
49
51
  "lib",
50
52
  "vendor/cursor",
51
- "cordis.patch.yml"
53
+ "cordis.patch.yml",
54
+ "locale/*.json",
55
+ "icon.svg"
52
56
  ],
53
57
  "dsh": {
54
58
  "bundle": {