opencode-account-manager 0.6.4 → 0.6.5

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 (86) hide show
  1. package/README.md +235 -216
  2. package/README_VI.md +235 -216
  3. package/dist/cli.js +83 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/core/config-store.d.ts +12 -0
  6. package/dist/core/config-store.d.ts.map +1 -1
  7. package/dist/core/config-store.js +98 -0
  8. package/dist/core/config-store.js.map +1 -1
  9. package/dist/core/health-log.d.ts +9 -0
  10. package/dist/core/health-log.d.ts.map +1 -0
  11. package/dist/core/health-log.js +154 -0
  12. package/dist/core/health-log.js.map +1 -0
  13. package/dist/core/health-oauth.d.ts +5 -0
  14. package/dist/core/health-oauth.d.ts.map +1 -0
  15. package/dist/core/health-oauth.js +147 -0
  16. package/dist/core/health-oauth.js.map +1 -0
  17. package/dist/core/health-orchestrator.d.ts +32 -0
  18. package/dist/core/health-orchestrator.d.ts.map +1 -0
  19. package/dist/core/health-orchestrator.js +148 -0
  20. package/dist/core/health-orchestrator.js.map +1 -0
  21. package/dist/core/health-utils.d.ts +15 -0
  22. package/dist/core/health-utils.d.ts.map +1 -0
  23. package/dist/core/health-utils.js +60 -0
  24. package/dist/core/health-utils.js.map +1 -0
  25. package/dist/core/paths.d.ts +1 -0
  26. package/dist/core/paths.d.ts.map +1 -1
  27. package/dist/core/paths.js +4 -0
  28. package/dist/core/paths.js.map +1 -1
  29. package/dist/core/types.d.ts +26 -0
  30. package/dist/core/types.d.ts.map +1 -1
  31. package/dist/tui/Dashboard.d.ts.map +1 -1
  32. package/dist/tui/Dashboard.js +69 -2
  33. package/dist/tui/Dashboard.js.map +1 -1
  34. package/dist/tui/components/AccountList.d.ts +5 -3
  35. package/dist/tui/components/AccountList.d.ts.map +1 -1
  36. package/dist/tui/components/AccountList.js +9 -3
  37. package/dist/tui/components/AccountList.js.map +1 -1
  38. package/dist/tui/components/DashboardView.d.ts +3 -2
  39. package/dist/tui/components/DashboardView.d.ts.map +1 -1
  40. package/dist/tui/components/DashboardView.js +50 -4
  41. package/dist/tui/components/DashboardView.js.map +1 -1
  42. package/dist/tui/components/HealthBadge.d.ts +9 -0
  43. package/dist/tui/components/HealthBadge.d.ts.map +1 -0
  44. package/dist/tui/components/HealthBadge.js +56 -0
  45. package/dist/tui/components/HealthBadge.js.map +1 -0
  46. package/dist/tui/components/StatusBadge.d.ts +2 -1
  47. package/dist/tui/components/StatusBadge.d.ts.map +1 -1
  48. package/dist/tui/components/StatusBadge.js +30 -2
  49. package/dist/tui/components/StatusBadge.js.map +1 -1
  50. package/dist/tui/components/index.d.ts +1 -0
  51. package/dist/tui/components/index.d.ts.map +1 -1
  52. package/dist/tui/components/index.js +3 -1
  53. package/dist/tui/components/index.js.map +1 -1
  54. package/docs/BLUEPRINT.md +476 -476
  55. package/docs/ROADMAP.md +125 -107
  56. package/package.json +36 -36
  57. package/src/cli.ts +139 -38
  58. package/src/core/config-store.ts +278 -171
  59. package/src/core/crypto.ts +162 -162
  60. package/src/core/health-log.ts +173 -0
  61. package/src/core/health-oauth.ts +190 -0
  62. package/src/core/health-orchestrator.ts +224 -0
  63. package/src/core/importers/amExport.ts +177 -177
  64. package/src/core/opencode-config.ts +217 -217
  65. package/src/core/paths.ts +10 -6
  66. package/src/core/types.ts +193 -147
  67. package/src/tui/Dashboard.tsx +557 -478
  68. package/src/tui/components/AccountList.tsx +122 -104
  69. package/src/tui/components/ActionPalette.tsx +117 -117
  70. package/src/tui/components/Box.tsx +7 -7
  71. package/src/tui/components/DashboardView.tsx +285 -230
  72. package/src/tui/components/ExportModal.tsx +255 -255
  73. package/src/tui/components/FileBrowser.tsx +393 -393
  74. package/src/tui/components/Header.tsx +26 -26
  75. package/src/tui/components/HealthBadge.tsx +64 -0
  76. package/src/tui/components/ImportModal.tsx +334 -334
  77. package/src/tui/components/McpServerList.tsx +67 -67
  78. package/src/tui/components/Menu.tsx +61 -61
  79. package/src/tui/components/PasswordInput.tsx +159 -159
  80. package/src/tui/components/ProviderList.tsx +59 -59
  81. package/src/tui/components/SectionBox.tsx +35 -35
  82. package/src/tui/components/StatsRow.tsx +33 -33
  83. package/src/tui/components/StatusBadge.tsx +36 -3
  84. package/src/tui/components/index.ts +15 -14
  85. package/test-minimal.js +26 -26
  86. package/test-with-accounts.js +58 -58
