claude-wec 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/LICENSE +675 -0
  2. package/README.md +371 -0
  3. package/dist/api-docs.html +879 -0
  4. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  5. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  6. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  7. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  8. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  9. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  10. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  11. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  12. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  13. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  14. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  15. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  16. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  17. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  18. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  19. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  20. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  21. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  22. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  23. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  24. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  25. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  26. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  27. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  28. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  29. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  30. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  31. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  32. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  33. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  34. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  35. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  36. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  37. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  38. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  39. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  40. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  41. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  42. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  44. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  45. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  46. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  47. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  48. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  49. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  50. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  51. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  52. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  53. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  54. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  55. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  56. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  57. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  58. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  59. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  60. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  61. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  62. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  63. package/dist/assets/index-cIxJ4RXb.js +1226 -0
  64. package/dist/assets/index-oyEz69sP.css +32 -0
  65. package/dist/assets/vendor-codemirror-CJLzwpLB.js +39 -0
  66. package/dist/assets/vendor-react-DcyRfQm3.js +59 -0
  67. package/dist/assets/vendor-xterm-DfaPXD3y.js +66 -0
  68. package/dist/clear-cache.html +85 -0
  69. package/dist/convert-icons.md +53 -0
  70. package/dist/favicon.png +0 -0
  71. package/dist/favicon.svg +9 -0
  72. package/dist/generate-icons.js +49 -0
  73. package/dist/icons/claude-ai-icon.svg +1 -0
  74. package/dist/icons/codex-white.svg +3 -0
  75. package/dist/icons/codex.svg +3 -0
  76. package/dist/icons/cursor-white.svg +12 -0
  77. package/dist/icons/cursor.svg +1 -0
  78. package/dist/icons/generate-icons.md +19 -0
  79. package/dist/icons/icon-128x128.png +0 -0
  80. package/dist/icons/icon-128x128.svg +12 -0
  81. package/dist/icons/icon-144x144.png +0 -0
  82. package/dist/icons/icon-144x144.svg +12 -0
  83. package/dist/icons/icon-152x152.png +0 -0
  84. package/dist/icons/icon-152x152.svg +12 -0
  85. package/dist/icons/icon-192x192.png +0 -0
  86. package/dist/icons/icon-192x192.svg +12 -0
  87. package/dist/icons/icon-384x384.png +0 -0
  88. package/dist/icons/icon-384x384.svg +12 -0
  89. package/dist/icons/icon-512x512.png +0 -0
  90. package/dist/icons/icon-512x512.svg +12 -0
  91. package/dist/icons/icon-72x72.png +0 -0
  92. package/dist/icons/icon-72x72.svg +12 -0
  93. package/dist/icons/icon-96x96.png +0 -0
  94. package/dist/icons/icon-96x96.svg +12 -0
  95. package/dist/icons/icon-template.svg +12 -0
  96. package/dist/index.html +52 -0
  97. package/dist/logo-128.png +0 -0
  98. package/dist/logo-256.png +0 -0
  99. package/dist/logo-32.png +0 -0
  100. package/dist/logo-512.png +0 -0
  101. package/dist/logo-64.png +0 -0
  102. package/dist/logo.svg +17 -0
  103. package/dist/manifest.json +61 -0
  104. package/dist/screenshots/cli-selection.png +0 -0
  105. package/dist/screenshots/desktop-main.png +0 -0
  106. package/dist/screenshots/mobile-chat.png +0 -0
  107. package/dist/screenshots/tools-modal.png +0 -0
  108. package/dist/sw.js +49 -0
  109. package/package.json +109 -0
  110. package/server/claude-sdk.js +721 -0
  111. package/server/cli.js +327 -0
  112. package/server/cursor-cli.js +267 -0
  113. package/server/database/auth.db +0 -0
  114. package/server/database/db.js +361 -0
  115. package/server/database/init.sql +52 -0
  116. package/server/index.js +1747 -0
  117. package/server/middleware/auth.js +111 -0
  118. package/server/openai-codex.js +389 -0
  119. package/server/projects.js +1604 -0
  120. package/server/routes/agent.js +1230 -0
  121. package/server/routes/auth.js +135 -0
  122. package/server/routes/cli-auth.js +263 -0
  123. package/server/routes/codex.js +345 -0
  124. package/server/routes/commands.js +521 -0
  125. package/server/routes/cursor.js +795 -0
  126. package/server/routes/git.js +1128 -0
  127. package/server/routes/mcp-utils.js +48 -0
  128. package/server/routes/mcp.js +552 -0
  129. package/server/routes/projects.js +378 -0
  130. package/server/routes/settings.js +178 -0
  131. package/server/routes/taskmaster.js +1963 -0
  132. package/server/routes/user.js +106 -0
  133. package/server/utils/commandParser.js +303 -0
  134. package/server/utils/gitConfig.js +24 -0
  135. package/server/utils/mcp-detector.js +198 -0
  136. package/server/utils/taskmaster-websocket.js +129 -0
  137. package/shared/modelConstants.js +65 -0
