@arandevcode/opencode-quota-extended 0.1.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 (97) hide show
  1. package/LICENSE +21 -0
  2. package/README-es.md +153 -0
  3. package/README.md +154 -0
  4. package/bin/quota.mjs +105 -0
  5. package/dist/config/defaults.d.ts +16 -0
  6. package/dist/config/defaults.d.ts.map +1 -0
  7. package/dist/config/defaults.js +15 -0
  8. package/dist/config/defaults.js.map +1 -0
  9. package/dist/config/index.html +26 -0
  10. package/dist/config/loader.d.ts +10 -0
  11. package/dist/config/loader.d.ts.map +1 -0
  12. package/dist/config/loader.js +89 -0
  13. package/dist/config/loader.js.map +1 -0
  14. package/dist/config/schema.d.ts +16 -0
  15. package/dist/config/schema.d.ts.map +1 -0
  16. package/dist/config/schema.js +28 -0
  17. package/dist/config/schema.js.map +1 -0
  18. package/dist/index.d.ts +14 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.html +29 -0
  21. package/dist/index.js +8 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/lib/auth.d.ts +45 -0
  24. package/dist/lib/auth.d.ts.map +1 -0
  25. package/dist/lib/auth.js +68 -0
  26. package/dist/lib/auth.js.map +1 -0
  27. package/dist/lib/http.d.ts +15 -0
  28. package/dist/lib/http.d.ts.map +1 -0
  29. package/dist/lib/http.js +30 -0
  30. package/dist/lib/http.js.map +1 -0
  31. package/dist/lib/index.html +46 -0
  32. package/dist/lib/opencode-go-config.d.ts +22 -0
  33. package/dist/lib/opencode-go-config.d.ts.map +1 -0
  34. package/dist/lib/opencode-go-config.js +92 -0
  35. package/dist/lib/opencode-go-config.js.map +1 -0
  36. package/dist/lib/reset-format.d.ts +9 -0
  37. package/dist/lib/reset-format.d.ts.map +1 -0
  38. package/dist/lib/reset-format.js +83 -0
  39. package/dist/lib/reset-format.js.map +1 -0
  40. package/dist/lib/sanitize.d.ts +10 -0
  41. package/dist/lib/sanitize.d.ts.map +1 -0
  42. package/dist/lib/sanitize.js +14 -0
  43. package/dist/lib/sanitize.js.map +1 -0
  44. package/dist/lib/throttle.d.ts +18 -0
  45. package/dist/lib/throttle.d.ts.map +1 -0
  46. package/dist/lib/throttle.js +26 -0
  47. package/dist/lib/throttle.js.map +1 -0
  48. package/dist/lib/toast-format.d.ts +24 -0
  49. package/dist/lib/toast-format.d.ts.map +1 -0
  50. package/dist/lib/toast-format.js +79 -0
  51. package/dist/lib/toast-format.js.map +1 -0
  52. package/dist/lib/types.d.ts +50 -0
  53. package/dist/lib/types.d.ts.map +1 -0
  54. package/dist/lib/types.js +10 -0
  55. package/dist/lib/types.js.map +1 -0
  56. package/dist/providers/copilot.d.ts +3 -0
  57. package/dist/providers/copilot.d.ts.map +1 -0
  58. package/dist/providers/copilot.js +303 -0
  59. package/dist/providers/copilot.js.map +1 -0
  60. package/dist/providers/index.html +34 -0
  61. package/dist/providers/openai.d.ts +10 -0
  62. package/dist/providers/openai.d.ts.map +1 -0
  63. package/dist/providers/openai.js +152 -0
  64. package/dist/providers/openai.js.map +1 -0
  65. package/dist/providers/opencode-go.d.ts +12 -0
  66. package/dist/providers/opencode-go.d.ts.map +1 -0
  67. package/dist/providers/opencode-go.js +102 -0
  68. package/dist/providers/opencode-go.js.map +1 -0
  69. package/dist/providers/registry.d.ts +20 -0
  70. package/dist/providers/registry.d.ts.map +1 -0
  71. package/dist/providers/registry.js +76 -0
  72. package/dist/providers/registry.js.map +1 -0
  73. package/dist/providers/types.d.ts +27 -0
  74. package/dist/providers/types.d.ts.map +1 -0
  75. package/dist/providers/types.js +2 -0
  76. package/dist/providers/types.js.map +1 -0
  77. package/dist/server.d.ts +10 -0
  78. package/dist/server.d.ts.map +1 -0
  79. package/dist/server.js +4 -0
  80. package/dist/server.js.map +1 -0
  81. package/dist/tui.d.ts +7 -0
  82. package/dist/tui.d.ts.map +1 -0
  83. package/dist/tui.tsx +121 -0
  84. package/dist/ui/SidebarPanel.d.ts +10 -0
  85. package/dist/ui/SidebarPanel.d.ts.map +1 -0
  86. package/dist/ui/SidebarPanel.tsx +36 -0
  87. package/dist/ui/cards.d.ts +13 -0
  88. package/dist/ui/cards.d.ts.map +1 -0
  89. package/dist/ui/cards.tsx +117 -0
  90. package/dist/ui/index.html +24 -0
  91. package/dist/ui/progress-bar.d.ts +27 -0
  92. package/dist/ui/progress-bar.d.ts.map +1 -0
  93. package/dist/ui/progress-bar.js +48 -0
  94. package/dist/ui/progress-bar.js.map +1 -0
  95. package/images/opencode_quota_extended_cli.png +0 -0
  96. package/images/opencode_quota_extended_sidebar.png +0 -0
  97. package/package.json +74 -0