package/docs/ROADMAP.md CHANGED
@@ -1,125 +1,143 @@
1
- # OpenCode Account Manager - Roadmap
2
-
3
- ## Version History
4
-
5
- ### v0.1.0 - Initial Release
6
- - [x] Basic TUI dashboard
7
- - [x] View accounts from antigravity-auth plugin
8
- - [x] Show rate limit status per account
9
- - [x] Import from Antigravity Manager folder
10
-
11
- ### v0.2.0 - Account Management
12
- - [x] Select mode with keyboard navigation
13
- - [x] Enable/Disable selected accounts
14
- - [x] Delete selected accounts
15
- - [x] Export selected accounts to JSON
16
- - [x] Per-model rate limit display (claude, gemini, etc.)
17
-
18
- ### v0.3.0 - OpenCode Dashboard
19
- - [x] Rename to opencode-account-manager
20
- - [x] Read opencode.json config
21
- - [x] Display all AI providers with model counts
22
- - [x] Display MCP servers with enabled/disabled status
23
- - [x] Tab navigation between sections (Providers, Accounts, MCP)
24
- - [x] Collapsible sections
25
-
26
- ### v0.4.0 - Encrypted Export/Import
27
- - [x] **Encrypted Export** - AES-256-GCM with password protection
28
- - [x] **Plain JSON Export** - Keep original format as option
29
- - [x] **File Browser UI** - Browse folders, quick locations, paste path
30
- - [x] **Import from File** - Select .ocam or .json files
31
- - [x] **Password Input** - Masked input with confirmation
32
- - [x] **Remember Preferences** - Last export/import folder saved
33
- - [x] **Overwrite Mode** - Replace existing accounts on import
34
-
35
- ### v0.4.1 - npm Publish
36
- - [x] Published to npm registry
37
- - [x] Global install via `npm install -g opencode-account-manager`
38
- - [x] Commands: `ocam`, `opencode-account-manager`
39
-
40
- ### v0.4.2 - Antigravity Manager Export Support
41
- - [x] **AM Export Import** - Support `[{email, refresh_token}]` format
42
- - [x] **Auto-detect Format** - Recognize encrypted, portable, AM export formats
43
- - [x] **Format Preview** - Show detected format in import preview
44
- - [x] **Professional README** - Updated documentation with LLM installation guide
45
-
46
- ### v0.4.3 - Keyboard Navigation Fix
47
- - [x] **Number Keys** - Press 1/2/3 to switch sections (Providers/Accounts/MCP)
48
- - [x] **Tab Navigation** - Fixed Tab key to cycle through sections
49
- - [x] **UI Indicator** - Show `[1] Providers [2] Accounts [3] MCP` in header
50
-
51
- ### v0.4.4 - Arrow Key Navigation
52
- - [x] **←→ Arrow Keys** - Switch between sections (Providers ↔ Accounts ↔ MCP)
53
- - [x] **↑↓ Arrow Keys** - Navigate account list, auto-enters select mode
54
- - [x] **Space Key** - Toggle selection in select mode
55
- - [x] **Updated Help Text** - Show arrow key hints in UI
56
-
57
- ### v0.5.0 - OpenCode-style UX
58
- - [x] **Action Palette** - Press P to open command palette (like OpenCode Ctrl+P)
59
- - [x] **Unified Navigation** - ↑↓ to navigate everything, Enter to expand/select
60
- - [x] **Simplified Controls** - No more complex keyboard shortcuts to remember
61
- - [x] **Inline Selection** - Space to toggle, selection count shown in help bar
62
- - [x] **Removed MenuBar** - Clean minimal interface
63
-
64
- ### v0.5.1 - Dashboard Tab
65
- - [x] **Two Tabs** - Dashboard (rate limits) + Settings (providers, accounts, MCP)
66
- - [x] **Tab Switching** - Press Tab to switch between Dashboard and Settings
67
- - [x] **Rate Limit View** - Like Antigravity Manager, shows accounts with model limits
68
- - [x] **Progress Bars** - Visual indicator of rate limit status per model
69
- - [x] **Time Remaining** - Shows hours/minutes until limit resets
70
-
71
- ### v0.5.2 - Loading Indicator (Current)
1
+ # OpenCode Account Manager - Roadmap
2
+
3
+ ## Version History
4
+
5
+ ### v0.1.0 - Initial Release
6
+ - [x] Basic TUI dashboard
7
+ - [x] View accounts from antigravity-auth plugin
8
+ - [x] Show rate limit status per account
9
+ - [x] Import from Antigravity Manager folder
10
+
11
+ ### v0.2.0 - Account Management
12
+ - [x] Select mode with keyboard navigation
13
+ - [x] Enable/Disable selected accounts
14
+ - [x] Delete selected accounts
15
+ - [x] Export selected accounts to JSON
16
+ - [x] Per-model rate limit display (claude, gemini, etc.)
17
+
18
+ ### v0.3.0 - OpenCode Dashboard
19
+ - [x] Rename to opencode-account-manager
20
+ - [x] Read opencode.json config
21
+ - [x] Display all AI providers with model counts
22
+ - [x] Display MCP servers with enabled/disabled status
23
+ - [x] Tab navigation between sections (Providers, Accounts, MCP)
24
+ - [x] Collapsible sections
25
+
26
+ ### v0.4.0 - Encrypted Export/Import
27
+ - [x] **Encrypted Export** - AES-256-GCM with password protection
28
+ - [x] **Plain JSON Export** - Keep original format as option
29
+ - [x] **File Browser UI** - Browse folders, quick locations, paste path
30
+ - [x] **Import from File** - Select .ocam or .json files
31
+ - [x] **Password Input** - Masked input with confirmation
32
+ - [x] **Remember Preferences** - Last export/import folder saved
33
+ - [x] **Overwrite Mode** - Replace existing accounts on import
34
+
35
+ ### v0.4.1 - npm Publish
36
+ - [x] Published to npm registry
37
+ - [x] Global install via `npm install -g opencode-account-manager`
38
+ - [x] Commands: `ocam`, `opencode-account-manager`
39
+
40
+ ### v0.4.2 - Antigravity Manager Export Support
41
+ - [x] **AM Export Import** - Support `[{email, refresh_token}]` format
42
+ - [x] **Auto-detect Format** - Recognize encrypted, portable, AM export formats
43
+ - [x] **Format Preview** - Show detected format in import preview
44
+ - [x] **Professional README** - Updated documentation with LLM installation guide
45
+
46
+ ### v0.4.3 - Keyboard Navigation Fix
47
+ - [x] **Number Keys** - Press 1/2/3 to switch sections (Providers/Accounts/MCP)
48
+ - [x] **Tab Navigation** - Fixed Tab key to cycle through sections
49
+ - [x] **UI Indicator** - Show `[1] Providers [2] Accounts [3] MCP` in header
50
+
51
+ ### v0.4.4 - Arrow Key Navigation
52
+ - [x] **←→ Arrow Keys** - Switch between sections (Providers ↔ Accounts ↔ MCP)
53
+ - [x] **↑↓ Arrow Keys** - Navigate account list, auto-enters select mode
54
+ - [x] **Space Key** - Toggle selection in select mode
55
+ - [x] **Updated Help Text** - Show arrow key hints in UI
56
+
57
+ ### v0.5.0 - OpenCode-style UX
58
+ - [x] **Action Palette** - Press P to open command palette (like OpenCode Ctrl+P)
59
+ - [x] **Unified Navigation** - ↑↓ to navigate everything, Enter to expand/select
60
+ - [x] **Simplified Controls** - No more complex keyboard shortcuts to remember
61
+ - [x] **Inline Selection** - Space to toggle, selection count shown in help bar
62
+ - [x] **Removed MenuBar** - Clean minimal interface
63
+
64
+ ### v0.5.1 - Dashboard Tab
65
+ - [x] **Two Tabs** - Dashboard (rate limits) + Settings (providers, accounts, MCP)
66
+ - [x] **Tab Switching** - Press Tab to switch between Dashboard and Settings
67
+ - [x] **Rate Limit View** - Like Antigravity Manager, shows accounts with model limits
68
+ - [x] **Progress Bars** - Visual indicator of rate limit status per model
69
+ - [x] **Time Remaining** - Shows hours/minutes until limit resets
70
+
71
+ ### v0.5.2 - Loading Indicator
72
72
  - [x] **Loading State** - Shows progress during refresh
