opencode-account-manager 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardView.d.ts","sourceRoot":"","sources":["../../../src/tui/components/DashboardView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAoED,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,kBAAkB,qBAyG5E"}
1
+ {"version":3,"file":"DashboardView.d.ts","sourceRoot":"","sources":["../../../src/tui/components/DashboardView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAgID,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,kBAAkB,qBAmI5E"}
@@ -6,52 +6,98 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DashboardView = DashboardView;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const ink_1 = require("ink");
9
+ // Model definitions with display names
10
+ const MODEL_CONFIGS = [
11
+ { key: "claude", displayName: "Claude", shortName: "Claude" },
12
+ { key: "gemini", displayName: "Gemini Pro", shortName: "G Pro" },
13
+ { key: "gemini-cli:gemini-3-flash-preview", displayName: "Gemini Flash", shortName: "G Flash" },
14
+ { key: "gemini-cli:gemini-3-pro-preview", displayName: "Gemini Pro", shortName: "G Pro" },
15
+ { key: "gemini-cli:gemini-2.5-pro", displayName: "G 2.5 Pro", shortName: "G2.5P" },
16
+ { key: "gemini-cli:imagen-3", displayName: "Imagen 3", shortName: "Img3" },
17
+ ];
9
18
  function formatTimeRemaining(resetTime) {
10
19
  const now = Date.now();
11
20
  if (resetTime <= now)
12
- return "";
21
+ return "0h 0m";
13
22
  const remaining = resetTime - now;
14
23
  const hours = Math.floor(remaining / 3600000);
15
24
  const minutes = Math.floor((remaining % 3600000) / 60000);
16
- if (hours > 24) {
25
+ if (hours >= 24) {
17
26
  const days = Math.floor(hours / 24);
18
27
  return `${days}d ${hours % 24}h`;
19
28
  }
20
- if (hours > 0) {
21
- return `${hours}h ${minutes}m`;
22
- }
23
- return `${minutes}m`;
29
+ return `${hours}h ${minutes}m`;
24
30
  }
25
- function getModelDisplayName(model) {
26
- // Shorten long model names for display
27
- if (model.includes(":")) {
28
- const parts = model.split(":");
29
- return parts[1] || parts[0];
30
- }
31
- return model;
31
+ function calculatePercentage(resetTime) {
32
+ const now = Date.now();
33
+ if (resetTime <= now)
34
+ return 100;
35
+ const remaining = resetTime - now;
36
+ const maxTime = 5 * 3600000; // 5 hours as max reference
37
+ const elapsed = maxTime - remaining;
38
+ const pct = Math.max(0, Math.min(100, Math.round((elapsed / maxTime) * 100)));
39
+ return pct;
32
40
  }
33
- function getAccountRateLimits(account) {
41
+ function getModelStatus(account, modelKey) {
34
42
  const now = Date.now();
35
43
  const rateLimits = account.rateLimitResetTimes || {};
36
- return Object.entries(rateLimits)
37
- .filter(([_, time]) => time > now)
38
- .map(([model, resetTime]) => ({
39
- model,
40
- displayName: getModelDisplayName(model),
41
- resetTime,
44
+ // Check if model is rate limited
45
+ const resetTime = rateLimits[modelKey] || 0;
46
+ if (resetTime <= now) {
47
+ return {
48
+ available: true,
49
+ timeRemaining: "0h 0m",
50
+ percentage: 100,
51
+ resetTime: 0,
52
+ };
53
+ }
54
+ return {
55
+ available: false,
42
56
  timeRemaining: formatTimeRemaining(resetTime),
43
- }))
44
- .sort((a, b) => a.resetTime - b.resetTime);
57
+ percentage: calculatePercentage(resetTime),
58
+ resetTime,
59
+ };
45
60
  }
46
- function getAccountStatus(account) {
47
- if (account.enabled === false) {
48
- return { status: "disabled", statusColor: "gray", indicator: "○" };
49
- }
50
- const limits = getAccountRateLimits(account);
51
- if (limits.length === 0) {
52
- return { status: "available", statusColor: "white", indicator: "●" };
61
+ function getActiveModels(accounts) {
62
+ const now = Date.now();
63
+ const activeKeys = new Set();
64
+ // Collect all model keys that have rate limit data
65
+ accounts.forEach(acc => {
66
+ if (acc.rateLimitResetTimes) {
67
+ Object.keys(acc.rateLimitResetTimes).forEach(key => {
68
+ activeKeys.add(key);
69
+ });
70
+ }
71
+ });
72
+ // Return models that are in our config OR have rate limit data
73
+ const models = MODEL_CONFIGS.filter(m => activeKeys.has(m.key));
74
+ // If no models found, show default ones
75
+ if (models.length === 0) {
76
+ return MODEL_CONFIGS.slice(0, 2); // Claude and Gemini
53
77
  }
54
- return { status: "limited", statusColor: "gray", indicator: "◐" };
78
+ return models;
79
+ }
80
+ // Progress bar component using text
81
+ function ProgressBar({ percentage, width = 8 }) {
82
+ const filled = Math.round((percentage / 100) * width);
83
+ const empty = width - filled;
84
+ // Color based on percentage
85
+ const color = percentage === 100 ? "white" : percentage >= 50 ? "gray" : "gray";
86
+ return (react_1.default.createElement(ink_1.Text, { color: color },
87
+ "█".repeat(filled),
88
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, "░".repeat(empty))));
89
+ }
90
+ // Model cell component
91
+ function ModelCell({ status, width = 18 }) {
92
+ return (react_1.default.createElement(ink_1.Box, { width: width },
93
+ react_1.default.createElement(ink_1.Box, { width: 8 },
94
+ react_1.default.createElement(ProgressBar, { percentage: status.percentage, width: 6 })),
95
+ react_1.default.createElement(ink_1.Box, { width: 6 },
96
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, status.timeRemaining.padStart(5))),
97
+ react_1.default.createElement(ink_1.Box, { width: 4 },
98
+ react_1.default.createElement(ink_1.Text, { color: status.percentage === 100 ? "white" : "gray" },
99
+ String(status.percentage).padStart(3),
100
+ "%"))));
55
101
  }
56
102
  function DashboardView({ accounts, selectedIndex }) {
57
103
  if (accounts.length === 0) {
@@ -59,51 +105,72 @@ function DashboardView({ accounts, selectedIndex }) {
59
105
  react_1.default.createElement(ink_1.Text, { dimColor: true }, "No accounts configured."),
60
106
  react_1.default.createElement(ink_1.Text, { dimColor: true }, "Press P to open actions and import accounts.")));
61
107
  }
108
+ const activeModels = getActiveModels(accounts);
109
+ const modelColumnWidth = 18;
110
+ const emailWidth = 28;
62
111
  return (react_1.default.createElement(ink_1.Box, { flexDirection: "column" },
63
- react_1.default.createElement(ink_1.Box, { paddingX: 1, marginBottom: 1 },
112
+ react_1.default.createElement(ink_1.Box, { paddingX: 1 },
64
113
  react_1.default.createElement(ink_1.Box, { width: 3 },
65
114
  react_1.default.createElement(ink_1.Text, { dimColor: true }, " ")),
66
- react_1.default.createElement(ink_1.Box, { width: 30 },
115
+ react_1.default.createElement(ink_1.Box, { width: emailWidth },
67
116
  react_1.default.createElement(ink_1.Text, { dimColor: true, bold: true }, "EMAIL")),
68
- react_1.default.createElement(ink_1.Box, { width: 10 },
69
- react_1.default.createElement(ink_1.Text, { dimColor: true, bold: true }, "STATUS")),
70
- react_1.default.createElement(ink_1.Box, { flexGrow: 1 },
71
- react_1.default.createElement(ink_1.Text, { dimColor: true, bold: true }, "RATE LIMITS"))),
117
+ activeModels.map(model => (react_1.default.createElement(ink_1.Box, { key: model.key, width: modelColumnWidth },
118
+ react_1.default.createElement(ink_1.Text, { dimColor: true, bold: true }, model.shortName))))),
119
+ react_1.default.createElement(ink_1.Box, { paddingX: 1 },
120
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, "─".repeat(3 + emailWidth + activeModels.length * modelColumnWidth))),
72
121
  accounts.map((account, index) => {
73
122
  const isSelected = index === selectedIndex;
74
- const { status, statusColor, indicator } = getAccountStatus(account);
75
- const limits = getAccountRateLimits(account);
123
+ const isDisabled = account.enabled === false;
76
124
  // Truncate email
77
- const email = account.email.length > 28
78
- ? account.email.slice(0, 25) + "..."
125
+ const email = account.email.length > emailWidth - 3
126
+ ? account.email.slice(0, emailWidth - 6) + "..."
79
127
  : account.email;
80
128
  return (react_1.default.createElement(ink_1.Box, { key: account.email, paddingX: 1 },
81
129
  react_1.default.createElement(ink_1.Box, { width: 3 },
82
- react_1.default.createElement(ink_1.Text, { color: isSelected ? "cyan" : undefined, bold: isSelected }, isSelected ? "› " : " ")),
83
- react_1.default.createElement(ink_1.Box, { width: 30 },
84
- react_1.default.createElement(ink_1.Text, { color: isSelected ? "cyan" : (account.enabled === false ? "gray" : "white"), bold: isSelected, dimColor: account.enabled === false }, email)),
85
- react_1.default.createElement(ink_1.Box, { width: 10 },
86
- react_1.default.createElement(ink_1.Text, { color: statusColor },
87
- indicator,
88
- " ",
89
- status)),
90
- react_1.default.createElement(ink_1.Box, { flexGrow: 1 }, limits.length === 0 ? (react_1.default.createElement(ink_1.Text, { dimColor: true }, "\u2014")) : (react_1.default.createElement(ink_1.Text, null, limits.map((limit, i) => (react_1.default.createElement(ink_1.Text, { key: limit.model },
91
- react_1.default.createElement(ink_1.Text, { dimColor: true }, limit.displayName),
92
- react_1.default.createElement(ink_1.Text, { color: "gray" }, ":"),
93
- react_1.default.createElement(ink_1.Text, { color: "white" }, limit.timeRemaining),
94
- i < limits.length - 1 ? react_1.default.createElement(ink_1.Text, { dimColor: true }, " \u2502 ") : null))))))));
130
+ react_1.default.createElement(ink_1.Text, { bold: isSelected }, isSelected ? "› " : " ")),
131
+ react_1.default.createElement(ink_1.Box, { width: emailWidth },
132
+ react_1.default.createElement(ink_1.Text, { bold: isSelected, dimColor: isDisabled }, email)),
133
+ activeModels.map(model => {
134
+ const status = getModelStatus(account, model.key);
135
+ if (isDisabled) {
136
+ return (react_1.default.createElement(ink_1.Box, { key: model.key, width: modelColumnWidth },
137
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, "\u2500\u2500 disabled \u2500\u2500")));
138
+ }
139
+ return (react_1.default.createElement(ModelCell, { key: model.key, status: status, width: modelColumnWidth }));
140
+ })));
95
141
  }),
96
142
  react_1.default.createElement(ink_1.Box, { paddingX: 1, marginTop: 1 },
97
- react_1.default.createElement(ink_1.Text, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),
143
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, "".repeat(3 + emailWidth + activeModels.length * modelColumnWidth))),
98
144
  react_1.default.createElement(ink_1.Box, { paddingX: 1 },
99
145
  react_1.default.createElement(ink_1.Text, { dimColor: true },
100
146
  accounts.length,
101
- " accounts \u2022 ",
102
- accounts.filter(a => a.enabled !== false && getAccountRateLimits(a).length === 0).length,
103
- " available \u2022 ",
104
- accounts.filter(a => getAccountRateLimits(a).length > 0).length,
105
- " limited")),
147
+ " accounts"),
148
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, " \u2502 "),
149
+ react_1.default.createElement(ink_1.Text, null, accounts.filter(a => {
150
+ if (a.enabled === false)
151
+ return false;
152
+ const now = Date.now();
153
+ const limits = a.rateLimitResetTimes || {};
154
+ return !Object.values(limits).some(t => t > now);
155
+ }).length),
156
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, " available"),
157
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, " \u2502 "),
158
+ react_1.default.createElement(ink_1.Text, { dimColor: true },
159
+ accounts.filter(a => {
160
+ if (a.enabled === false)
161
+ return false;
162
+ const now = Date.now();
163
+ const limits = a.rateLimitResetTimes || {};
164
+ return Object.values(limits).some(t => t > now);
165
+ }).length,
166
+ " limited"),
167
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, " \u2502 "),
168
+ react_1.default.createElement(ink_1.Text, { dimColor: true },
169
+ accounts.filter(a => a.enabled === false).length,
170
+ " disabled")),
106
171
  react_1.default.createElement(ink_1.Box, { paddingX: 1, marginTop: 1 },
107
- react_1.default.createElement(ink_1.Text, { dimColor: true }, "\u25CF available \u25D0 limited \u25CB disabled"))));
172
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, "\u2588\u2588\u2588\u2588 100%"),
173
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, " \u2591\u2591\u2591\u2591 limited "),
174
+ react_1.default.createElement(ink_1.Text, { dimColor: true }, "\u2500\u2500 disabled"))));
108
175
  }