@@ -0,0 +1,361 @@
1
+ import Database from 'better-sqlite3';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import crypto from 'crypto';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname } from 'path';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ // ANSI color codes for terminal output
12
+ const colors = {
13
+ reset: '\x1b[0m',
14
+ bright: '\x1b[1m',
15
+ cyan: '\x1b[36m',
16
+ dim: '\x1b[2m',
17
+ };
18
+
19
+ const c = {
20
+ info: (text) => `${colors.cyan}${text}${colors.reset}`,
21
+ bright: (text) => `${colors.bright}${text}${colors.reset}`,
22
+ dim: (text) => `${colors.dim}${text}${colors.reset}`,
23
+ };
24
+
25
+ // Use DATABASE_PATH environment variable if set, otherwise use default location
26
+ const DB_PATH = process.env.DATABASE_PATH || path.join(__dirname, 'auth.db');
27
+ const INIT_SQL_PATH = path.join(__dirname, 'init.sql');
28
+
29
+ // Ensure database directory exists if custom path is provided
30
+ if (process.env.DATABASE_PATH) {
31
+ const dbDir = path.dirname(DB_PATH);
32
+ try {
33
+ if (!fs.existsSync(dbDir)) {
34
+ fs.mkdirSync(dbDir, { recursive: true });
35
+ console.log(`Created database directory: ${dbDir}`);
36
+ }
37
+ } catch (error) {
38
+ console.error(`Failed to create database directory ${dbDir}:`, error.message);
39
+ throw error;
40
+ }
41
+ }
42
+
43
+ // Create database connection
44
+ const db = new Database(DB_PATH);
45
+
46
+ // Show app installation path prominently
47
+ const appInstallPath = path.join(__dirname, '../..');
48
+ console.log('');
49
+ console.log(c.dim('═'.repeat(60)));
50
+ console.log(`${c.info('[INFO]')} App Installation: ${c.bright(appInstallPath)}`);
51
+ console.log(`${c.info('[INFO]')} Database: ${c.dim(path.relative(appInstallPath, DB_PATH))}`);
52
+ if (process.env.DATABASE_PATH) {
53
+ console.log(` ${c.dim('(Using custom DATABASE_PATH from environment)')}`);
54
+ }
55
+ console.log(c.dim('═'.repeat(60)));
56
+ console.log('');
57
+
58
+ const runMigrations = () => {
59
+ try {
60
+ const tableInfo = db.prepare("PRAGMA table_info(users)").all();
61
+ const columnNames = tableInfo.map(col => col.name);
62
+
63
+ if (!columnNames.includes('git_name')) {
64
+ console.log('Running migration: Adding git_name column');
65
+ db.exec('ALTER TABLE users ADD COLUMN git_name TEXT');
66
+ }
67
+
68
+ if (!columnNames.includes('git_email')) {
69
+ console.log('Running migration: Adding git_email column');
70
+ db.exec('ALTER TABLE users ADD COLUMN git_email TEXT');
71
+ }
72
+
73
+ if (!columnNames.includes('has_completed_onboarding')) {
74
+ console.log('Running migration: Adding has_completed_onboarding column');
75
+ db.exec('ALTER TABLE users ADD COLUMN has_completed_onboarding BOOLEAN DEFAULT 0');
76
+ }
77
+
78
+ console.log('Database migrations completed successfully');
79
+ } catch (error) {
80
+ console.error('Error running migrations:', error.message);
81
+ throw error;
82
+ }
83
+ };
84
+
85
+ // Initialize database with schema
86
+ const initializeDatabase = async () => {
87
+ try {
88
+ const initSQL = fs.readFileSync(INIT_SQL_PATH, 'utf8');
89
+ db.exec(initSQL);
90
+ console.log('Database initialized successfully');
91
+ runMigrations();
92
+ } catch (error) {
93
+ console.error('Error initializing database:', error.message);
94
+ throw error;
95
+ }
96
+ };
97
+
98
+ // User database operations
99
+ const userDb = {
100
+ // Check if any users exist
101
+ hasUsers: () => {
102
+ try {
103
+ const row = db.prepare('SELECT COUNT(*) as count FROM users').get();
104
+ return row.count > 0;
105
+ } catch (err) {
106
+ throw err;
107
+ }
108
+ },
109
+
110
+ // Create a new user
111
+ createUser: (username, passwordHash) => {
112
+ try {
113
+ const stmt = db.prepare('INSERT INTO users (username, password_hash) VALUES (?, ?)');
114
+ const result = stmt.run(username, passwordHash);
115
+ return { id: result.lastInsertRowid, username };
116
+ } catch (err) {
117
+ throw err;
118
+ }
119
+ },
120
+
121
+ // Get user by username
122
+ getUserByUsername: (username) => {
123
+ try {
124
+ const row = db.prepare('SELECT * FROM users WHERE username = ? AND is_active = 1').get(username);
125
+ return row;
126
+ } catch (err) {
127
+ throw err;
128
+ }
129
+ },
130
+
131
+ // Update last login time
132
+ updateLastLogin: (userId) => {
133
+ try {
134
+ db.prepare('UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?').run(userId);
135
+ } catch (err) {
136
+ throw err;
137
+ }
138
+ },
139
+
140
+ // Get user by ID
141
+ getUserById: (userId) => {
142
+ try {
143
+ const row = db.prepare('SELECT id, username, created_at, last_login FROM users WHERE id = ? AND is_active = 1').get(userId);
144
+ return row;
145
+ } catch (err) {
146
+ throw err;
147
+ }
148
+ },
149
+
150
+ getFirstUser: () => {
151
+ try {
152
+ const row = db.prepare('SELECT id, username, created_at, last_login FROM users WHERE is_active = 1 LIMIT 1').get();
153
+ return row;
154
+ } catch (err) {
155
+ throw err;
156
+ }
157
+ },
158
+
159
+ updateGitConfig: (userId, gitName, gitEmail) => {
160
+ try {
161
+ const stmt = db.prepare('UPDATE users SET git_name = ?, git_email = ? WHERE id = ?');
162
+ stmt.run(gitName, gitEmail, userId);
163
+ } catch (err) {
164
+ throw err;
165
+ }
166
+ },
167
+
168
+ getGitConfig: (userId) => {
169
+ try {
170
+ const row = db.prepare('SELECT git_name, git_email FROM users WHERE id = ?').get(userId);
171
+ return row;
172
+ } catch (err) {
173
+ throw err;
174
+ }
175
+ },
176
+
177
+ completeOnboarding: (userId) => {
178
+ try {
179
+ const stmt = db.prepare('UPDATE users SET has_completed_onboarding = 1 WHERE id = ?');
180
+ stmt.run(userId);
181
+ } catch (err) {
182
+ throw err;
183
+ }
184
+ },
185
+
186
+ hasCompletedOnboarding: (userId) => {
187
+ try {
188
+ const row = db.prepare('SELECT has_completed_onboarding FROM users WHERE id = ?').get(userId);
189
+ return row?.has_completed_onboarding === 1;
190
+ } catch (err) {
191
+ throw err;
192
+ }
193
+ }
194
+ };
195
+
196
+ // API Keys database operations
197
+ const apiKeysDb = {
198
+ // Generate a new API key
199
+ generateApiKey: () => {
200
+ return 'ck_' + crypto.randomBytes(32).toString('hex');
201
+ },
202
+
203
+ // Create a new API key
204
+ createApiKey: (userId, keyName) => {
205
+ try {
206
+ const apiKey = apiKeysDb.generateApiKey();
207
+ const stmt = db.prepare('INSERT INTO api_keys (user_id, key_name, api_key) VALUES (?, ?, ?)');
208
+ const result = stmt.run(userId, keyName, apiKey);
209
+ return { id: result.lastInsertRowid, keyName, apiKey };
210
+ } catch (err) {
211
+ throw err;
212
+ }
213
+ },
214
+
215
+ // Get all API keys for a user
216
+ getApiKeys: (userId) => {
217
+ try {
218
+ const rows = db.prepare('SELECT id, key_name, api_key, created_at, last_used, is_active FROM api_keys WHERE user_id = ? ORDER BY created_at DESC').all(userId);
219
+ return rows;
220
+ } catch (err) {
221
+ throw err;
222
+ }
223
+ },
224
+
225
+ // Validate API key and get user
226
+ validateApiKey: (apiKey) => {
227
+ try {
228
+ const row = db.prepare(`
229
+ SELECT u.id, u.username, ak.id as api_key_id
230
+ FROM api_keys ak
231
+ JOIN users u ON ak.user_id = u.id
232
+ WHERE ak.api_key = ? AND ak.is_active = 1 AND u.is_active = 1
233
+ `).get(apiKey);
234
+
235
+ if (row) {
236
+ // Update last_used timestamp
237
+ db.prepare('UPDATE api_keys SET last_used = CURRENT_TIMESTAMP WHERE id = ?').run(row.api_key_id);
238
+ }
239
+
240
+ return row;
241
+ } catch (err) {
242
+ throw err;
243
+ }
244
+ },
245
+
246
+ // Delete an API key
247
+ deleteApiKey: (userId, apiKeyId) => {
248
+ try {
249
+ const stmt = db.prepare('DELETE FROM api_keys WHERE id = ? AND user_id = ?');
250
+ const result = stmt.run(apiKeyId, userId);
251
+ return result.changes > 0;
252
+ } catch (err) {
253
+ throw err;
254
+ }
255
+ },
256
+
257
+ // Toggle API key active status
258
+ toggleApiKey: (userId, apiKeyId, isActive) => {
259
+ try {
260
+ const stmt = db.prepare('UPDATE api_keys SET is_active = ? WHERE id = ? AND user_id = ?');
261
+ const result = stmt.run(isActive ? 1 : 0, apiKeyId, userId);
262
+ return result.changes > 0;
263
+ } catch (err) {
264
+ throw err;
265
+ }
266
+ }
267
+ };
268
+
269
+ // User credentials database operations (for GitHub tokens, GitLab tokens, etc.)
270
+ const credentialsDb = {
271
+ // Create a new credential
272
+ createCredential: (userId, credentialName, credentialType, credentialValue, description = null) => {
273
+ try {
274
+ const stmt = db.prepare('INSERT INTO user_credentials (user_id, credential_name, credential_type, credential_value, description) VALUES (?, ?, ?, ?, ?)');
275
+ const result = stmt.run(userId, credentialName, credentialType, credentialValue, description);
276
+ return { id: result.lastInsertRowid, credentialName, credentialType };
277
+ } catch (err) {
278
+ throw err;
279
+ }
280
+ },
281
+
282
+ // Get all credentials for a user, optionally filtered by type
283
+ getCredentials: (userId, credentialType = null) => {
284
+ try {
285
+ let query = 'SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ?';
286
+ const params = [userId];
287
+
288
+ if (credentialType) {
289
+ query += ' AND credential_type = ?';
290
+ params.push(credentialType);
291
+ }
292
+
293
+ query += ' ORDER BY created_at DESC';
294
+
295
+ const rows = db.prepare(query).all(...params);
296
+ return rows;
297
+ } catch (err) {
298
+ throw err;
299
+ }
300
+ },
301
+
302
+ // Get active credential value for a user by type (returns most recent active)
303
+ getActiveCredential: (userId, credentialType) => {
304
+ try {
305
+ const row = db.prepare('SELECT credential_value FROM user_credentials WHERE user_id = ? AND credential_type = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1').get(userId, credentialType);
306
+ return row?.credential_value || null;
307
+ } catch (err) {
308
+ throw err;
309
+ }
310
+ },
311
+
312
+ // Delete a credential
313
+ deleteCredential: (userId, credentialId) => {
314
+ try {
315
+ const stmt = db.prepare('DELETE FROM user_credentials WHERE id = ? AND user_id = ?');
316
+ const result = stmt.run(credentialId, userId);
317
+ return result.changes > 0;
318
+ } catch (err) {
319
+ throw err;
320
+ }
321
+ },
322
+
323
+ // Toggle credential active status
324
+ toggleCredential: (userId, credentialId, isActive) => {
325
+ try {
326
+ const stmt = db.prepare('UPDATE user_credentials SET is_active = ? WHERE id = ? AND user_id = ?');
327
+ const result = stmt.run(isActive ? 1 : 0, credentialId, userId);
328
+ return result.changes > 0;
329
+ } catch (err) {
330
+ throw err;
331
+ }
332
+ }
333
+ };
334
+
335
+ // Backward compatibility - keep old names pointing to new system
336
+ const githubTokensDb = {
337
+ createGithubToken: (userId, tokenName, githubToken, description = null) => {
338
+ return credentialsDb.createCredential(userId, tokenName, 'github_token', githubToken, description);
339
+ },
340
+ getGithubTokens: (userId) => {
341
+ return credentialsDb.getCredentials(userId, 'github_token');
342
+ },
343
+ getActiveGithubToken: (userId) => {
344
+ return credentialsDb.getActiveCredential(userId, 'github_token');
345
+ },
346
+ deleteGithubToken: (userId, tokenId) => {
347
+ return credentialsDb.deleteCredential(userId, tokenId);
348
+ },
349
+ toggleGithubToken: (userId, tokenId, isActive) => {
350
+ return credentialsDb.toggleCredential(userId, tokenId, isActive);
351
+ }
352
+ };
353
+
354
+ export {
355
+ db,
356
+ initializeDatabase,
357
+ userDb,
358
+ apiKeysDb,
359
+ credentialsDb,
360
+ githubTokensDb // Backward compatibility
361
+ };
@@ -0,0 +1,52 @@
1
+ -- Initialize authentication database
2
+ PRAGMA foreign_keys = ON;
3
+
4
+ -- Users table (single user system)
5
+ CREATE TABLE IF NOT EXISTS users (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ username TEXT UNIQUE NOT NULL,
8
+ password_hash TEXT NOT NULL,
9
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
10
+ last_login DATETIME,
11
+ is_active BOOLEAN DEFAULT 1,
12
+ git_name TEXT,
13
+ git_email TEXT,
14
+ has_completed_onboarding BOOLEAN DEFAULT 0
15
+ );
16
+
17
+ -- Indexes for performance
18
+ CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
19
+ CREATE INDEX IF NOT EXISTS idx_users_active ON users(is_active);
20
+
21
+ -- API Keys table for external API access
22
+ CREATE TABLE IF NOT EXISTS api_keys (
23
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
24
+ user_id INTEGER NOT NULL,
25
+ key_name TEXT NOT NULL,
26
+ api_key TEXT UNIQUE NOT NULL,
27
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
28
+ last_used DATETIME,
29
+ is_active BOOLEAN DEFAULT 1,
30
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
31
+ );
32
+
33
+ CREATE INDEX IF NOT EXISTS idx_api_keys_key ON api_keys(api_key);
34
+ CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id);
35
+ CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(is_active);
36
+
37
+ -- User credentials table for storing various tokens/credentials (GitHub, GitLab, etc.)
38
+ CREATE TABLE IF NOT EXISTS user_credentials (
39
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
40
+ user_id INTEGER NOT NULL,
41
+ credential_name TEXT NOT NULL,
42
+ credential_type TEXT NOT NULL, -- 'github_token', 'gitlab_token', 'bitbucket_token', etc.
43
+ credential_value TEXT NOT NULL,
44
+ description TEXT,
45
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
46
+ is_active BOOLEAN DEFAULT 1,
47
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
48
+ );
49
+
50
+ CREATE INDEX IF NOT EXISTS idx_user_credentials_user_id ON user_credentials(user_id);
51
+ CREATE INDEX IF NOT EXISTS idx_user_credentials_type ON user_credentials(credential_type);
52
+ CREATE INDEX IF NOT EXISTS idx_user_credentials_active ON user_credentials(is_active);