73
73
  - [x] **Step Messages** - "Loading OpenCode config...", "Loading accounts...", "Done!"
74
74
  - [x] **R Key Shortcut** - Added R to help bar for quick refresh
75
75
 
76
- **Controls:**
77
- | Key | Action |
78
- |-----|--------|
76
+ ### v0.6.4 - Account Health Check (Current)
77
+ - [x] **Health Check Cache** - TTL + cooldown stored in ocam-config.json
78
+ - [x] **OAuth Validation** - Refresh token check with status mapping
79
+ - [x] **Log Hints** - Parse antigravity-logs for verification errors
80
+ - [x] **Dashboard Badges** - Health indicator + summary counts
81
+ - [x] **CLI Command** - `ocam check` with progress and warnings
82
+
83
+ **Controls:**
84
+ | Key | Action |
85
+ |-----|--------|
79
86
  | Tab | Switch between Dashboard and Settings |
80
87
  | ↑↓ | Navigate accounts/sections |
81
88
  | Space | Toggle account selection |
82
89
  | R | Refresh with progress indicator |
90
+ | H | Check account health |
83
91
  | P | Open Action Palette |
84
92
  | Q | Quit |
85
-
86
- ---
87
-
88
- ## Future Ideas (Backlog)
89
-
90
- ### v0.5.0 - Enhanced Security
91
- - [ ] Password strength indicator
92
- - [ ] Auto-lock after inactivity
93
- - [ ] Encrypted storage for config
94
-
95
- ### v0.6.0 - Cloud Sync
96
- - [ ] Sync accounts to cloud storage (Google Drive, Dropbox)
97
- - [ ] Multi-device sync
98
- - [ ] Conflict resolution
99
-
100
- ### v0.7.0 - Account Health
101
- - [ ] Check if refresh tokens are still valid
93
+
94
+ ---
95
+
96
+ ## Future Ideas (Backlog)
97
+
98
+ ### v0.5.0 - Enhanced Security
99
+ - [ ] Password strength indicator
100
+ - [ ] Auto-lock after inactivity
101
+ - [ ] Encrypted storage for config
102
+
103
+ ### v0.6.0 - Cloud Sync
104
+ - [ ] Sync accounts to cloud storage (Google Drive, Dropbox)
105
+ - [ ] Multi-device sync
106
+ - [ ] Conflict resolution
107
+
108
+ ### v0.7.0 - Account Health (Follow-ups)
102
109
  - [ ] Auto-refresh expired tokens
