@slkiser/opencode-quota 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/dist/data/modelsdev-pricing.min.json +504 -0
  4. package/dist/index.d.ts +10 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +15 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/lib/cache.d.ts +49 -0
  9. package/dist/lib/cache.d.ts.map +1 -0
  10. package/dist/lib/cache.js +127 -0
  11. package/dist/lib/cache.js.map +1 -0
  12. package/dist/lib/config.d.ts +30 -0
  13. package/dist/lib/config.d.ts.map +1 -0
  14. package/dist/lib/config.js +154 -0
  15. package/dist/lib/config.js.map +1 -0
  16. package/dist/lib/copilot.d.ts +25 -0
  17. package/dist/lib/copilot.d.ts.map +1 -0
  18. package/dist/lib/copilot.js +306 -0
  19. package/dist/lib/copilot.js.map +1 -0
  20. package/dist/lib/entries.d.ts +60 -0
  21. package/dist/lib/entries.d.ts.map +1 -0
  22. package/dist/lib/entries.js +8 -0
  23. package/dist/lib/entries.js.map +1 -0
  24. package/dist/lib/firmware.d.ts +15 -0
  25. package/dist/lib/firmware.d.ts.map +1 -0
  26. package/dist/lib/firmware.js +79 -0
  27. package/dist/lib/firmware.js.map +1 -0
  28. package/dist/lib/format.d.ts +16 -0
  29. package/dist/lib/format.d.ts.map +1 -0
  30. package/dist/lib/format.js +99 -0
  31. package/dist/lib/format.js.map +1 -0
  32. package/dist/lib/google-token-cache.d.ts +30 -0
  33. package/dist/lib/google-token-cache.d.ts.map +1 -0
  34. package/dist/lib/google-token-cache.js +100 -0
  35. package/dist/lib/google-token-cache.js.map +1 -0
  36. package/dist/lib/google.d.ts +43 -0
  37. package/dist/lib/google.d.ts.map +1 -0
  38. package/dist/lib/google.js +399 -0
  39. package/dist/lib/google.js.map +1 -0
  40. package/dist/lib/markdown-table.d.ts +8 -0
  41. package/dist/lib/markdown-table.d.ts.map +1 -0
  42. package/dist/lib/markdown-table.js +56 -0
  43. package/dist/lib/markdown-table.js.map +1 -0
  44. package/dist/lib/modelsdev-pricing.d.ts +23 -0
  45. package/dist/lib/modelsdev-pricing.d.ts.map +1 -0
  46. package/dist/lib/modelsdev-pricing.js +32 -0
  47. package/dist/lib/modelsdev-pricing.js.map +1 -0
  48. package/dist/lib/openai.d.ts +33 -0
  49. package/dist/lib/openai.d.ts.map +1 -0
  50. package/dist/lib/openai.js +162 -0
  51. package/dist/lib/openai.js.map +1 -0
  52. package/dist/lib/opencode-auth.d.ts +11 -0
  53. package/dist/lib/opencode-auth.d.ts.map +1 -0
  54. package/dist/lib/opencode-auth.js +27 -0
  55. package/dist/lib/opencode-auth.js.map +1 -0
  56. package/dist/lib/opencode-storage.d.ts +45 -0
  57. package/dist/lib/opencode-storage.d.ts.map +1 -0
  58. package/dist/lib/opencode-storage.js +120 -0
  59. package/dist/lib/opencode-storage.js.map +1 -0
  60. package/dist/lib/quota-command-format.d.ts +14 -0
  61. package/dist/lib/quota-command-format.d.ts.map +1 -0
  62. package/dist/lib/quota-command-format.js +122 -0
  63. package/dist/lib/quota-command-format.js.map +1 -0
  64. package/dist/lib/quota-stats-format.d.ts +9 -0
  65. package/dist/lib/quota-stats-format.d.ts.map +1 -0
  66. package/dist/lib/quota-stats-format.js +244 -0
  67. package/dist/lib/quota-stats-format.js.map +1 -0
  68. package/dist/lib/quota-stats.d.ts +71 -0
  69. package/dist/lib/quota-stats.d.ts.map +1 -0
  70. package/dist/lib/quota-stats.js +270 -0
  71. package/dist/lib/quota-stats.js.map +1 -0
  72. package/dist/lib/quota-status.d.ts +23 -0
  73. package/dist/lib/quota-status.d.ts.map +1 -0
  74. package/dist/lib/quota-status.js +112 -0
  75. package/dist/lib/quota-status.js.map +1 -0
  76. package/dist/lib/toast-format-grouped.d.ts +25 -0
  77. package/dist/lib/toast-format-grouped.d.ts.map +1 -0
  78. package/dist/lib/toast-format-grouped.js +123 -0
  79. package/dist/lib/toast-format-grouped.js.map +1 -0
  80. package/dist/lib/types.d.ts +218 -0
  81. package/dist/lib/types.d.ts.map +1 -0
  82. package/dist/lib/types.js +39 -0
  83. package/dist/lib/types.js.map +1 -0
  84. package/dist/plugin.d.ts +13 -0
  85. package/dist/plugin.d.ts.map +1 -0
  86. package/dist/plugin.js +633 -0
  87. package/dist/plugin.js.map +1 -0
  88. package/dist/providers/copilot.d.ts +8 -0
  89. package/dist/providers/copilot.d.ts.map +1 -0
  90. package/dist/providers/copilot.js +50 -0
  91. package/dist/providers/copilot.js.map +1 -0
  92. package/dist/providers/firmware.d.ts +6 -0
  93. package/dist/providers/firmware.d.ts.map +1 -0
  94. package/dist/providers/firmware.js +52 -0
  95. package/dist/providers/firmware.js.map +1 -0
  96. package/dist/providers/google-antigravity.d.ts +6 -0
  97. package/dist/providers/google-antigravity.d.ts.map +1 -0
  98. package/dist/providers/google-antigravity.js +77 -0
  99. package/dist/providers/google-antigravity.js.map +1 -0
  100. package/dist/providers/openai.d.ts +6 -0
  101. package/dist/providers/openai.d.ts.map +1 -0
  102. package/dist/providers/openai.js +106 -0
  103. package/dist/providers/openai.js.map +1 -0
  104. package/dist/providers/registry.d.ts +8 -0
  105. package/dist/providers/registry.d.ts.map +1 -0
  106. package/dist/providers/registry.js +14 -0
  107. package/dist/providers/registry.js.map +1 -0
  108. package/package.json +71 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copilot provider wrapper.
