opencode-account-manager 0.4.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.
- package/LICENSE +21 -0
- package/README.md +266 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +183 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/accounts.d.ts +19 -0
- package/dist/core/accounts.d.ts.map +1 -0
- package/dist/core/accounts.js +181 -0
- package/dist/core/accounts.js.map +1 -0
- package/dist/core/config-store.d.ts +48 -0
- package/dist/core/config-store.d.ts.map +1 -0
- package/dist/core/config-store.js +206 -0
- package/dist/core/config-store.js.map +1 -0
- package/dist/core/crypto.d.ts +40 -0
- package/dist/core/crypto.d.ts.map +1 -0
- package/dist/core/crypto.js +172 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/core/importers/amJson.d.ts +17 -0
- package/dist/core/importers/amJson.d.ts.map +1 -0
- package/dist/core/importers/amJson.js +131 -0
- package/dist/core/importers/amJson.js.map +1 -0
- package/dist/core/opencode-config.d.ts +92 -0
- package/dist/core/opencode-config.d.ts.map +1 -0
- package/dist/core/opencode-config.js +148 -0
- package/dist/core/opencode-config.js.map +1 -0
- package/dist/core/paths.d.ts +5 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +38 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/types.d.ts +74 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +30 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/utils.d.ts +5 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +35 -0
- package/dist/core/utils.js.map +1 -0
- package/dist/tui/Dashboard.d.ts +7 -0
- package/dist/tui/Dashboard.d.ts.map +1 -0
- package/dist/tui/Dashboard.js +331 -0
- package/dist/tui/Dashboard.js.map +1 -0
- package/dist/tui/components/AccountList.d.ts +18 -0
- package/dist/tui/components/AccountList.d.ts.map +1 -0
- package/dist/tui/components/AccountList.js +92 -0
- package/dist/tui/components/AccountList.js.map +1 -0
- package/dist/tui/components/Box.d.ts +11 -0
- package/dist/tui/components/Box.d.ts.map +1 -0
- package/dist/tui/components/Box.js +15 -0
- package/dist/tui/components/Box.js.map +1 -0
- package/dist/tui/components/ExportModal.d.ts +10 -0
- package/dist/tui/components/ExportModal.d.ts.map +1 -0
- package/dist/tui/components/ExportModal.js +192 -0
- package/dist/tui/components/ExportModal.js.map +1 -0
- package/dist/tui/components/FileBrowser.d.ts +12 -0
- package/dist/tui/components/FileBrowser.d.ts.map +1 -0
- package/dist/tui/components/FileBrowser.js +349 -0
- package/dist/tui/components/FileBrowser.js.map +1 -0
- package/dist/tui/components/Header.d.ts +8 -0
- package/dist/tui/components/Header.d.ts.map +1 -0
- package/dist/tui/components/Header.js +20 -0
- package/dist/tui/components/Header.js.map +1 -0
- package/dist/tui/components/ImportModal.d.ts +10 -0
- package/dist/tui/components/ImportModal.d.ts.map +1 -0
- package/dist/tui/components/ImportModal.js +215 -0
- package/dist/tui/components/ImportModal.js.map +1 -0
- package/dist/tui/components/McpServerList.d.ts +8 -0
- package/dist/tui/components/McpServerList.d.ts.map +1 -0
- package/dist/tui/components/McpServerList.js +35 -0
- package/dist/tui/components/McpServerList.js.map +1 -0
- package/dist/tui/components/Menu.d.ts +10 -0
- package/dist/tui/components/Menu.d.ts.map +1 -0
- package/dist/tui/components/Menu.js +83 -0
- package/dist/tui/components/Menu.js.map +1 -0
- package/dist/tui/components/PasswordInput.d.ts +12 -0
- package/dist/tui/components/PasswordInput.d.ts.map +1 -0
- package/dist/tui/components/PasswordInput.js +130 -0
- package/dist/tui/components/PasswordInput.js.map +1 -0
- package/dist/tui/components/ProviderList.d.ts +8 -0
- package/dist/tui/components/ProviderList.d.ts.map +1 -0
- package/dist/tui/components/ProviderList.js +37 -0
- package/dist/tui/components/ProviderList.js.map +1 -0
- package/dist/tui/components/SectionBox.d.ts +10 -0
- package/dist/tui/components/SectionBox.d.ts.map +1 -0
- package/dist/tui/components/SectionBox.js +16 -0
- package/dist/tui/components/SectionBox.js.map +1 -0
- package/dist/tui/components/StatsRow.d.ts +13 -0
- package/dist/tui/components/StatsRow.d.ts.map +1 -0
- package/dist/tui/components/StatsRow.js +18 -0
- package/dist/tui/components/StatsRow.js.map +1 -0
- package/dist/tui/components/StatusBadge.d.ts +8 -0
- package/dist/tui/components/StatusBadge.d.ts.map +1 -0
- package/dist/tui/components/StatusBadge.js +30 -0
- package/dist/tui/components/StatusBadge.js.map +1 -0
- package/dist/tui/components/index.d.ts +14 -0
- package/dist/tui/components/index.d.ts.map +1 -0
- package/dist/tui/components/index.js +32 -0
- package/dist/tui/components/index.js.map +1 -0
- package/dist/tui/index.d.ts +5 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +13 -0
- package/dist/tui/index.js.map +1 -0
- package/docs/BLUEPRINT.md +476 -0
- package/docs/ROADMAP.md +74 -0
- package/package.json +38 -0
- package/src/cli.ts +207 -0
- package/src/core/accounts.ts +215 -0
- package/src/core/config-store.ts +212 -0
- package/src/core/crypto.ts +162 -0
- package/src/core/importers/amJson.ts +185 -0
- package/src/core/opencode-config.ts +217 -0
- package/src/core/paths.ts +32 -0
- package/src/core/types.ts +118 -0
- package/src/core/utils.ts +28 -0
- package/src/tui/Dashboard.tsx +431 -0
- package/src/tui/components/AccountList.tsx +155 -0
- package/src/tui/components/Box.tsx +37 -0
- package/src/tui/components/ExportModal.tsx +255 -0
- package/src/tui/components/FileBrowser.tsx +393 -0
- package/src/tui/components/Header.tsx +26 -0
- package/src/tui/components/ImportModal.tsx +288 -0
- package/src/tui/components/McpServerList.tsx +67 -0
- package/src/tui/components/Menu.tsx +103 -0
- package/src/tui/components/PasswordInput.tsx +159 -0
- package/src/tui/components/ProviderList.tsx +61 -0
- package/src/tui/components/SectionBox.tsx +35 -0
- package/src/tui/components/StatsRow.tsx +33 -0
- package/src/tui/components/StatusBadge.tsx +33 -0
- package/src/tui/components/index.ts +13 -0
- package/src/tui/index.tsx +11 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
# OpenCode Account Manager - Blueprint v0.4.0
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Technical specification for the **Encrypted Export/Import** feature.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Architecture
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
13
|
+
│ TUI Layer │
|
|
14
|
+
├─────────────────────────────────────────────────────────────────────────┤
|
|
15
|
+
│ Dashboard.tsx │
|
|
16
|
+
│ ├── ExportModal.tsx (format selection → folder → password → save) │
|
|
17
|
+
│ ├── ImportModal.tsx (file selection → password → preview → import) │
|
|
18
|
+
│ └── Components: │
|
|
19
|
+
│ ├── FileBrowser.tsx (folder/file selection UI) │
|
|
20
|
+
│ ├── PasswordInput.tsx (masked password input) │
|
|
21
|
+
│ └── FormatSelector.tsx (encrypted vs plain) │
|
|
22
|
+
├─────────────────────────────────────────────────────────────────────────┤
|
|
23
|
+
│ Core Layer │
|
|
24
|
+
├─────────────────────────────────────────────────────────────────────────┤
|
|
25
|
+
│ crypto.ts - AES-256-GCM encryption/decryption │
|
|
26
|
+
│ config-store.ts - Persist user preferences (last folder, etc.) │
|
|
27
|
+
│ accounts.ts - Extended with encrypt/decrypt functions │
|
|
28
|
+
│ types.ts - New types for encrypted files │
|
|
29
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. File Specifications
|
|
35
|
+
|
|
36
|
+
### 2.1 Encrypted Export File (.ocam)
|
|
37
|
+
|
|
38
|
+
**Extension:** `.ocam` (OpenCode Account Manager)
|
|
39
|
+
|
|
40
|
+
**Structure:**
|
|
41
|
+
```typescript
|
|
42
|
+
interface EncryptedExportFile {
|
|
43
|
+
// Header (not encrypted)
|
|
44
|
+
version: 1;
|
|
45
|
+
format: "encrypted";
|
|
46
|
+
algorithm: "aes-256-gcm";
|
|
47
|
+
|
|
48
|
+
// Encryption parameters
|
|
49
|
+
salt: string; // 32 bytes, hex encoded (for key derivation)
|
|
50
|
+
iv: string; // 12 bytes, hex encoded (initialization vector)
|
|
51
|
+
authTag: string; // 16 bytes, hex encoded (authentication tag)
|
|
52
|
+
|
|
53
|
+
// Encrypted payload
|
|
54
|
+
data: string; // Encrypted JSON, hex encoded
|
|
55
|
+
|
|
56
|
+
// Metadata (not encrypted)
|
|
57
|
+
exportedAt: number; // Unix timestamp ms
|
|
58
|
+
accountCount: number; // Number of accounts (for display)
|
|
59
|
+
exportedFrom: string; // App identifier
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Example:**
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"version": 1,
|
|
67
|
+
"format": "encrypted",
|
|
68
|
+
"algorithm": "aes-256-gcm",
|
|
69
|
+
"salt": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
|
|
70
|
+
"iv": "1234567890abcdef12345678",
|
|
71
|
+
"authTag": "fedcba0987654321fedcba0987654321",
|
|
72
|
+
"data": "encrypted_hex_data_here...",
|
|
73
|
+
"exportedAt": 1707123456789,
|
|
74
|
+
"accountCount": 5,
|
|
75
|
+
"exportedFrom": "opencode-account-manager"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2.2 Plain Export File (.json)
|
|
80
|
+
|
|
81
|
+
Keep existing format for backward compatibility:
|
|
82
|
+
```typescript
|
|
83
|
+
interface PortableExportFile {
|
|
84
|
+
version: number;
|
|
85
|
+
exportedAt: number;
|
|
86
|
+
exportedFrom: "opencode-account-manager";
|
|
87
|
+
accounts: Account[];
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 2.3 App Config File
|
|
92
|
+
|
|
93
|
+
**Location:** `%APPDATA%/opencode/ocam-config.json` (Windows)
|
|
94
|
+
`~/.config/opencode/ocam-config.json` (Linux/Mac)
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
interface AppConfig {
|
|
98
|
+
lastExportFolder?: string;
|
|
99
|
+
lastImportFolder?: string;
|
|
100
|
+
defaultExportFormat?: "encrypted" | "plain";
|
|
101
|
+
recentFolders?: string[]; // Max 5 recent folders
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 3. Encryption Specification
|
|
108
|
+
|
|
109
|
+
### 3.1 Algorithm: AES-256-GCM
|
|
110
|
+
|
|
111
|
+
- **Key Derivation:** scrypt (N=16384, r=8, p=1)
|
|
112
|
+
- **Key Length:** 256 bits (32 bytes)
|
|
113
|
+
- **Salt Length:** 256 bits (32 bytes, random)
|
|
114
|
+
- **IV Length:** 96 bits (12 bytes, random)
|
|
115
|
+
- **Auth Tag:** 128 bits (16 bytes)
|
|
116
|
+
|
|
117
|
+
### 3.2 Encryption Flow
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
Password (user input)
|
|
121
|
+
│
|
|
122
|
+
▼
|
|
123
|
+
┌───────────────────────────────────────┐
|
|
124
|
+
│ scrypt(password, salt, N=16384) │
|
|
125
|
+
│ Output: 32-byte key │
|
|
126
|
+
└───────────────────────────────────────┘
|
|
127
|
+
│
|
|
128
|
+
▼
|
|
129
|
+
┌───────────────────────────────────────┐
|
|
130
|
+
│ AES-256-GCM.encrypt( │
|
|
131
|
+
│ key: derived_key, │
|
|
132
|
+
│ iv: random_12_bytes, │
|
|
133
|
+
│ plaintext: JSON.stringify(data), │
|
|
134
|
+
│ aad: none │
|
|
135
|
+
│ ) │
|
|
136
|
+
│ Output: ciphertext + authTag │
|
|
137
|
+
└───────────────────────────────────────┘
|
|
138
|
+
│
|
|
139
|
+
▼
|
|
140
|
+
{ salt, iv, authTag, data: ciphertext }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 3.3 Decryption Flow
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
{ salt, iv, authTag, data } + Password
|
|
147
|
+
│
|
|
148
|
+
▼
|
|
149
|
+
┌───────────────────────────────────────┐
|
|
150
|
+
│ scrypt(password, salt, N=16384) │
|
|
151
|
+
│ Output: 32-byte key │
|
|
152
|
+
└───────────────────────────────────────┘
|
|
153
|
+
│
|
|
154
|
+
▼
|
|
155
|
+
┌───────────────────────────────────────┐
|
|
156
|
+
│ AES-256-GCM.decrypt( │
|
|
157
|
+
│ key: derived_key, │
|
|
158
|
+
│ iv: iv, │
|
|
159
|
+
│ ciphertext: data, │
|
|
160
|
+
│ authTag: authTag │
|
|
161
|
+
│ ) │
|
|
162
|
+
│ Output: plaintext (or throw error) │
|
|
163
|
+
└───────────────────────────────────────┘
|
|
164
|
+
│
|
|
165
|
+
▼
|
|
166
|
+
JSON.parse(plaintext) → Account[]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 4. UI Components
|
|
172
|
+
|
|
173
|
+
### 4.1 ExportModal
|
|
174
|
+
|
|
175
|
+
**States:**
|
|
176
|
+
1. `format-select` - Choose encrypted or plain
|
|
177
|
+
2. `folder-select` - Choose destination folder
|
|
178
|
+
3. `password-input` - Enter password (only for encrypted)
|
|
179
|
+
4. `exporting` - Show progress
|
|
180
|
+
5. `success` - Show result
|
|
181
|
+
6. `error` - Show error message
|
|
182
|
+
|
|
183
|
+
**Props:**
|
|
184
|
+
```typescript
|
|
185
|
+
interface ExportModalProps {
|
|
186
|
+
accounts: Account[];
|
|
187
|
+
onComplete: (filePath: string) => void;
|
|
188
|
+
onCancel: () => void;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 4.2 ImportModal
|
|
193
|
+
|
|
194
|
+
**States:**
|
|
195
|
+
1. `file-select` - Choose file to import
|
|
196
|
+
2. `password-input` - Enter password (only for .ocam)
|
|
197
|
+
3. `preview` - Show accounts to import with conflict info
|
|
198
|
+
4. `importing` - Show progress
|
|
199
|
+
5. `success` - Show result
|
|
200
|
+
6. `error` - Show error message
|
|
201
|
+
|
|
202
|
+
**Props:**
|
|
203
|
+
```typescript
|
|
204
|
+
interface ImportModalProps {
|
|
205
|
+
existingAccounts: Account[];
|
|
206
|
+
onComplete: (imported: number, overwritten: number) => void;
|
|
207
|
+
onCancel: () => void;
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### 4.3 FileBrowser
|
|
212
|
+
|
|
213
|
+
**Features:**
|
|
214
|
+
- Quick locations (Current Dir, Desktop, Documents, Recent)
|
|
215
|
+
- Folder navigation with arrow keys
|
|
216
|
+
- Text input for pasting path
|
|
217
|
+
- Filter by extension (for import)
|
|
218
|
+
- Show file metadata (size, date, encrypted/plain)
|
|
219
|
+
|
|
220
|
+
**Props:**
|
|
221
|
+
```typescript
|
|
222
|
+
interface FileBrowserProps {
|
|
223
|
+
mode: "folder" | "file";
|
|
224
|
+
initialPath?: string;
|
|
225
|
+
extensions?: string[]; // e.g., [".ocam", ".json"]
|
|
226
|
+
onSelect: (path: string) => void;
|
|
227
|
+
onCancel: () => void;
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### 4.4 PasswordInput
|
|
232
|
+
|
|
233
|
+
**Features:**
|
|
234
|
+
- Masked input (show dots)
|
|
235
|
+
- Optional confirmation field (for export)
|
|
236
|
+
- Password mismatch warning
|
|
237
|
+
- Enter to submit, Escape to cancel
|
|
238
|
+
|
|
239
|
+
**Props:**
|
|
240
|
+
```typescript
|
|
241
|
+
interface PasswordInputProps {
|
|
242
|
+
mode: "single" | "confirm";
|
|
243
|
+
title?: string;
|
|
244
|
+
warning?: string;
|
|
245
|
+
onSubmit: (password: string) => void;
|
|
246
|
+
onCancel: () => void;
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 5. User Flows
|
|
253
|
+
|
|
254
|
+
### 5.1 Export Flow
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
[E] pressed
|
|
258
|
+
│
|
|
259
|
+
▼
|
|
260
|
+
┌─────────────────────────────────┐
|
|
261
|
+
│ Select export format: │
|
|
262
|
+
│ [1] Encrypted (.ocam) │
|
|
263
|
+
│ [2] Plain JSON │
|
|
264
|
+
└─────────────────────────────────┘
|
|
265
|
+
│
|
|
266
|
+
├─── [1] ──────────────────────────────────────┐
|
|
267
|
+
│ │
|
|
268
|
+
▼ ▼
|
|
269
|
+
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
|
|
270
|
+
│ Select folder │ │ Select folder │
|
|
271
|
+
│ (FileBrowser mode="folder") │ │ (FileBrowser mode="folder") │
|
|
272
|
+
└─────────────────────────────────┘ └─────────────────────────────────┘
|
|
273
|
+
│ │
|
|
274
|
+
▼ ▼
|
|
275
|
+
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
|
|
276
|
+
│ Enter password │ │ Save file │
|
|
277
|
+
│ (PasswordInput mode="confirm") │ │ filename: accounts-{date}.json │
|
|
278
|
+
└─────────────────────────────────┘ └─────────────────────────────────┘
|
|
279
|
+
│ │
|
|
280
|
+
▼ ▼
|
|
281
|
+
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
|
|
282
|
+
│ Encrypt & Save │ │ Success message │
|
|
283
|
+
│ filename: accounts-{date}.ocam │ └─────────────────────────────────┘
|
|
284
|
+
└─────────────────────────────────┘
|
|
285
|
+
│
|
|
286
|
+
▼
|
|
287
|
+
┌─────────────────────────────────┐
|
|
288
|
+
│ Success message │
|
|
289
|
+
└─────────────────────────────────┘
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### 5.2 Import Flow
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
[I] pressed
|
|
296
|
+
│
|
|
297
|
+
▼
|
|
298
|
+
┌─────────────────────────────────┐
|
|
299
|
+
│ Select file │
|
|
300
|
+
│ (FileBrowser mode="file") │
|
|
301
|
+
│ extensions: [".ocam", ".json"] │
|
|
302
|
+
└─────────────────────────────────┘
|
|
303
|
+
│
|
|
304
|
+
├─── .ocam ────────────────────────────────────┐
|
|
305
|
+
│ │
|
|
306
|
+
▼ ▼
|
|
307
|
+
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
|
|
308
|
+
│ Enter password │ │ .json file │
|
|
309
|
+
│ (PasswordInput mode="single") │ │ Parse directly │
|
|
310
|
+
└─────────────────────────────────┘ └─────────────────────────────────┘
|
|
311
|
+
│ │
|
|
312
|
+
├─── Wrong password ───┐ │
|
|
313
|
+
│ ▼ │
|
|
314
|
+
│ ┌─────────────────────────────────┐ │
|
|
315
|
+
│ │ Error: Invalid password │ │
|
|
316
|
+
│ │ [Try again] [Cancel] │ │
|
|
317
|
+
│ └─────────────────────────────────┘ │
|
|
318
|
+
│ │
|
|
319
|
+
▼ ▼
|
|
320
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
321
|
+
│ Preview accounts │
|
|
322
|
+
│ Show: email, exists? (will overwrite) │
|
|
323
|
+
│ [Enter] Import [Esc] Cancel │
|
|
324
|
+
└─────────────────────────────────────────────────────────────┘
|
|
325
|
+
│
|
|
326
|
+
▼
|
|
327
|
+
┌─────────────────────────────────┐
|
|
328
|
+
│ Import accounts (overwrite) │
|
|
329
|
+
└─────────────────────────────────┘
|
|
330
|
+
│
|
|
331
|
+
▼
|
|
332
|
+
┌─────────────────────────────────┐
|
|
333
|
+
│ Success: X imported, Y new │
|
|
334
|
+
└─────────────────────────────────┘
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## 6. Implementation Checklist
|
|
340
|
+
|
|
341
|
+
### Core Layer
|
|
342
|
+
- [ ] `src/core/crypto.ts`
|
|
343
|
+
- [ ] `generateSalt(): string`
|
|
344
|
+
- [ ] `generateIV(): string`
|
|
345
|
+
- [ ] `deriveKey(password: string, salt: string): Buffer`
|
|
346
|
+
- [ ] `encrypt(data: object, password: string): EncryptedData`
|
|
347
|
+
- [ ] `decrypt(encrypted: EncryptedData, password: string): object`
|
|
348
|
+
|
|
349
|
+
- [ ] `src/core/config-store.ts`
|
|
350
|
+
- [ ] `getConfigPath(): string`
|
|
351
|
+
- [ ] `readConfig(): AppConfig`
|
|
352
|
+
- [ ] `writeConfig(config: AppConfig): void`
|
|
353
|
+
- [ ] `updateLastExportFolder(folder: string): void`
|
|
354
|
+
- [ ] `updateLastImportFolder(folder: string): void`
|
|
355
|
+
- [ ] `getRecentFolders(): string[]`
|
|
356
|
+
|
|
357
|
+
- [ ] `src/core/types.ts`
|
|
358
|
+
- [ ] `EncryptedExportFile` interface
|
|
359
|
+
- [ ] `AppConfig` interface
|
|
360
|
+
- [ ] Update `PortableExportFile.exportedFrom`
|
|
361
|
+
|
|
362
|
+
- [ ] `src/core/accounts.ts`
|
|
363
|
+
- [ ] `encryptAndExport(accounts: Account[], password: string): EncryptedExportFile`
|
|
364
|
+
- [ ] `decryptAndImport(file: EncryptedExportFile, password: string): Account[]`
|
|
365
|
+
- [ ] `isEncryptedFile(data: unknown): boolean`
|
|
366
|
+
|
|
367
|
+
### TUI Layer
|
|
368
|
+
- [ ] `src/tui/components/PasswordInput.tsx`
|
|
369
|
+
- [ ] Masked input display
|
|
370
|
+
- [ ] Confirm mode (two fields)
|
|
371
|
+
- [ ] Mismatch warning
|
|
372
|
+
- [ ] Enter/Escape handling
|
|
373
|
+
|
|
374
|
+
- [ ] `src/tui/components/FileBrowser.tsx`
|
|
375
|
+
- [ ] Quick locations list
|
|
376
|
+
- [ ] Folder navigation
|
|
377
|
+
- [ ] Text input for path
|
|
378
|
+
- [ ] File filtering by extension
|
|
379
|
+
- [ ] File metadata display
|
|
380
|
+
|
|
381
|
+
- [ ] `src/tui/components/ExportModal.tsx`
|
|
382
|
+
- [ ] Format selection step
|
|
383
|
+
- [ ] Folder selection step
|
|
384
|
+
- [ ] Password input step
|
|
385
|
+
- [ ] Export execution
|
|
386
|
+
- [ ] Success/Error display
|
|
387
|
+
|
|
388
|
+
- [ ] `src/tui/components/ImportModal.tsx`
|
|
389
|
+
- [ ] File selection step
|
|
390
|
+
- [ ] Password input step (if encrypted)
|
|
391
|
+
- [ ] Preview with conflict detection
|
|
392
|
+
- [ ] Import execution
|
|
393
|
+
- [ ] Success/Error display
|
|
394
|
+
|
|
395
|
+
- [ ] `src/tui/Dashboard.tsx`
|
|
396
|
+
- [ ] Modal state management
|
|
397
|
+
- [ ] Export handler
|
|
398
|
+
- [ ] Import handler
|
|
399
|
+
|
|
400
|
+
- [ ] `src/tui/components/Menu.tsx`
|
|
401
|
+
- [ ] Update export action
|
|
402
|
+
- [ ] Update import action
|
|
403
|
+
|
|
404
|
+
- [ ] `src/tui/components/index.ts`
|
|
405
|
+
- [ ] Export new components
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## 7. Testing
|
|
410
|
+
|
|
411
|
+
### Manual Test Cases
|
|
412
|
+
|
|
413
|
+
1. **Export Encrypted**
|
|
414
|
+
- Export 5 accounts with password "test123"
|
|
415
|
+
- Verify .ocam file created
|
|
416
|
+
- Verify file content is encrypted (not readable)
|
|
417
|
+
|
|
418
|
+
2. **Import Encrypted**
|
|
419
|
+
- Import the exported .ocam file
|
|
420
|
+
- Enter correct password → success
|
|
421
|
+
- Enter wrong password → error with retry option
|
|
422
|
+
|
|
423
|
+
3. **Export Plain**
|
|
424
|
+
- Export 5 accounts as plain JSON
|
|
425
|
+
- Verify .json file created
|
|
426
|
+
- Verify accounts are readable in file
|
|
427
|
+
|
|
428
|
+
4. **Import Plain**
|
|
429
|
+
- Import a plain .json file
|
|
430
|
+
- No password required
|
|
431
|
+
- Accounts imported successfully
|
|
432
|
+
|
|
433
|
+
5. **Overwrite Existing**
|
|
434
|
+
- Export 3 accounts
|
|
435
|
+
- Modify 1 account locally
|
|
436
|
+
- Import the file
|
|
437
|
+
- Verify the account is overwritten
|
|
438
|
+
|
|
439
|
+
6. **Remember Folder**
|
|
440
|
+
- Export to custom folder
|
|
441
|
+
- Close and reopen app
|
|
442
|
+
- Export again → should show last folder as recent
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## 8. Security Considerations
|
|
447
|
+
|
|
448
|
+
1. **Password not stored** - Never save password to disk
|
|
449
|
+
2. **Memory cleanup** - Clear password from memory after use
|
|
450
|
+
3. **Auth tag verification** - Detect tampered files
|
|
451
|
+
4. **No password hints** - Don't store any password metadata
|
|
452
|
+
5. **Salt per file** - Each export uses unique salt
|
|
453
|
+
6. **Warning on plain export** - Show security warning
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## 9. Error Handling
|
|
458
|
+
|
|
459
|
+
| Error | User Message |
|
|
460
|
+
|-------|--------------|
|
|
461
|
+
| Wrong password | "Invalid password. Please try again." |
|
|
462
|
+
| Corrupted file | "File is corrupted or invalid format." |
|
|
463
|
+
| File not found | "File not found: {path}" |
|
|
464
|
+
| Permission denied | "Cannot write to folder: {path}" |
|
|
465
|
+
| Disk full | "Not enough disk space." |
|
|
466
|
+
| Invalid JSON | "Invalid file format." |
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## 10. Dependencies
|
|
471
|
+
|
|
472
|
+
No new npm packages required. Using Node.js built-in:
|
|
473
|
+
- `crypto` - AES-256-GCM, scrypt
|
|
474
|
+
- `fs` - File operations
|
|
475
|
+
- `path` - Path manipulation
|
|
476
|
+
- `os` - Home directory, platform detection
|
package/docs/ROADMAP.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
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 (Current)
|
|
27
|
+
- [ ] **Encrypted Export** - AES-256-GCM with password protection
|
|
28
|
+
- [ ] **Plain JSON Export** - Keep original format as option
|
|
29
|
+
- [ ] **File Browser UI** - Browse folders, quick locations, paste path
|
|
30
|
+
- [ ] **Import from File** - Select .ocam or .json files
|
|
31
|
+
- [ ] **Password Input** - Masked input with confirmation
|
|
32
|
+
- [ ] **Remember Preferences** - Last export/import folder saved
|
|
33
|
+
- [ ] **Overwrite Mode** - Replace existing accounts on import
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Future Ideas (Backlog)
|
|
38
|
+
|
|
39
|
+
### v0.5.0 - Enhanced Security
|
|
40
|
+
- [ ] Password strength indicator
|
|
41
|
+
- [ ] Auto-lock after inactivity
|
|
42
|
+
- [ ] Encrypted storage for config
|
|
43
|
+
|
|
44
|
+
### v0.6.0 - Cloud Sync
|
|
45
|
+
- [ ] Sync accounts to cloud storage (Google Drive, Dropbox)
|
|
46
|
+
- [ ] Multi-device sync
|
|
47
|
+
- [ ] Conflict resolution
|
|
48
|
+
|
|
49
|
+
### v0.7.0 - Account Health
|
|
50
|
+
- [ ] Check if refresh tokens are still valid
|
|
51
|
+
- [ ] Auto-refresh expired tokens
|
|
52
|
+
- [ ] Health check on startup
|
|
53
|
+
|
|
54
|
+
### v0.8.0 - MCP Management
|
|
55
|
+
- [ ] Enable/Disable MCP servers from TUI
|
|
56
|
+
- [ ] Add new MCP servers
|
|
57
|
+
- [ ] View MCP server logs
|
|
58
|
+
|
|
59
|
+
### v0.9.0 - Provider Management
|
|
60
|
+
- [ ] Add/Edit custom providers
|
|
61
|
+
- [ ] Test provider connection
|
|
62
|
+
- [ ] Model usage statistics
|
|
63
|
+
|
|
64
|
+
### v1.0.0 - Stable Release
|
|
65
|
+
- [ ] Full documentation
|
|
66
|
+
- [ ] npm publish
|
|
67
|
+
- [ ] Windows/Mac/Linux installers
|
|
68
|
+
- [ ] Integration tests
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Contributing
|
|
73
|
+
|
|
74
|
+
See [BLUEPRINT.md](./BLUEPRINT.md) for technical architecture and implementation details.
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-account-manager",
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "TUI Dashboard for OpenCode - View providers, MCP servers, and plugin accounts",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ocam": "./dist/cli.js",
|
|
8
|
+
"opencode-account-manager": "./dist/cli.js"
|
|
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
|
+
}
|