110
+ - [ ] Background health monitoring
103
111
  - [ ] Health check on startup
112
+
113
+ ### v0.8.0 - MCP Management
114
+ - [ ] Enable/Disable MCP servers from TUI
115
+ - [ ] Add new MCP servers
116
+ - [ ] View MCP server logs
117
+
118
+ ### v0.9.0 - Provider Management
119
+ - [ ] Add/Edit custom providers
120
+ - [ ] Test provider connection
121
+ - [ ] Model usage statistics
122
+
123
+ ### v1.0.0 - Stable Release
124
+ - [x] npm publish
125
+ - [ ] Full documentation
126
+ - [ ] Windows/Mac/Linux installers
127
+ - [ ] Integration tests
128
+
129
+ ---
104
130
 
105
- ### v0.8.0 - MCP Management
106
- - [ ] Enable/Disable MCP servers from TUI
107
- - [ ] Add new MCP servers
108
- - [ ] View MCP server logs
109
-
110
- ### v0.9.0 - Provider Management
111
- - [ ] Add/Edit custom providers
112
- - [ ] Test provider connection
113
- - [ ] Model usage statistics
131
+ ## Manual Test Checklist (Health Check)
114
132
 
115
- ### v1.0.0 - Stable Release
116
- - [x] npm publish
117
- - [ ] Full documentation
118
- - [ ] Windows/Mac/Linux installers
119
- - [ ] Integration tests
133
+ - [ ] **Happy path**: OAuth config set, refresh token valid → status `ok`
134
+ - [ ] **Invalid grant**: revoked/expired token → status `revoked` (or `password_changed` if error_description matches)
135
+ - [ ] **Verification required**: trigger `verification_required` mapping via `invalid_grant` with verify/challenge text
136
+ - [ ] **Network error**: disconnect network → status `network_error`
137
+ - [ ] **Missing OAuth config**: no client_id/client_secret → status `not_configured`
120
138
 