109
176
  //# sourceMappingURL=DashboardView.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardView.js","sourceRoot":"","sources":["../../../src/tui/components/DashboardView.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DashboardView.js","sourceRoot":"","sources":["../../../src/tui/components/DashboardView.tsx"],"names":[],"mappings":"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"}
@@ -9,8 +9,8 @@ const ink_1 = require("ink");
9
9
  function SectionBox({ title, children, borderColor = "gray", collapsed = false }) {
10
10
  return (react_1.default.createElement(ink_1.Box, { flexDirection: "column", borderStyle: "round", borderColor: borderColor, marginBottom: 1 },
11
11
  react_1.default.createElement(ink_1.Box, { paddingX: 1 },
12
- react_1.default.createElement(ink_1.Text, { bold: true, color: "white" }, title),
13
- collapsed && react_1.default.createElement(ink_1.Text, { dimColor: true }, " (collapsed)")),
14
- !collapsed && (react_1.default.createElement(ink_1.Box, { flexDirection: "column", paddingY: 0 }, children))));
12
+ react_1.default.createElement(ink_1.Text, { bold: true }, title),
13
+ collapsed ? react_1.default.createElement(ink_1.Text, { dimColor: true }, " (collapsed)") : null),
14
+ !collapsed ? (react_1.default.createElement(ink_1.Box, { flexDirection: "column", paddingY: 0 }, children)) : null));
15
15
  }