3
+ *
4
+ * Normalizes Copilot quota into generic toast entries.
5
+ */
6
+ import type { QuotaProvider } from "../lib/entries.js";
7
+ export declare const copilotProvider: QuotaProvider;
8
+ //# sourceMappingURL=copilot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copilot.d.ts","sourceRoot":"","sources":["../../src/providers/copilot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA6C,MAAM,mBAAmB,CAAC;AAGlG,eAAO,MAAM,eAAe,EAAE,aA8C7B,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Copilot provider wrapper.
3
+ *
4
+ * Normalizes Copilot quota into generic toast entries.
5
+ */
6
+ import { queryCopilotQuota } from "../lib/copilot.js";
7
+ export const copilotProvider = {
8
+ id: "copilot",
9
+ async isAvailable(ctx) {
10
+ try {
11
+ const resp = await ctx.client.config.providers();
12
+ const ids = new Set((resp.data?.providers ?? []).map((p) => p.id));
13
+ return ids.has("github-copilot");
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ },
19
+ matchesCurrentModel(model) {
20
+ const provider = model.split("/")[0]?.toLowerCase();
21
+ if (!provider)
22
+ return false;
23
+ return provider.includes("copilot") || provider.includes("github");
24
+ },
25
+ async fetch(_ctx) {
26
+ const result = await queryCopilotQuota();
27
+ if (!result) {
28
+ return { attempted: false, entries: [], errors: [] };
29
+ }
30
+ if (!result.success) {
31
+ return {
32
+ attempted: true,
33
+ entries: [],
34
+ errors: [{ label: "Copilot", message: result.error }],
35
+ };
36
+ }
37
+ return {
38
+ attempted: true,
39
+ entries: [
40
+ {
41
+ name: "Copilot",
42
+ percentRemaining: result.percentRemaining,
43
+ resetTimeIso: result.resetTimeIso,
44
+ },
45
+ ],
46
+ errors: [],
47
+ };
48
+ },
49
+ };
50
+ //# sourceMappingURL=copilot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copilot.js","sourceRoot":"","sources":["../../src/providers/copilot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,CAAC,MAAM,eAAe,GAAkB;IAC5C,EAAE,EAAE,SAAS;IAEb,KAAK,CAAC,WAAW,CAAC,GAAyB;QACzC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACjD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACnE,OAAO,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,mBAAmB,CAAC,KAAa;QAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACpD,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC5B,OAAO,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAA0B;QACpC,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAEzC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACvD,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;aACtD,CAAC;QACJ,CAAC;QAED,OAAO;YACL,SAAS,EAAE,IAAI;YACf,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,SAAS;oBACf,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;oBACzC,YAAY,EAAE,MAAM,CAAC,YAAY;iBAClC;aACF;YACD,MAAM,EAAE,EAAE;SACX,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Firmware AI provider wrapper.
3
+ */
4
+ import type { QuotaProvider } from "../lib/entries.js";
5
+ export declare const firmwareProvider: QuotaProvider;
6
+ //# sourceMappingURL=firmware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firmware.d.ts","sourceRoot":"","sources":["../../src/providers/firmware.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA6C,MAAM,mBAAmB,CAAC;AAGlG,eAAO,MAAM,gBAAgB,EAAE,aAkD9B,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Firmware AI provider wrapper.
3
+ */
4
+ import { hasFirmwareApiKeyConfigured, queryFirmwareQuota } from "../lib/firmware.js";
5
+ export const firmwareProvider = {
6
+ id: "firmware",
7
+ async isAvailable(ctx) {
8
+ // Best-effort: if OpenCode exposes a firmware provider, prefer that.
9
+ // Otherwise, fallback to local auth.json presence.
10
+ try {
11
+ const resp = await ctx.client.config.providers();
12
+ const ids = new Set((resp.data?.providers ?? []).map((p) => p.id));
13
+ if (ids.has("firmware") || ids.has("firmware-ai"))
14
+ return true;
15
+ }
16
+ catch {
17
+ // ignore
18
+ }
19
+ return await hasFirmwareApiKeyConfigured();
20
+ },
21
+ matchesCurrentModel(model) {
22
+ const provider = model.split("/")[0]?.toLowerCase();
23
+ if (!provider)
24
+ return false;
25
+ return provider.includes("firmware");
26
+ },
27
+ async fetch(_ctx) {
28
+ const result = await queryFirmwareQuota();
29
+ if (!result) {
30
+ return { attempted: false, entries: [], errors: [] };
31
+ }
32
+ if (!result.success) {
33
+ return {
34
+ attempted: true,
35
+ entries: [],
36
+ errors: [{ label: "Firmware", message: result.error }],
37
+ };
38
+ }
39
+ return {
40
+ attempted: true,
41
+ entries: [
42
+ {
43
+ name: "Firmware",
44
+ percentRemaining: result.percentRemaining,
45
+ resetTimeIso: result.resetTimeIso,
46
+ },
47
+ ],
48
+ errors: [],
49
+ };
50
+ },
51
+ };
52
+ //# sourceMappingURL=firmware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firmware.js","sourceRoot":"","sources":["../../src/providers/firmware.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAErF,MAAM,CAAC,MAAM,gBAAgB,GAAkB;IAC7C,EAAE,EAAE,UAAU;IAEd,KAAK,CAAC,WAAW,CAAC,GAAyB;QACzC,qEAAqE;QACrE,mDAAmD;QACnD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACjD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACnE,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC;gBAAE,OAAO,IAAI,CAAC;QACjE,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,OAAO,MAAM,2BAA2B,EAAE,CAAC;IAC7C,CAAC;IAED,mBAAmB,CAAC,KAAa;QAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;QACpD,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC5B,OAAO,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAA0B;QACpC,MAAM,MAAM,GAAG,MAAM,kBAAkB,EAAE,CAAC;QAE1C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACvD,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;aACvD,CAAC;QACJ,CAAC;QAED,OAAO;YACL,SAAS,EAAE,IAAI;YACf,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,UAAU;oBAChB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;oBACzC,YAAY,EAAE,MAAM,CAAC,YAAY;iBAClC;aACF;YACD,MAAM,EAAE,EAAE;SACX,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Google Antigravity provider wrapper.
3
+ */
4
+ import type { QuotaProvider } from "../lib/entries.js";
5
+ export declare const googleAntigravityProvider: QuotaProvider;
6
+ //# sourceMappingURL=google-antigravity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-antigravity.d.ts","sourceRoot":"","sources":["../../src/providers/google-antigravity.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,aAAa,EAId,MAAM,mBAAmB,CAAC;AAuB3B,eAAO,MAAM,yBAAyB,EAAE,aA2DvC,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Google Antigravity provider wrapper.
3
+ */
4
+ import { hasAntigravityAccountsConfigured, queryGoogleQuota } from "../lib/google.js";
5
+ function truncateEmail(email) {
6
+ if (!email)
7
+ return "Unknown";
8
+ const prefix = email.slice(0, 3);
9
+ return `${prefix}..gmail`;
10
+ }
11
+ function normalizeGoogleErrors(result) {
12
+ if (!result || !result.success || !result.errors || result.errors.length === 0)
13
+ return [];
14
+ return result.errors.map((e) => ({ label: truncateEmail(e.email), message: e.error }));
15
+ }
16
+ async function isAccountsConfigured() {
17
+ try {
18
+ return await hasAntigravityAccountsConfigured();
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ export const googleAntigravityProvider = {
25
+ id: "google-antigravity",
26
+ async isAvailable(ctx) {
27
+ try {
28
+ const resp = await ctx.client.config.providers();
29
+ const ids = new Set((resp.data?.providers ?? []).map((p) => p.id));
30
+ if (ids.has("google") || ids.has("antigravity"))
31
+ return true;
32
+ // Even if OpenCode doesn't report the provider (or uses different ids),
33
+ // the presence of the accounts file is enough to attempt quota.
34
+ return await isAccountsConfigured();
35
+ }
36
+ catch {
37
+ // Best-effort fallback: if accounts file exists, consider provider available.
38
+ return await isAccountsConfigured();
39
+ }
40
+ },
41
+ matchesCurrentModel(model) {
42
+ const provider = model.split("/")[0]?.toLowerCase();
43
+ if (!provider)
44
+ return false;
45
+ return (provider.includes("google") ||
46
+ provider.includes("antigravity") ||
47
+ provider.includes("opencode"));
48
+ },
49
+ async fetch(ctx) {
50
+ const modelIds = ctx.config.googleModels;
51
+ const result = await queryGoogleQuota(modelIds);
52
+ if (!result) {
53
+ return { attempted: false, entries: [], errors: [] };
54
+ }
55
+ if (!result.success) {
56
+ return {
57
+ attempted: true,
58
+ entries: [],
59
+ errors: [{ label: "Antigravity", message: result.error }],
60
+ };
61
+ }
62
+ const entries = result.models.map((m) => {
63
+ const emailLabel = truncateEmail(m.accountEmail) || "Antigravity";
64
+ return {
65
+ name: `${m.displayName} (${emailLabel})`,
66
+ percentRemaining: m.percentRemaining,
67
+ resetTimeIso: m.resetTimeIso,
68
+ };
69
+ });
70
+ return {
71
+ attempted: true,
72
+ entries,
73
+ errors: normalizeGoogleErrors(result),
74
+ };
75
+ },
76
+ };
77
+ //# sourceMappingURL=google-antigravity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-antigravity.js","sourceRoot":"","sources":["../../src/providers/google-antigravity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * OpenAI (Plus/Pro) provider wrapper.
3
+ */
4
+ import type { QuotaProvider } from "../lib/entries.js";
5
+ export declare const openaiProvider: QuotaProvider;
6
+ //# sourceMappingURL=openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA6C,MAAM,mBAAmB,CAAC;AAYlG,eAAO,MAAM,cAAc,EAAE,aAmH5B,CAAC"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * OpenAI (Plus/Pro) provider wrapper.
3
+ */
4
+ import { queryOpenAIQuota } from "../lib/openai.js";
5
+ export const openaiProvider = {
6
+ id: "openai",
7
+ async isAvailable(ctx) {
8
+ // Best-effort: if OpenCode exposes an openai provider, prefer that.
9
+ // Otherwise, this provider will still work if auth.json has openai oauth.
10
+ try {
11
+ const resp = await ctx.client.config.providers();
12
+ const ids = new Set((resp.data?.providers ?? []).map((p) => p.id));
13
+ return ids.has("openai") || ids.has("chatgpt") || ids.has("codex") || ids.has("opencode");
14
+ }
15
+ catch {
16
+ return true;
17
+ }
18
+ },
19
+ matchesCurrentModel(model) {
20
+ const provider = model.split("/")[0]?.toLowerCase();
21
+ if (!provider)
22
+ return false;
23
+ return (provider.includes("openai") || provider.includes("chatgpt") || provider.includes("codex"));
24
+ },
25
+ async fetch(_ctx) {
26
+ const result = await queryOpenAIQuota();
27
+ if (!result) {
28
+ return { attempted: false, entries: [], errors: [] };
29
+ }
30
+ if (!result.success) {
31
+ return {
32
+ attempted: true,
33
+ entries: [],
34
+ errors: [{ label: "OpenAI", message: result.error }],
35
+ };
36
+ }
37
+ const style = _ctx.config.toastStyle ?? "classic";
38
+ // Keep the classic toast behavior: show a single entry based on the worst remaining window.
39
+ if (style === "classic") {
40
+ const windows = [
41
+ result.windows.hourly && { name: "Hourly", ...result.windows.hourly },
42
+ result.windows.weekly && { name: "Weekly", ...result.windows.weekly },
43
+ result.windows.codeReview && { name: "Code Review", ...result.windows.codeReview },
44
+ ].filter(Boolean);
45
+ if (windows.length === 0) {
46
+ return {
47
+ attempted: true,
48
+ entries: [{ name: result.label, percentRemaining: 0 }],
49
+ errors: [],
50
+ };
51
+ }
52
+ windows.sort((a, b) => a.percentRemaining - b.percentRemaining);
53
+ const worst = windows[0];
54
+ return {
55
+ attempted: true,
56
+ entries: [
57
+ {
58
+ name: result.label,
59
+ percentRemaining: worst.percentRemaining,
60
+ resetTimeIso: worst.resetTimeIso,
61
+ },
62
+ ],
63
+ errors: [],
64
+ };
65
+ }
66
+ // Grouped style: expose all windows.
67
+ const entries = [];
68
+ const group = result.label;
69
+ const hourly = result.windows.hourly;
70
+ if (hourly) {
71
+ entries.push({
72
+ name: `${group} Hourly`,
73
+ group,
74
+ label: "Hourly:",
75
+ percentRemaining: hourly.percentRemaining,
76
+ resetTimeIso: hourly.resetTimeIso,
77
+ });
78
+ }
79
+ const weekly = result.windows.weekly;
80
+ if (weekly) {
81
+ entries.push({
82
+ name: `${group} Weekly`,
83
+ group,
84
+ label: "Weekly:",
85
+ percentRemaining: weekly.percentRemaining,
86
+ resetTimeIso: weekly.resetTimeIso,
87
+ });
88
+ }
89
+ const codeReview = result.windows.codeReview;
90
+ if (codeReview) {
91
+ entries.push({
92
+ name: `${group} Code Review`,
93
+ group,
94
+ label: "Code Review:",
95
+ percentRemaining: codeReview.percentRemaining,
96
+ resetTimeIso: codeReview.resetTimeIso,
97
+ });
98
+ }
99
+ return {
100
+ attempted: true,
101
+ entries,
102
+ errors: [],
103
+ };
104
+ },
105
+ };
106
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Provider registry.
3
+ *
4
+ * Add new providers here; everything else should stay provider-agnostic.
5
+ */
6
+ import type { QuotaProvider } from "../lib/entries.js";
7
+ export declare function getProviders(): QuotaProvider[];
8
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAMvD,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAG9C"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Provider registry.
3
+ *
4
+ * Add new providers here; everything else should stay provider-agnostic.
5
+ */
6
+ import { copilotProvider } from "./copilot.js";
7
+ import { openaiProvider } from "./openai.js";
8
+ import { googleAntigravityProvider } from "./google-antigravity.js";
9
+ import { firmwareProvider } from "./firmware.js";
10
+ export function getProviders() {
11
+ // Order here defines display ordering in the toast.
12
+ return [copilotProvider, openaiProvider, firmwareProvider, googleAntigravityProvider];
13
+ }
14
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,UAAU,YAAY;IAC1B,oDAAoD;IACpD,OAAO,CAAC,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,yBAAyB,CAAC,CAAC;AACxF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@slkiser/opencode-quota",
3
+ "version": "1.0.0",
4
+ "description": "Show quota status in OpenCode without LLM invocation. Toast + usage reports across providers.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc && node scripts/copy-data.mjs",
21
+ "typecheck": "tsc --noEmit",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "prepublishOnly": "npm run build",
25
+ "prepare": "husky"
26
+ },
27
+ "keywords": [
28
+ "opencode",
29
+ "plugin",
30
+ "quota",
31
+ "toast",
32
+ "github",
33
+ "copilot",
34
+ "google",
35
+ "antigravity",
36
+ "gemini",
37
+ "claude"
38
+ ],
39
+ "author": "",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/slkiser/opencode-quota.git"
44
+ },
45
+ "bugs": {
46
+ "url": "https://github.com/slkiser/opencode-quota/issues"
47
+ },
48
+ "homepage": "https://github.com/slkiser/opencode-quota#readme",
49
+ "devDependencies": {
50
+ "@opencode-ai/plugin": "^1.1.14",
51
+ "@types/node": "^22.0.0",
52
+ "husky": "^9.1.7",
53
+ "lint-staged": "^16.2.7",
54
+ "prettier": "^3.8.0",
55
+ "typescript": "^5.8.0",
56
+ "vitest": "^4.0.17",
57
+ "zod": "4.1.8"
58
+ },
59
+ "dependencies": {
60
+ "opencode-antigravity-auth": "^1.2.8"
61
+ },
62
+ "overrides": {
63
+ "zod": "4.1.8"
64
+ },
65
+ "peerDependencies": {
66
+ "@opencode-ai/plugin": "^1.1.0"
67
+ },
68
+ "engines": {
69
+ "node": ">=18.0.0"
70
+ }
71
+ }