121
139
  ---
122
140
 
123
141
  ## Contributing
124
-
125
- See [BLUEPRINT.md](./BLUEPRINT.md) for technical architecture and implementation details.
142
+
143
+ See [BLUEPRINT.md](./BLUEPRINT.md) for technical architecture and implementation details.
package/package.json CHANGED
@@ -1,38 +1,38 @@
1
- {
2
- "name": "opencode-account-manager",
3
- "version": "0.6.4",
4
- "description": "TUI Dashboard for OpenCode - View providers, MCP servers, and plugin accounts",
5
- "main": "dist/cli.js",
1
+ {
2
+ "name": "opencode-account-manager",
3
+ "version": "0.6.5",
4
+ "description": "TUI Dashboard for OpenCode - View providers, MCP servers, and plugin accounts",
5
+ "main": "dist/cli.js",
6
6
  "bin": {
7
- "ocam": "./dist/cli.js",
8
- "opencode-account-manager": "./dist/cli.js"
7
+ "ocam": "dist/cli.js",
8
+ "opencode-account-manager": "dist/cli.js"
9
9
  },
10
- "scripts": {
11
- "build": "npx tsc",
12
- "dev": "npx tsc -w",
13
- "start": "node dist/cli.js",
14
- "dashboard": "node dist/cli.js dashboard"
15
- },
16
- "keywords": [
17
- "opencode",
18
- "antigravity",
19
- "account-manager",
20
- "dashboard",
21
- "tui",
22
- "mcp",
23
- "providers"
24
- ],
25
- "author": "",
26
- "license": "MIT",
27
- "devDependencies": {
28
- "@types/node": "^20.10.0",
29
- "@types/react": "^17.0.0",
30
- "typescript": "^5.3.0"
31
- },
32
- "dependencies": {
33
- "chalk": "^4.1.2",
34
- "commander": "^11.1.0",
35
- "ink": "^3.2.0",
36
- "react": "^17.0.2"
37
- }
38
- }
10
+ "scripts": {
11
+ "build": "npx tsc",
12
+ "dev": "npx tsc -w",
13
+ "start": "node dist/cli.js",
14
+ "dashboard": "node dist/cli.js dashboard"
15
+ },
16
+ "keywords": [
17
+ "opencode",
18
+ "antigravity",
19
+ "account-manager",
20
+ "dashboard",
21
+ "tui",
22
+ "mcp",
23
+ "providers"
24
+ ],
25
+ "author": "",
26
+ "license": "MIT",
27
+ "devDependencies": {
28
+ "@types/node": "^20.10.0",
29
+ "@types/react": "^17.0.0",
30
+ "typescript": "^5.3.0"
31
+ },
32
+ "dependencies": {
33
+ "chalk": "^4.1.2",
34
+ "commander": "^11.1.0",
35
+ "ink": "^3.2.0",
36
+ "react": "^17.0.2"
37
+ }
38
+ }
package/src/cli.ts CHANGED
@@ -3,24 +3,84 @@ import fs from "fs";
3
3
  import path from "path";
