@siteboon/claude-code-ui 1.21.0 → 1.23.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.
- package/README.md +82 -213
- package/dist/assets/index-7_J3n3lH.js +1204 -0
- package/dist/assets/index-BFyod1Qa.css +32 -0
- package/dist/assets/{vendor-codemirror-BMLq5tLB.js → vendor-codemirror-C8f1vU1x.js} +9 -9
- package/dist/assets/{vendor-react-DIN4KjD2.js → vendor-react-CdSTmIF1.js} +1 -1
- package/dist/index.html +6 -6
- package/package.json +23 -2
- package/server/claude-sdk.js +63 -11
- package/server/database/db.js +68 -0
- package/server/database/init.sql +14 -1
- package/server/index.js +486 -6
- package/server/projects.js +53 -14
- package/server/routes/cli-auth.js +23 -4
- package/server/routes/codex.js +3 -0
- package/server/routes/cursor.js +5 -2
- package/server/routes/gemini.js +2 -0
- package/shared/modelConstants.js +6 -2
- package/dist/assets/index-Cxnz_sny.css +0 -32
- package/dist/assets/index-DN2ZJcRJ.js +0 -1381
package/server/projects.js
CHANGED
|
@@ -66,6 +66,7 @@ import sqlite3 from 'sqlite3';
|
|
|
66
66
|
import { open } from 'sqlite';
|
|
67
67
|
import os from 'os';
|
|
68
68
|
import sessionManager from './sessionManager.js';
|
|
69
|
+
import { applyCustomSessionNames } from './database/db.js';
|
|
69
70
|
|
|
70
71
|
// Import TaskMaster detection functions
|
|
71
72
|
async function detectTaskMasterFolder(projectPath) {
|
|
@@ -458,6 +459,7 @@ async function getProjects(progressCallback = null) {
|
|
|
458
459
|
total: 0
|
|
459
460
|
};
|
|
460
461
|
}
|
|
462
|
+
applyCustomSessionNames(project.sessions, 'claude');
|
|
461
463
|
|
|
462
464
|
// Also fetch Cursor sessions for this project
|
|
463
465
|
try {
|
|
@@ -466,6 +468,7 @@ async function getProjects(progressCallback = null) {
|
|
|
466
468
|
console.warn(`Could not load Cursor sessions for project ${entry.name}:`, e.message);
|
|
467
469
|
project.cursorSessions = [];
|
|
468
470
|
}
|
|
471
|
+
applyCustomSessionNames(project.cursorSessions, 'cursor');
|
|
469
472
|
|
|
470
473
|
// Also fetch Codex sessions for this project
|
|
471
474
|
try {
|
|
@@ -476,6 +479,7 @@ async function getProjects(progressCallback = null) {
|
|
|
476
479
|
console.warn(`Could not load Codex sessions for project ${entry.name}:`, e.message);
|
|
477
480
|
project.codexSessions = [];
|
|
478
481
|
}
|
|
482
|
+
applyCustomSessionNames(project.codexSessions, 'codex');
|
|
479
483
|
|
|
480
484
|
// Also fetch Gemini sessions for this project
|
|
481
485
|
try {
|
|
@@ -484,6 +488,7 @@ async function getProjects(progressCallback = null) {
|
|
|
484
488
|
console.warn(`Could not load Gemini sessions for project ${entry.name}:`, e.message);
|
|
485
489
|
project.geminiSessions = [];
|
|
486
490
|
}
|
|
491
|
+
applyCustomSessionNames(project.geminiSessions, 'gemini');
|
|
487
492
|
|
|
488
493
|
// Add TaskMaster detection
|
|
489
494
|
try {
|
|
@@ -567,6 +572,7 @@ async function getProjects(progressCallback = null) {
|
|
|
567
572
|
} catch (e) {
|
|
568
573
|
console.warn(`Could not load Cursor sessions for manual project ${projectName}:`, e.message);
|
|
569
574
|
}
|
|
575
|
+
applyCustomSessionNames(project.cursorSessions, 'cursor');
|
|
570
576
|
|
|
571
577
|
// Try to fetch Codex sessions for manual projects too
|
|
572
578
|
try {
|
|
@@ -576,6 +582,7 @@ async function getProjects(progressCallback = null) {
|
|
|
576
582
|
} catch (e) {
|
|
577
583
|
console.warn(`Could not load Codex sessions for manual project ${projectName}:`, e.message);
|
|
578
584
|
}
|
|
585
|
+
applyCustomSessionNames(project.codexSessions, 'codex');
|
|
579
586
|
|
|
580
587
|
// Try to fetch Gemini sessions for manual projects too
|
|
581
588
|
try {
|
|
@@ -583,6 +590,7 @@ async function getProjects(progressCallback = null) {
|
|
|
583
590
|
} catch (e) {
|
|
584
591
|
console.warn(`Could not load Gemini sessions for manual project ${projectName}:`, e.message);
|
|
585
592
|
}
|
|
593
|
+
applyCustomSessionNames(project.geminiSessions, 'gemini');
|
|
586
594
|
|
|
587
595
|
// Add TaskMaster detection for manual projects
|
|
588
596
|
try {
|
|
@@ -1071,10 +1079,13 @@ async function renameProject(projectName, newDisplayName) {
|
|
|
1071
1079
|
|
|
1072
1080
|
if (!newDisplayName || newDisplayName.trim() === '') {
|
|
1073
1081
|
// Remove custom name if empty, will fall back to auto-generated
|
|
1074
|
-
|
|
1082
|
+
if (config[projectName]) {
|
|
1083
|
+
delete config[projectName].displayName;
|
|
1084
|
+
}
|
|
1075
1085
|
} else {
|
|
1076
|
-
// Set custom display name
|
|
1086
|
+
// Set custom display name, preserving other properties (manuallyAdded, originalPath)
|
|
1077
1087
|
config[projectName] = {
|
|
1088
|
+
...config[projectName],
|
|
1078
1089
|
displayName: newDisplayName.trim()
|
|
1079
1090
|
};
|
|
1080
1091
|
}
|
|
@@ -1479,6 +1490,23 @@ async function getCodexSessions(projectPath, options = {}) {
|
|
|
1479
1490
|
}
|
|
1480
1491
|
}
|
|
1481
1492
|
|
|
1493
|
+
function isVisibleCodexUserMessage(payload) {
|
|
1494
|
+
if (!payload || payload.type !== 'user_message') {
|
|
1495
|
+
return false;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// Codex logs internal context (environment, instructions) as non-plain user_message kinds.
|
|
1499
|
+
if (payload.kind && payload.kind !== 'plain') {
|
|
1500
|
+
return false;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
if (typeof payload.message !== 'string' || payload.message.trim().length === 0) {
|
|
1504
|
+
return false;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
return true;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1482
1510
|
// Parse a Codex session JSONL file to extract metadata
|
|
1483
1511
|
async function parseCodexSessionFile(filePath) {
|
|
1484
1512
|
try {
|
|
@@ -1514,8 +1542,8 @@ async function parseCodexSessionFile(filePath) {
|
|
|
1514
1542
|
};
|
|
1515
1543
|
}
|
|
1516
1544
|
|
|
1517
|
-
// Count messages and extract
|
|
1518
|
-
if (entry.type === 'event_msg' && entry.payload
|
|
1545
|
+
// Count visible user messages and extract summary from the latest plain user input.
|
|
1546
|
+
if (entry.type === 'event_msg' && isVisibleCodexUserMessage(entry.payload)) {
|
|
1519
1547
|
messageCount++;
|
|
1520
1548
|
if (entry.payload.message) {
|
|
1521
1549
|
lastUserMessage = entry.payload.message;
|
|
@@ -1622,25 +1650,36 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
1622
1650
|
};
|
|
1623
1651
|
}
|
|
1624
1652
|
}
|
|
1653
|
+
|
|
1654
|
+
// Use event_msg.user_message for user-visible inputs.
|
|
1655
|
+
if (entry.type === 'event_msg' && isVisibleCodexUserMessage(entry.payload)) {
|
|
1656
|
+
messages.push({
|
|
1657
|
+
type: 'user',
|
|
1658
|
+
timestamp: entry.timestamp,
|
|
1659
|
+
message: {
|
|
1660
|
+
role: 'user',
|
|
1661
|
+
content: entry.payload.message
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1625
1665
|
|
|
1626
|
-
//
|
|
1627
|
-
|
|
1666
|
+
// response_item.message may include internal prompts for non-assistant roles.
|
|
1667
|
+
// Keep only assistant output from response_item.
|
|
1668
|
+
if (
|
|
1669
|
+
entry.type === 'response_item' &&
|
|
1670
|
+
entry.payload?.type === 'message' &&
|
|
1671
|
+
entry.payload.role === 'assistant'
|
|
1672
|
+
) {
|
|
1628
1673
|
const content = entry.payload.content;
|
|
1629
|
-
const role = entry.payload.role || 'assistant';
|
|
1630
1674
|
const textContent = extractText(content);
|
|
1631
1675
|
|
|
1632
|
-
// Skip system context messages (environment_context)
|
|
1633
|
-
if (textContent?.includes('<environment_context>')) {
|
|
1634
|
-
continue;
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
1676
|
// Only add if there's actual content
|
|
1638
1677
|
if (textContent?.trim()) {
|
|
1639
1678
|
messages.push({
|
|
1640
|
-
type:
|
|
1679
|
+
type: 'assistant',
|
|
1641
1680
|
timestamp: entry.timestamp,
|
|
1642
1681
|
message: {
|
|
1643
|
-
role:
|
|
1682
|
+
role: 'assistant',
|
|
1644
1683
|
content: textContent
|
|
1645
1684
|
}
|
|
1646
1685
|
});
|
|
@@ -14,13 +14,14 @@ router.get('/claude/status', async (req, res) => {
|
|
|
14
14
|
return res.json({
|
|
15
15
|
authenticated: true,
|
|
16
16
|
email: credentialsResult.email || 'Authenticated',
|
|
17
|
-
method: 'credentials_file'
|
|
17
|
+
method: credentialsResult.method // 'api_key' or 'credentials_file'
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
return res.json({
|
|
22
22
|
authenticated: false,
|
|
23
23
|
email: null,
|
|
24
|
+
method: null,
|
|
24
25
|
error: credentialsResult.error || 'Not authenticated'
|
|
25
26
|
});
|
|
26
27
|
|
|
@@ -29,6 +30,7 @@ router.get('/claude/status', async (req, res) => {
|
|
|
29
30
|
res.status(500).json({
|
|
30
31
|
authenticated: false,
|
|
31
32
|
email: null,
|
|
33
|
+
method: null,
|
|
32
34
|
error: error.message
|
|
33
35
|
});
|
|
34
36
|
}
|
|
@@ -115,6 +117,20 @@ router.get('/gemini/status', async (req, res) => {
|
|
|
115
117
|
* - method: 'api_key' for env var, 'credentials_file' for OAuth tokens
|
|
116
118
|
*/
|
|
117
119
|
async function checkClaudeCredentials() {
|
|
120
|
+
// Priority 1: Check for ANTHROPIC_API_KEY environment variable
|
|
121
|
+
// The SDK checks this first and uses it if present, even if OAuth tokens exist.
|
|
122
|
+
// When set, API calls are charged via pay-as-you-go rates instead of subscription.
|
|
123
|
+
if (process.env.ANTHROPIC_API_KEY && process.env.ANTHROPIC_API_KEY.trim()) {
|
|
124
|
+
return {
|
|
125
|
+
authenticated: true,
|
|
126
|
+
email: 'API Key Auth',
|
|
127
|
+
method: 'api_key'
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Priority 2: Check ~/.claude/.credentials.json for OAuth tokens
|
|
132
|
+
// This is the standard authentication method used by Claude CLI after running
|
|
133
|
+
// 'claude /login' or 'claude setup-token' commands.
|
|
118
134
|
try {
|
|
119
135
|
const credPath = path.join(os.homedir(), '.claude', '.credentials.json');
|
|
120
136
|
const content = await fs.readFile(credPath, 'utf8');
|
|
@@ -127,19 +143,22 @@ async function checkClaudeCredentials() {
|
|
|
127
143
|
if (!isExpired) {
|
|
128
144
|
return {
|
|
129
145
|
authenticated: true,
|
|
130
|
-
email: creds.email || creds.user || null
|
|
146
|
+
email: creds.email || creds.user || null,
|
|
147
|
+
method: 'credentials_file'
|
|
131
148
|
};
|
|
132
149
|
}
|
|
133
150
|
}
|
|
134
151
|
|
|
135
152
|
return {
|
|
136
153
|
authenticated: false,
|
|
137
|
-
email: null
|
|
154
|
+
email: null,
|
|
155
|
+
method: null
|
|
138
156
|
};
|
|
139
157
|
} catch (error) {
|
|
140
158
|
return {
|
|
141
159
|
authenticated: false,
|
|
142
|
-
email: null
|
|
160
|
+
email: null,
|
|
161
|
+
method: null
|
|
143
162
|
};
|
|
144
163
|
}
|
|
145
164
|
}
|
package/server/routes/codex.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from 'path';
|
|
|
5
5
|
import os from 'os';
|
|
6
6
|
import TOML from '@iarna/toml';
|
|
7
7
|
import { getCodexSessions, getCodexSessionMessages, deleteCodexSession } from '../projects.js';
|
|
8
|
+
import { applyCustomSessionNames, sessionNamesDb } from '../database/db.js';
|
|
8
9
|
|
|
9
10
|
const router = express.Router();
|
|
10
11
|
|
|
@@ -59,6 +60,7 @@ router.get('/sessions', async (req, res) => {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
const sessions = await getCodexSessions(projectPath);
|
|
63
|
+
applyCustomSessionNames(sessions, 'codex');
|
|
62
64
|
res.json({ success: true, sessions });
|
|
63
65
|
} catch (error) {
|
|
64
66
|
console.error('Error fetching Codex sessions:', error);
|
|
@@ -88,6 +90,7 @@ router.delete('/sessions/:sessionId', async (req, res) => {
|
|
|
88
90
|
try {
|
|
89
91
|
const { sessionId } = req.params;
|
|
90
92
|
await deleteCodexSession(sessionId);
|
|
93
|
+
sessionNamesDb.deleteName(sessionId, 'codex');
|
|
91
94
|
res.json({ success: true });
|
|
92
95
|
} catch (error) {
|
|
93
96
|
console.error(`Error deleting Codex session ${req.params.sessionId}:`, error);
|
package/server/routes/cursor.js
CHANGED
|
@@ -7,6 +7,7 @@ import sqlite3 from 'sqlite3';
|
|
|
7
7
|
import { open } from 'sqlite';
|
|
8
8
|
import crypto from 'crypto';
|
|
9
9
|
import { CURSOR_MODELS } from '../../shared/modelConstants.js';
|
|
10
|
+
import { applyCustomSessionNames } from '../database/db.js';
|
|
10
11
|
|
|
11
12
|
const router = express.Router();
|
|
12
13
|
|
|
@@ -560,8 +561,10 @@ router.get('/sessions', async (req, res) => {
|
|
|
560
561
|
return new Date(b.createdAt) - new Date(a.createdAt);
|
|
561
562
|
});
|
|
562
563
|
|
|
563
|
-
|
|
564
|
-
|
|
564
|
+
applyCustomSessionNames(sessions, 'cursor');
|
|
565
|
+
|
|
566
|
+
res.json({
|
|
567
|
+
success: true,
|
|
565
568
|
sessions: sessions,
|
|
566
569
|
cwdId: cwdId,
|
|
567
570
|
path: cursorChatsPath
|
package/server/routes/gemini.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import sessionManager from '../sessionManager.js';
|
|
3
|
+
import { sessionNamesDb } from '../database/db.js';
|
|
3
4
|
|
|
4
5
|
const router = express.Router();
|
|
5
6
|
|
|
@@ -36,6 +37,7 @@ router.delete('/sessions/:sessionId', async (req, res) => {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
await sessionManager.deleteSession(sessionId);
|
|
40
|
+
sessionNamesDb.deleteName(sessionId, 'gemini');
|
|
39
41
|
res.json({ success: true });
|
|
40
42
|
} catch (error) {
|
|
41
43
|
console.error(`Error deleting Gemini session ${req.params.sessionId}:`, error);
|
package/shared/modelConstants.js
CHANGED
|
@@ -28,6 +28,8 @@ export const CLAUDE_MODELS = {
|
|
|
28
28
|
*/
|
|
29
29
|
export const CURSOR_MODELS = {
|
|
30
30
|
OPTIONS: [
|
|
31
|
+
{ value: 'opus-4.6-thinking', label: 'Claude 4.6 Opus (Thinking)' },
|
|
32
|
+
{ value: 'gpt-5.3-codex', label: 'GPT-5.3' },
|
|
31
33
|
{ value: 'gpt-5.2-high', label: 'GPT-5.2 High' },
|
|
32
34
|
{ value: 'gemini-3-pro', label: 'Gemini 3 Pro' },
|
|
33
35
|
{ value: 'opus-4.5-thinking', label: 'Claude 4.5 Opus (Thinking)' },
|
|
@@ -47,7 +49,7 @@ export const CURSOR_MODELS = {
|
|
|
47
49
|
{ value: 'grok', label: 'Grok' }
|
|
48
50
|
],
|
|
49
51
|
|
|
50
|
-
DEFAULT: 'gpt-5'
|
|
52
|
+
DEFAULT: 'gpt-5-3-codex'
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
/**
|
|
@@ -55,6 +57,8 @@ export const CURSOR_MODELS = {
|
|
|
55
57
|
*/
|
|
56
58
|
export const CODEX_MODELS = {
|
|
57
59
|
OPTIONS: [
|
|
60
|
+
{ value: 'gpt-5.4', label: 'GPT-5.4' },
|
|
61
|
+
{ value: 'gpt-5.3-codex', label: 'GPT-5.3 Codex' },
|
|
58
62
|
{ value: 'gpt-5.3-codex', label: 'GPT-5.3 Codex' },
|
|
59
63
|
{ value: 'gpt-5.2-codex', label: 'GPT-5.2 Codex' },
|
|
60
64
|
{ value: 'gpt-5.2', label: 'GPT-5.2' },
|
|
@@ -63,7 +67,7 @@ export const CODEX_MODELS = {
|
|
|
63
67
|
{ value: 'o4-mini', label: 'O4-mini' }
|
|
64
68
|
],
|
|
65
69
|
|
|
66
|
-
DEFAULT: 'gpt-5.
|
|
70
|
+
DEFAULT: 'gpt-5.4'
|
|
67
71
|
};
|
|
68
72
|
|
|
69
73
|
/**
|