@@ -0,0 +1,13 @@
1
+ import type { JSX } from "@opentui/solid";
2
+ import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui";
3
+ import type { ProviderQuota } from "../lib/types.js";
4
+ export declare function ProviderCards(props: {
5
+ api: {
6
+ theme: {
7
+ current: TuiThemeCurrent;
8
+ };
9
+ };
10
+ provider: ProviderQuota;
11
+ showOnlyMultiWindow: boolean;
12
+ }): JSX.Element;
13
+ //# sourceMappingURL=cards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cards.d.ts","sourceRoot":"","sources":["../../src/ui/cards.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,iBAAiB,CAAC;AAIlE,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,GAAG,EAAE;QAAE,KAAK,EAAE;YAAE,OAAO,EAAE,eAAe,CAAA;SAAE,CAAA;KAAE,CAAC;IAC7C,QAAQ,EAAE,aAAa,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,GAAG,GAAG,CAAC,OAAO,CA+Cd"}
@@ -0,0 +1,117 @@
1
+ /** @jsxImportSource @opentui/solid */
2
+ import { For } from "solid-js";
3
+ import { TextAttributes } from "@opentui/core";
4
+ import type { JSX } from "@opentui/solid";
5
+ import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui";
6
+ import type { ProviderQuota, QuotaWindow } from "../lib/types.js";
7
+ import { trafficLight } from "./progress-bar.js";
8
+ import { formatResetCombined } from "../lib/reset-format.js";
9
+
10
+ export function ProviderCards(props: {
11
+ api: { theme: { current: TuiThemeCurrent } };
12
+ provider: ProviderQuota;
13
+ showOnlyMultiWindow: boolean;
14
+ }): JSX.Element {
15
+ const theme = () => props.api.theme.current;
16
+ const muted = () => theme().textMuted;
17
+ const errorColor = () => theme().error;
18
+
19
+ if (props.provider.status === "skipped") return null;
20
+
21
+ if (props.provider.status === "no-auth") {
22
+ return (
23
+ <text fg={muted()}>
24
+ {props.provider.label}: no auth
25
+ </text>
26
+ );
27
+ }
28
+
29
+ if (props.provider.status === "error") {
30
+ return (
31
+ <text fg={errorColor()}>
32
+ {props.provider.label}: {props.provider.error ?? "error"}
33
+ </text>
34
+ );
35
+ }
36
+
37
+ if (props.provider.windows.length === 0) {
38
+ return (
39
+ <text fg={muted()}>
40
+ {props.provider.label}: no data
41
+ </text>
42
+ );
43
+ }
44
+
45
+ if (props.showOnlyMultiWindow && props.provider.windows.length === 1) {
46
+ return null;
47
+ }
48
+
49
+ return (
50
+ <box flexDirection="column" gap={0}>
51
+ <text fg={theme().text} attributes={TextAttributes.BOLD}>
52
+ {props.provider.label}
53
+ </text>
54
+ <box flexDirection="column" gap={1}>
55
+ <For each={props.provider.windows}>
56
+ {(window) => <WindowRow api={props.api} window={window} />}
57
+ </For>
58
+ </box>
59
+ </box>
60
+ );
61
+ }
62
+
63
+ function WindowRow(props: {
64
+ api: { theme: { current: TuiThemeCurrent } };
65
+ window: QuotaWindow;
66
+ }): JSX.Element {
67
+ const theme = () => props.api.theme.current;
68
+ const muted = () => theme().textMuted;
69
+ const textColor = () => theme().text;
70
+ const percent = () => props.window.percentRemaining;
71
+ const light = () => trafficLight(percent());
72
+ const barColor = () => {
73
+ if (light() === "ok") return "#6b8e23";
74
+ if (light() === "warning") return "#ffa500";
75
+ return "#ff6b6b";
76
+ };
77
+ const label = () => windowDisplayLabel(props.window);
78
+ const reset = () => formatReset(props.window.resetTimeIso);
79
+ const usedPct = () => {
80
+ const v = Math.round(100 - percent());
81
+ return v >= 0 ? v : 0;
82
+ };
83
+
84
+ return (
85
+ <box flexDirection="column" gap={0}>
86
+ <box flexDirection="row" gap={0} justifyContent="space-between">
87
+ <text>{label()}</text>
88
+ <text fg={light() === "error" ? barColor() : muted()}>{reset()}</text>
89
+ </box>
90
+ <box flexDirection="row" height={1}>
91
+ <box flexGrow={percent()} backgroundColor={barColor()} />
92
+ <box flexGrow={100 - percent()} backgroundColor={theme().backgroundElement} />
93
+ </box>
94
+ <box flexDirection="row" gap={0} justifyContent="flex-end">
95
+ <text fg={textColor()}>{`${usedPct()}% used / `}</text>
96
+ <text fg={barColor()}>{`${percent()}% left`}</text>
97
+ </box>
98
+ </box>
99
+ );
100
+ }
101
+
102
+ function windowDisplayLabel(window: QuotaWindow): string {
103
+ if (
104
+ typeof window.remaining === "number" &&
105
+ typeof window.total === "number" &&
106
+ Number.isFinite(window.remaining) &&
107
+ Number.isFinite(window.total)
108
+ ) {
109
+ return `${window.remaining}/${window.total}`;
110
+ }
111
+ return window.label;
112
+ }
113
+
114
+ function formatReset(iso: string | undefined): string {
115
+ const s = formatResetCombined(iso);
116
+ return s || "—";
117
+ }
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE HTML>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Directory listing for /opencode-personal-projects/opencode-quota-extended/dist/ui/</title>
6
+ </head>
7
+ <body>
8
+ <h1>Directory listing for /opencode-personal-projects/opencode-quota-extended/dist/ui/</h1>
9
+ <hr>
10
+ <ul>
11
+ <li><a href="cards.d.ts">cards.d.ts</a></li>
12
+ <li><a href="cards.d.ts.map">cards.d.ts.map</a></li>
13
+ <li><a href="cards.tsx">cards.tsx</a></li>
14
+ <li><a href="progress-bar.d.ts">progress-bar.d.ts</a></li>
15
+ <li><a href="progress-bar.d.ts.map">progress-bar.d.ts.map</a></li>
16
+ <li><a href="progress-bar.js">progress-bar.js</a></li>
17
+ <li><a href="progress-bar.js.map">progress-bar.js.map</a></li>
18
+ <li><a href="SidebarPanel.d.ts">SidebarPanel.d.ts</a></li>
19
+ <li><a href="SidebarPanel.d.ts.map">SidebarPanel.d.ts.map</a></li>
20
+ <li><a href="SidebarPanel.tsx">SidebarPanel.tsx</a></li>
21
+ </ul>
22
+ <hr>
23
+ </body>
24
+ </html>
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Build a visual progress bar using Unicode block characters.
3
+ *
4
+ * @param percentRemaining - Percent remaining (0-100), clamped internally
5
+ * @param width - Character width of the bar (default 12)
6
+ * @returns Filled (▓) and empty (░) strings of the appropriate length
7
+ */
8
+ export declare function renderProgressBar(percentRemaining: number, width?: number): {
9
+ filled: string;
10
+ empty: string;
11
+ width: number;
12
+ };
13
+ export type TrafficLight = "ok" | "warning" | "error";
14
+ /**
15
+ * Classify a percent-remaining value into a traffic-light state.
16
+ *
17
+ * - `ok` (≥50%): high quota remaining
18
+ * - `warning` (25–49%): moderate usage
19
+ * - `error` (<25%): nearly exhausted
20
+ */
21
+ export declare function trafficLight(percentRemaining: number): TrafficLight;
22
+ /**
23
+ * Map a traffic-light state to the corresponding TuiThemeCurrent color key.
24
+ */
25
+ export declare function trafficLightColorKey(light: TrafficLight): "accent" | "warning" | "error";
26
+ export declare function trafficLightIndicator(light: TrafficLight): string;
27
+ //# sourceMappingURL=progress-bar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-bar.d.ts","sourceRoot":"","sources":["../../src/ui/progress-bar.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAQA;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,CAInE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAIxF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAIjE"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Build a visual progress bar using Unicode block characters.
3
+ *
4
+ * @param percentRemaining - Percent remaining (0-100), clamped internally
5
+ * @param width - Character width of the bar (default 12)
6
+ * @returns Filled (▓) and empty (░) strings of the appropriate length
7
+ */
8
+ export function renderProgressBar(percentRemaining, width = 12) {
9
+ const clamped = Math.max(0, Math.min(100, percentRemaining));
10
+ const filledCount = Math.max(0, Math.min(width, Math.round((clamped / 100) * width)));
11
+ return {
12
+ filled: "▓".repeat(filledCount),
13
+ empty: "░".repeat(width - filledCount),
14
+ width,
15
+ };
16
+ }
17
+ /**
18
+ * Classify a percent-remaining value into a traffic-light state.
19
+ *
20
+ * - `ok` (≥50%): high quota remaining
21
+ * - `warning` (25–49%): moderate usage
22
+ * - `error` (<25%): nearly exhausted
23
+ */
24
+ export function trafficLight(percentRemaining) {
25
+ if (percentRemaining >= 50)
26
+ return "ok";
27
+ if (percentRemaining >= 25)
28
+ return "warning";
29
+ return "error";
30
+ }
31
+ /**
32
+ * Map a traffic-light state to the corresponding TuiThemeCurrent color key.
33
+ */
34
+ export function trafficLightColorKey(light) {
35
+ if (light === "ok")
36
+ return "accent";
37
+ if (light === "warning")
38
+ return "warning";
39
+ return "error";
40
+ }
41
+ export function trafficLightIndicator(light) {
42
+ if (light === "ok")
43
+ return "●";
44
+ if (light === "warning")
45
+ return "●";
46
+ return "●";
47
+ }
48
+ //# sourceMappingURL=progress-bar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-bar.js","sourceRoot":"","sources":["../../src/ui/progress-bar.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,gBAAwB,EAAE,QAAgB,EAAE;IAK5E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtF,OAAO;QACL,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;QAC/B,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,WAAW,CAAC;QACtC,KAAK;KACN,CAAC;AACJ,CAAC;AAID;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,gBAAwB;IACnD,IAAI,gBAAgB,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,gBAAgB,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IAC7C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IACtD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IACpC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAmB;IACvD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,GAAG,CAAC;IAC/B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IACpC,OAAO,GAAG,CAAC;AACb,CAAC"}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@arandevcode/opencode-quota-extended",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode plugin: quota usage cards with progress bars (5h / weekly / monthly) for OpenAI, GitHub Copilot and OpenCode Go in the TUI sidebar.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./server": {
14
+ "types": "./dist/server.d.ts",
15
+ "default": "./dist/server.js"
16
+ },
17
+ "./tui": {
18
+ "types": "./dist/tui.d.ts",
19
+ "default": "./dist/tui.tsx"
20
+ }
21
+ },
22
+ "bin": {
23
+ "opencode-quota": "./bin/quota.mjs"
24
+ },
25
+ "oc-plugin": [
26
+ "server",
27
+ "tui"
28
+ ],
29
+ "files": [
30
+ "dist",
31
+ "bin",
32
+ "images",
33
+ "README.md",
34
+ "README-es.md",
35
+ "LICENSE"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc && node scripts/postbuild.mjs",
39
+ "prepublish": "tsc && node scripts/postbuild.mjs",
40
+ "build:watch": "tsc --watch",
41
+ "typecheck": "tsc --noEmit",
42
+ "quota": "node bin/quota.mjs",
43
+ "setup": "node scripts/setup.mjs",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest"
46
+ },
47
+ "dependencies": {
48
+ "xdg-basedir": "^5.1.0"
49
+ },
50
+ "peerDependencies": {
51
+ "@opencode-ai/plugin": "^1.4.3",
52
+ "@opentui/core": "^0.4.1",
53
+ "@opentui/solid": "^0.4.1",
54
+ "solid-js": "^1.9.12"
55
+ },
56
+ "devDependencies": {
57
+ "@opencode-ai/plugin": "^1.15.13",
58
+ "@opentui/core": "^0.4.1",
59
+ "@opentui/solid": "^0.4.1",
60
+ "@types/node": "^22.0.0",
61
+ "solid-js": "^1.9.12",
62
+ "typescript": "^5.8.0",
63
+ "vitest": "^4.0.17",
64
+ "zod": "^4.1.8"
65
+ },
66
+ "engines": {
67
+ "node": ">=18.0.0"
68
+ },
69
+ "publishConfig": {
70
+ "access": "public"
71
+ },
72
+ "author": "arandevcode",
73
+ "license": "MIT"
74
+ }