4
4
  import { Command } from "commander";
5
5
  import chalk from "chalk";
6
- import {
7
- buildPortableExport,
8
- createEmptyPluginAccountsFile,
9
- extractAccountsFromImport,
10
- mergeAccounts,
11
- readPluginAccountsFile,
12
- summarizeAccounts,
13
- writePluginAccountsFile,
14
- } from "./core/accounts";
15
- import { getPluginAccountsPath, getAmFolderPath } from "./core/paths";
16
- import { importFromAmFolder, isAmFolder } from "./core/importers/amJson";
17
- import { writeJsonFile } from "./core/utils";
18
- import { startTuiDashboard } from "./tui";
19
-
20
- function formatTimestamp(timestamp?: number): string {
21
- if (!timestamp) return "-";
22
- return new Date(timestamp).toLocaleString();
23
- }
6
+ import {
7
+ buildPortableExport,
8
+ createEmptyPluginAccountsFile,
9
+ extractAccountsFromImport,
10
+ mergeAccounts,
11
+ readPluginAccountsFile,
12
+ summarizeAccounts,
13
+ writePluginAccountsFile,
14
+ } from "./core/accounts";
15
+ import { getPluginAccountsPath, getAmFolderPath } from "./core/paths";
16
+ import { importFromAmFolder, isAmFolder } from "./core/importers/amJson";
17
+ import { writeJsonFile } from "./core/utils";
18
+ import { startTuiDashboard } from "./tui";
19
+ import { checkAccountsHealth, HealthCheckResult } from "./core/health-orchestrator";
20
+ import { AccountHealthStatus } from "./core/types";
21
+
22
+ function formatTimestamp(timestamp?: number): string {
23
+ if (!timestamp) return "-";
24
+ return new Date(timestamp).toLocaleString();
25
+ }
26
+
27
+ function getStatusLabel(status: AccountHealthStatus): string {
28
+ switch (status) {
29
+ case "verification_required":
30
+ return chalk.bgYellow.black(" VERIFY ");
31
+ case "revoked":
32
+ return chalk.bgRed.white(" REVOKED ");
33
+ case "disabled":
34
+ return chalk.bgRed.white(" DISABLED ");
35
+ case "deleted":
36
+ return chalk.bgRed.white(" DELETED ");
37
+ case "password_changed":
38
+ return chalk.bgYellow.black(" PASSWD ");
39
+ case "network_error":
40
+ return chalk.bgBlue.white(" NETWORK ");
41
+ case "unknown_error":
42
+ return chalk.bgRed.white(" ERROR ");
43
+ case "not_configured":
44
+ return chalk.bgGray.white(" CONFIG ");
45
+ default:
46
+ return chalk.bgGray.white(` ${status.toUpperCase()} `);
47
+ }
48
+ }
49
+
50
+ function printHealthCheckResult(result: HealthCheckResult) {
51
+ const { counts, timing, items } = result;
52
+ const issues =
53
+ counts.total -
54
+ counts.byStatus.ok -
55
+ (counts.byStatus.not_checked || 0);
56
+
57
+ console.log(chalk.bold("\n=== Health Check Summary ==="));
58
+ console.log(
59
+ `Total: ${counts.total} | ` +
60
+ `${chalk.green("OK: " + counts.byStatus.ok)} | ` +
61
+ `${
62
+ issues > 0 ? chalk.red("Issues: " + issues) : chalk.gray("Issues: 0")
63
+ } | ` +
64
+ `Time: ${(timing.durationMs / 1000).toFixed(1)}s\n`
65
+ );
66
+
67
+ const warnings = items.filter(
68
+ (item) => item.result.status !== "ok" && item.result.status !== "not_checked"
69
+ );
70
+
71
+ if (warnings.length > 0) {
72
+ console.log(chalk.bold("Warnings:"));
73
+ for (const item of warnings) {
74
+ const label = getStatusLabel(item.result.status);
75
+ const msg = item.result.message || item.result.status;
76
+ console.log(`${label} ${item.email} ${chalk.gray(`(${msg})`)}`);
77
+ }
78
+ console.log("");
79
+ } else if (counts.total > 0) {
80
+ console.log(chalk.green("All accounts are healthy.\n"));
81
+ }
82
+ }
83
+
24
84
 