16
16
  //# sourceMappingURL=SectionBox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SectionBox.js","sourceRoot":"","sources":["../../../src/tui/components/SectionBox.tsx"],"names":[],"mappings":";;;;;AAUA,gCAwBC;AAlCD,kDAA0B;AAC1B,6BAAgC;AAShC,SAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,WAAW,GAAG,MAAM,EACpB,SAAS,GAAG,KAAK,EACD;IAChB,OAAO,CACL,8BAAC,SAAG,IACF,aAAa,EAAC,QAAQ,EACtB,WAAW,EAAC,OAAO,EACnB,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,CAAC;QAEf,8BAAC,SAAG,IAAC,QAAQ,EAAE,CAAC;YACd,8BAAC,UAAI,IAAC,IAAI,QAAC,KAAK,EAAC,OAAO,IAAE,KAAK,CAAQ;YACtC,SAAS,IAAI,8BAAC,UAAI,IAAC,QAAQ,yBAAoB,CAC5C;QACL,CAAC,SAAS,IAAI,CACb,8BAAC,SAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,IACpC,QAAQ,CACL,CACP,CACG,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"SectionBox.js","sourceRoot":"","sources":["../../../src/tui/components/SectionBox.tsx"],"names":[],"mappings":";;;;;AAUA,gCAwBC;AAlCD,kDAA0B;AAC1B,6BAAgC;AAShC,SAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,WAAW,GAAG,MAAM,EACpB,SAAS,GAAG,KAAK,EACD;IAChB,OAAO,CACL,8BAAC,SAAG,IACF,aAAa,EAAC,QAAQ,EACtB,WAAW,EAAC,OAAO,EACnB,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,CAAC;QAEf,8BAAC,SAAG,IAAC,QAAQ,EAAE,CAAC;YACd,8BAAC,UAAI,IAAC,IAAI,UAAE,KAAK,CAAQ;YACxB,SAAS,CAAC,CAAC,CAAC,8BAAC,UAAI,IAAC,QAAQ,yBAAoB,CAAC,CAAC,CAAC,IAAI,CAClD;QACL,CAAC,SAAS,CAAC,CAAC,CAAC,CACZ,8BAAC,SAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,IACpC,QAAQ,CACL,CACP,CAAC,CAAC,CAAC,IAAI,CACJ,CACP,CAAC;AACJ,CAAC"}