25
85
  function isLimited(rateLimitResetTimes?: Record<string, number>): boolean {
26
86
  if (!rateLimitResetTimes) return false;
@@ -28,20 +88,29 @@ function isLimited(rateLimitResetTimes?: Record<string, number>): boolean {
28
88
  return Object.values(rateLimitResetTimes).some((value) => value > now);
29
89
  }
30
90
 
31
- function safeReadPluginFile(pluginPath: string) {
32
- try {
33
- return readPluginAccountsFile(pluginPath);
34
- } catch {
35
- return createEmptyPluginAccountsFile();
36
- }
37
- }
91
+ function safeReadPluginFile(pluginPath: string) {
92
+ try {
93
+ return readPluginAccountsFile(pluginPath);
94
+ } catch {
95
+ return createEmptyPluginAccountsFile();
96
+ }
97
+ }
98
+
99
+ function parseEmailList(input?: string): string[] | undefined {
100
+ if (!input) return undefined;
101
+ const items = input
102
+ .split(",")
103
+ .map((value) => value.trim())
104
+ .filter((value) => value.length > 0);
105
+ return items.length > 0 ? items : undefined;
106
+ }
38
107
 
39
108
  const program = new Command();
40
109
 
41
- program
42
- .name("ocam")
43
- .description("OpenCode Account Manager - TUI dashboard and CLI for managing accounts")
44
- .version("0.5.3");
110
+ program
111
+ .name("ocam")
112
+ .description("OpenCode Account Manager - TUI dashboard and CLI for managing accounts")
113
+ .version("0.5.3");
45
114
 
46
115
  // Default command - show dashboard
47
116
  program
@@ -54,11 +123,11 @@ program
54
123
  });
55
124
  });
56
125
 
57
- program
58
- .command("list")
59
- .description("List plugin accounts")
60
- .option("--plugin-path <path>", "Path to plugin accounts file")
61
- .action((options) => {
126
+ program
127
+ .command("list")
128
+ .description("List plugin accounts")
129
+ .option("--plugin-path <path>", "Path to plugin accounts file")
130
+ .action((options) => {
62
131
  const pluginPath = getPluginAccountsPath(options.pluginPath);
63
132
  const file = safeReadPluginFile(pluginPath);
64
133
  const summary = summarizeAccounts(file.accounts);
@@ -79,9 +148,41 @@ program
79
148
  console.log(chalk.gray(` └─ ${model}: ${hours}h`));
80
149
  }
81
150
  }
82
- }
83
- }
84
- });
151
+ }
152
+ }
153
+ });
154
+
155
+ program
156
+ .command("check")
157
+ .description("Check account health status")
158
+ .option("--plugin-path <path>", "Path to plugin accounts file")
159
+ .option("--emails <emails>", "Comma-separated list of emails to check")
160
+ .option("--force", "Bypass cache and cooldown checks", false)
161
+ .action(async (options) => {
162
+ const pluginPath = getPluginAccountsPath(options.pluginPath);
163
+ const file = safeReadPluginFile(pluginPath);
164
+ const emails = parseEmailList(options.emails);
165
+
166
+ if (file.accounts.length === 0) {
167
+ console.log(chalk.yellow("No accounts to check."));
168
+ return;
169
+ }
170
+
171
+ const result = await checkAccountsHealth(file.accounts, {
172
+ emails,
173
+ force: options.force === true,
174
+ includeLogs: true,
175
+ onProgress: (current, total, message) => {
176
+ if (total > 0) {
177
+ process.stdout.write(`\r${chalk.gray(`[${current}/${total}]`)} ${message} `);
178
+ }
179
+ },
180
+ });
181
+
182
+ process.stdout.write("\n");
183
+
184
+ printHealthCheckResult(result);
185
+ });
85
186
 
86
187
  program
87
188
  .command("export")
@@ -204,4 +305,4 @@ program
204
305
  console.log(`Total: ${merged.accounts.length} accounts`);
205
306
  });
206
307
 
207
- program.parse();
308
+ program.parse();