@@ -7,15 +7,15 @@ exports.StatusBadge = StatusBadge;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const ink_1 = require("ink");
9
9
  const STATUS_COLORS = {
10
- available: "green",
11
- limited: "yellow",
10
+ available: "white",
11
+ limited: "gray",
12
12
  disabled: "gray",
13
- error: "red",
13
+ error: "gray",
14
14
  };
15
15
  const STATUS_ICONS = {
16
16
  available: "●",
17
- limited: "",
18
- disabled: "x",
17
+ limited: "",
18
+ disabled: "",
19
19
  error: "!",
20
20
  };
21
21
  function StatusBadge({ status, label }) {
@@ -1 +1 @@
1
- {"version":3,"file":"StatusBadge.js","sourceRoot":"","sources":["../../../src/tui/components/StatusBadge.tsx"],"names":[],"mappings":";;;;;AAsBA,kCAUC;AAhCD,kDAA0B;AAC1B,6BAA2B;AAO3B,MAAM,aAAa,GAA2B;IAC5C,SAAS,EAAE,OAAO;IAClB,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,MAAM;IAChB,KAAK,EAAE,KAAK;CACb,CAAC;AAEF,MAAM,YAAY,GAA2B;IAC3C,SAAS,EAAE,GAAG;IACd,OAAO,EAAE,GAAG;IACZ,QAAQ,EAAE,GAAG;IACb,KAAK,EAAE,GAAG;CACX,CAAC;AAEF,SAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAoB;IAC7D,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;IAC9C,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;IACzC,MAAM,IAAI,GAAG,KAAK,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;IAE3C,OAAO,CACL,8BAAC,UAAI,IAAC,KAAK,EAAE,KAAK;QACf,IAAI;;QAAG,IAAI,CACP,CACR,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"StatusBadge.js","sourceRoot":"","sources":["../../../src/tui/components/StatusBadge.tsx"],"names":[],"mappings":";;;;;AAsBA,kCAUC;AAhCD,kDAA0B;AAC1B,6BAA2B;AAO3B,MAAM,aAAa,GAA2B;IAC5C,SAAS,EAAE,OAAO;IAClB,OAAO,EAAE,MAAM;IACf,QAAQ,EAAE,MAAM;IAChB,KAAK,EAAE,MAAM;CACd,CAAC;AAEF,MAAM,YAAY,GAA2B;IAC3C,SAAS,EAAE,GAAG;IACd,OAAO,EAAE,GAAG;IACZ,QAAQ,EAAE,GAAG;IACb,KAAK,EAAE,GAAG;CACX,CAAC;AAEF,SAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAoB;IAC7D,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;IAC9C,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;IACzC,MAAM,IAAI,GAAG,KAAK,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;IAE3C,OAAO,CACL,8BAAC,UAAI,IAAC,KAAK,EAAE,KAAK;QACf,IAAI;;QAAG,IAAI,CACP,CACR,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-account-manager",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "TUI Dashboard for OpenCode - View providers, MCP servers, and plugin accounts",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -7,70 +7,130 @@ interface DashboardViewProps {
7
7
  selectedIndex: number;
8
8
  }
9
9
 
10
- interface RateLimitInfo {
11
- model: string;
12
- displayName: string;
13
- resetTime: number;
10
+ // Model definitions with display names
11
+ const MODEL_CONFIGS = [
12
+ { key: "claude", displayName: "Claude", shortName: "Claude" },
13
+ { key: "gemini", displayName: "Gemini Pro", shortName: "G Pro" },
14
+ { key: "gemini-cli:gemini-3-flash-preview", displayName: "Gemini Flash", shortName: "G Flash" },
15
+ { key: "gemini-cli:gemini-3-pro-preview", displayName: "Gemini Pro", shortName: "G Pro" },
16
+ { key: "gemini-cli:gemini-2.5-pro", displayName: "G 2.5 Pro", shortName: "G2.5P" },
17
+ { key: "gemini-cli:imagen-3", displayName: "Imagen 3", shortName: "Img3" },
18
+ ];
19
+
20
+ interface ModelStatus {
21
+ available: boolean;
14
22
  timeRemaining: string;
23
+ percentage: number;
24
+ resetTime: number;
15
25
  }
16
26
 
17
27
  function formatTimeRemaining(resetTime: number): string {
18
28
  const now = Date.now();
19
- if (resetTime <= now) return "";
29
+ if (resetTime <= now) return "0h 0m";
20
30
 
21
31
  const remaining = resetTime - now;
22
32
  const hours = Math.floor(remaining / 3600000);
23
33
  const minutes = Math.floor((remaining % 3600000) / 60000);
24
34
 
25
- if (hours > 24) {
35
+ if (hours >= 24) {
26
36
  const days = Math.floor(hours / 24);
27
37
  return `${days}d ${hours % 24}h`;
28
38
  }
29
- if (hours > 0) {
30
- return `${hours}h ${minutes}m`;
31
- }
32
- return `${minutes}m`;
39
+ return `${hours}h ${minutes}m`;
33
40
  }
34
41
 
35
- function getModelDisplayName(model: string): string {
36
- // Shorten long model names for display
37
- if (model.includes(":")) {
38
- const parts = model.split(":");
39
- return parts[1] || parts[0];
40
- }
41
- return model;
42
+ function calculatePercentage(resetTime: number): number {
43
+ const now = Date.now();
44
+ if (resetTime <= now) return 100;
45
+
46
+ const remaining = resetTime - now;
47
+ const maxTime = 5 * 3600000; // 5 hours as max reference
48
+ const elapsed = maxTime - remaining;
49
+ const pct = Math.max(0, Math.min(100, Math.round((elapsed / maxTime) * 100)));
50
+ return pct;
42
51
  }
43
52
 
44
- function getAccountRateLimits(account: Account): RateLimitInfo[] {
53
+ function getModelStatus(account: Account, modelKey: string): ModelStatus {
45
54
  const now = Date.now();
46
55
  const rateLimits = account.rateLimitResetTimes || {};
47
56
 
48
- return Object.entries(rateLimits)
49
- .filter(([_, time]) => time > now)
50
- .map(([model, resetTime]) => ({
51
- model,
52
- displayName: getModelDisplayName(model),
53
- resetTime,
54
- timeRemaining: formatTimeRemaining(resetTime),
55
- }))
56
- .sort((a, b) => a.resetTime - b.resetTime);
57
+ // Check if model is rate limited
58
+ const resetTime = rateLimits[modelKey] || 0;
59
+
60
+ if (resetTime <= now) {
61
+ return {
62
+ available: true,
63
+ timeRemaining: "0h 0m",
64
+ percentage: 100,
65
+ resetTime: 0,
66
+ };
67
+ }
68
+
69
+ return {
70
+ available: false,
71
+ timeRemaining: formatTimeRemaining(resetTime),
72
+ percentage: calculatePercentage(resetTime),
73
+ resetTime,
74
+ };
57
75
  }
58
76
 
59
- function getAccountStatus(account: Account): {
60
- status: string;
61
- statusColor: string;
62
- indicator: string;
63
- } {
64
- if (account.enabled === false) {
65
- return { status: "disabled", statusColor: "gray", indicator: "○" };
66
- }
77
+ function getActiveModels(accounts: Account[]): typeof MODEL_CONFIGS {
78
+ const now = Date.now();
79
+ const activeKeys = new Set<string>();
67
80
 
68
- const limits = getAccountRateLimits(account);
69
- if (limits.length === 0) {
70
- return { status: "available", statusColor: "white", indicator: "●" };
81
+ // Collect all model keys that have rate limit data
82
+ accounts.forEach(acc => {
83
+ if (acc.rateLimitResetTimes) {
84
+ Object.keys(acc.rateLimitResetTimes).forEach(key => {
85
+ activeKeys.add(key);
86
+ });
87
+ }
88
+ });
89
+
90
+ // Return models that are in our config OR have rate limit data
91
+ const models = MODEL_CONFIGS.filter(m => activeKeys.has(m.key));
92
+
93
+ // If no models found, show default ones
94
+ if (models.length === 0) {
95
+ return MODEL_CONFIGS.slice(0, 2); // Claude and Gemini
71
96
  }
72
97
 
73
- return { status: "limited", statusColor: "gray", indicator: "◐" };
98
+ return models;
99
+ }
100
+
101
+ // Progress bar component using text
102
+ function ProgressBar({ percentage, width = 8 }: { percentage: number; width?: number }) {
103
+ const filled = Math.round((percentage / 100) * width);
104
+ const empty = width - filled;
105
+
106
+ // Color based on percentage
107
+ const color = percentage === 100 ? "white" : percentage >= 50 ? "gray" : "gray";
108
+
109
+ return (
110
+ <Text color={color}>
111
+ {"█".repeat(filled)}
112
+ <Text dimColor>{"░".repeat(empty)}</Text>
113
+ </Text>
114
+ );
115
+ }
116
+
117
+ // Model cell component
118
+ function ModelCell({ status, width = 18 }: { status: ModelStatus; width?: number }) {
119
+ return (
120
+ <Box width={width}>
121
+ <Box width={8}>
122
+ <ProgressBar percentage={status.percentage} width={6} />
123
+ </Box>
124
+ <Box width={6}>
125
+ <Text dimColor>{status.timeRemaining.padStart(5)}</Text>
126
+ </Box>
127
+ <Box width={4}>
128
+ <Text color={status.percentage === 100 ? "white" : "gray"}>
129
+ {String(status.percentage).padStart(3)}%
130
+ </Text>
131
+ </Box>
132
+ </Box>
133
+ );
74
134
  }
75
135
 
76
136
  export function DashboardView({ accounts, selectedIndex }: DashboardViewProps) {
@@ -83,98 +143,124 @@ export function DashboardView({ accounts, selectedIndex }: DashboardViewProps) {
83
143
  );
84
144
  }
85
145
 
146
+ const activeModels = getActiveModels(accounts);
147
+ const modelColumnWidth = 18;
148
+ const emailWidth = 28;
149
+
86
150
  return (
87
151
  <Box flexDirection="column">
88
152
  {/* Header */}
89
- <Box paddingX={1} marginBottom={1}>
153
+ <Box paddingX={1}>
90
154
  <Box width={3}>
91
155
  <Text dimColor>{" "}</Text>
92
156
  </Box>
93
- <Box width={30}>
157
+ <Box width={emailWidth}>
94
158
  <Text dimColor bold>EMAIL</Text>
95
159
  </Box>
96
- <Box width={10}>
97
- <Text dimColor bold>STATUS</Text>
98
- </Box>
99
- <Box flexGrow={1}>
100
- <Text dimColor bold>RATE LIMITS</Text>
101
- </Box>
160
+ {activeModels.map(model => (
161
+ <Box key={model.key} width={modelColumnWidth}>
162
+ <Text dimColor bold>{model.shortName}</Text>
163
+ </Box>
164
+ ))}
165
+ </Box>
166
+
167
+ {/* Separator */}
168
+ <Box paddingX={1}>
169
+ <Text dimColor>{"─".repeat(3 + emailWidth + activeModels.length * modelColumnWidth)}</Text>
102
170
  </Box>
103
171
 
104
- {/* Accounts */}
172
+ {/* Account rows */}
105
173
  {accounts.map((account, index) => {
106
174
  const isSelected = index === selectedIndex;
107
- const { status, statusColor, indicator } = getAccountStatus(account);
108
- const limits = getAccountRateLimits(account);
175
+ const isDisabled = account.enabled === false;
109
176
 
110
177
  // Truncate email
111
- const email = account.email.length > 28
112
- ? account.email.slice(0, 25) + "..."
178
+ const email = account.email.length > emailWidth - 3
179
+ ? account.email.slice(0, emailWidth - 6) + "..."
113
180
  : account.email;
114
181
 
115
182
  return (
116
183
  <Box key={account.email} paddingX={1}>
117
184
  {/* Selection cursor */}
118
185
  <Box width={3}>
119
- <Text color={isSelected ? "cyan" : undefined} bold={isSelected}>
186
+ <Text bold={isSelected}>
120
187
  {isSelected ? "› " : " "}
121
188
  </Text>
122
189
  </Box>
123
190
 
124
191
  {/* Email */}
125
- <Box width={30}>
192
+ <Box width={emailWidth}>
126
193
  <Text
127
- color={isSelected ? "cyan" : (account.enabled === false ? "gray" : "white")}
128
194
  bold={isSelected}
129
- dimColor={account.enabled === false}
195
+ dimColor={isDisabled}
130
196
  >
131
197
  {email}
132
198
  </Text>
133
199
  </Box>
134
200
 
135
- {/* Status indicator */}
136
- <Box width={10}>
137
- <Text color={statusColor}>
138
- {indicator} {status}
139
- </Text>
140
- </Box>
141
-
142
- {/* Rate limits - show all models */}
143
- <Box flexGrow={1}>
144
- {limits.length === 0 ? (
145
- <Text dimColor>—</Text>
146
- ) : (
147
- <Text>
148
- {limits.map((limit, i) => (
149
- <Text key={limit.model}>
150
- <Text dimColor>{limit.displayName}</Text>
151
- <Text color="gray">:</Text>
152
- <Text color="white">{limit.timeRemaining}</Text>
153
- {i < limits.length - 1 ? <Text dimColor> │ </Text> : null}
154
- </Text>
155
- ))}
156
- </Text>
157
- )}
158
- </Box>
201
+ {/* Model columns */}
202
+ {activeModels.map(model => {
203
+ const status = getModelStatus(account, model.key);
204
+
205
+ if (isDisabled) {
206
+ return (
207
+ <Box key={model.key} width={modelColumnWidth}>
208
+ <Text dimColor>── disabled ──</Text>
209
+ </Box>
210
+ );
211
+ }
212
+
213
+ return (
214
+ <ModelCell
215
+ key={model.key}
216
+ status={status}
217
+ width={modelColumnWidth}
218
+ />
219
+ );
220
+ })}
159
221
  </Box>
160
222
  );
161
223
  })}
162
224
 
163
225
  {/* Summary */}
164
226
  <Box paddingX={1} marginTop={1}>
165
- <Text dimColor>
166
- ─────────────────────────────────────────────────────────────────
167
- </Text>
227
+ <Text dimColor>{"─".repeat(3 + emailWidth + activeModels.length * modelColumnWidth)}</Text>
168
228
  </Box>
229
+
169
230
  <Box paddingX={1}>
170
231
  <Text dimColor>
171
- {accounts.length} accounts • {accounts.filter(a => a.enabled !== false && getAccountRateLimits(a).length === 0).length} available • {accounts.filter(a => getAccountRateLimits(a).length > 0).length} limited
232
+ {accounts.length} accounts
233
+ </Text>
234
+ <Text dimColor> │ </Text>
235
+ <Text>
236
+ {accounts.filter(a => {
237
+ if (a.enabled === false) return false;
238
+ const now = Date.now();
239
+ const limits = a.rateLimitResetTimes || {};
240
+ return !Object.values(limits).some(t => t > now);
241
+ }).length}
242
+ </Text>
243
+ <Text dimColor> available</Text>
244
+ <Text dimColor> │ </Text>
245
+ <Text dimColor>
246
+ {accounts.filter(a => {
247
+ if (a.enabled === false) return false;
248
+ const now = Date.now();
249
+ const limits = a.rateLimitResetTimes || {};
250
+ return Object.values(limits).some(t => t > now);
251
+ }).length} limited
252
+ </Text>
253
+ <Text dimColor> │ </Text>
254
+ <Text dimColor>
255
+ {accounts.filter(a => a.enabled === false).length} disabled
172
256
  </Text>
173
257
  </Box>
174
258
 
175
259
  {/* Legend */}
176
260
  <Box paddingX={1} marginTop={1}>
177
- <Text dimColor>● available ◐ limited ○ disabled</Text>
261
+ <Text dimColor>████ 100%</Text>
262
+ <Text dimColor> ░░░░ limited </Text>
263
+ <Text dimColor>── disabled</Text>
178
264
  </Box>
179
265
  </Box>
180
266
  );
@@ -22,14 +22,14 @@ export function SectionBox({
22
22
  marginBottom={1}
23
23
  >
24
24
  <Box paddingX={1}>
25
- <Text bold color="white">{title}</Text>
26
- {collapsed && <Text dimColor> (collapsed)</Text>}
25
+ <Text bold>{title}</Text>
26
+ {collapsed ? <Text dimColor> (collapsed)</Text> : null}
27
27
  </Box>
28
- {!collapsed && (
28
+ {!collapsed ? (
29
29
  <Box flexDirection="column" paddingY={0}>
30
30
  {children}
31
31
  </Box>
32
- )}
32
+ ) : null}
33
33
  </Box>
34
34
  );
35
35
  }
@@ -1,33 +1,33 @@
1
- import React from "react";
2
- import { Box, Text } from "ink";
3
-
4
- interface StatCardProps {
5
- label: string;
6
- value: string | number;
7
- color?: string;
8
- }
9
-
10
- export function StatCard({ label, value, color = "white" }: StatCardProps) {
11
- return (
12
- <Box flexDirection="column" marginRight={2}>
13
- <Text dimColor>{label}</Text>
14
- <Text bold color={color}>
15
- {value}
16
- </Text>
17
- </Box>
18
- );
19
- }
20
-
21
- interface StatsRowProps {
22
- stats: StatCardProps[];
23
- }
24
-
25
- export function StatsRow({ stats }: StatsRowProps) {
26
- return (
27
- <Box flexDirection="row" marginY={1}>
28
- {stats.map((stat, index) => (
29
- <StatCard key={index} {...stat} />
30
- ))}
31
- </Box>
32
- );
33
- }
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+
4
+ interface StatCardProps {
5
+ label: string;
6
+ value: string | number;
7
+ color?: string;
8
+ }
9
+
10
+ export function StatCard({ label, value, color = "white" }: StatCardProps) {
11
+ return (
12
+ <Box flexDirection="column" marginRight={2}>
13
+ <Text dimColor>{label}</Text>
14
+ <Text bold color={color}>
15
+ {value}
16
+ </Text>
17
+ </Box>
18
+ );
19
+ }
20
+
21
+ interface StatsRowProps {
22
+ stats: StatCardProps[];
23
+ }
24
+
25
+ export function StatsRow({ stats }: StatsRowProps) {
26
+ return (
27
+ <Box flexDirection="row" marginY={1}>
28
+ {stats.map((stat, index) => (
29
+ <StatCard key={index} {...stat} />
30
+ ))}
31
+ </Box>
32
+ );
33
+ }
@@ -1,33 +1,33 @@
1
- import React from "react";
2
- import { Text } from "ink";
3
-
4
- interface StatusBadgeProps {
5
- status: "available" | "limited" | "disabled" | "error";
6
- label?: string;
7
- }
8
-
9
- const STATUS_COLORS: Record<string, string> = {
10
- available: "green",
11
- limited: "yellow",
12
- disabled: "gray",
13
- error: "red",
14
- };
15
-
16
- const STATUS_ICONS: Record<string, string> = {
17
- available: "●",
18
- limited: "",
19
- disabled: "x",
20
- error: "!",
21
- };
22
-
23
- export function StatusBadge({ status, label }: StatusBadgeProps) {
24
- const color = STATUS_COLORS[status] || "gray";
25
- const icon = STATUS_ICONS[status] || "?";
26
- const text = label || status.toUpperCase();
27
-
28
- return (
29
- <Text color={color}>
30
- {icon} {text}
31
- </Text>
32
- );
33
- }
1
+ import React from "react";
2
+ import { Text } from "ink";
3
+
4
+ interface StatusBadgeProps {
5
+ status: "available" | "limited" | "disabled" | "error";
6
+ label?: string;
7
+ }
8
+
9
+ const STATUS_COLORS: Record<string, string> = {
10
+ available: "white",
11
+ limited: "gray",
12
+ disabled: "gray",
13
+ error: "gray",
14
+ };
15
+
16
+ const STATUS_ICONS: Record<string, string> = {
17
+ available: "●",
18
+ limited: "",
19
+ disabled: "",
20
+ error: "!",
21
+ };
22
+
23
+ export function StatusBadge({ status, label }: StatusBadgeProps) {
24
+ const color = STATUS_COLORS[status] || "gray";
25
+ const icon = STATUS_ICONS[status] || "?";
26
+ const text = label || status.toUpperCase();
27
+
28
+ return (
29
+ <Text color={color}>
30
+ {icon} {text}
31
+ </Text>
32
+ );
33
+ }