Skip to content

Commit 069d627

Browse files
authored
chore(release): v3.3.0 (#53)
- Fix critical syntax error in server.js close() method - Fix memory leaks in all bridge files (claude, codex, agent) - Fix race condition in session-store.js - Fix duplicate signal handlers in server.js - Remove undefined method call in usage-reader.js - Clean up unused variables - Add missing test coverage for agent alias - Remove token usage top bar from UI All bug fixes are backward-compatible. Test suite: 12/12 passing.
1 parent d73d607 commit 069d627

13 files changed

Lines changed: 98 additions & 307 deletions

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Changelog
22

3+
## [3.3.0] - 2025-10-23
4+
5+
### Fixed
6+
- **Critical**: Fixed syntax error in `server.js` close() method causing improper indentation in agent session cleanup
7+
- **Critical**: Fixed memory leaks in all three bridge files (claude-bridge.js, codex-bridge.js, agent-bridge.js) by properly tracking and clearing kill timeouts
8+
- Fixed race condition in `session-store.js` where atomic rename could fail if directory was deleted between write and rename operations
9+
- Fixed duplicate signal handlers in `server.js` that could cause double-shutdown attempts
10+
- Removed call to undefined method `clearProcessedEntriesCache()` in `usage-reader.js`
11+
- Removed unused `sessionCache` Map variable from `usage-reader.js`
12+
- Added missing test coverage for agent alias in server alias tests
13+
- Fixed test cleanup warnings by ensuring storage directory exists before save operations
14+
15+
### Changed
16+
- Removed token usage top bar from UI - no longer displays real-time token statistics in the header
17+
- Updated `applySettings()` to reflect removal of token stats visibility toggle
18+
- Disabled `updateUsageDisplay()` and `startSessionTimerUpdate()` functions as UI elements no longer exist
19+
20+
### Notes
21+
- All bug fixes are backward-compatible
22+
- Usage statistics backend code still runs but is no longer displayed in the UI
23+
- Test suite passing: 12/12 tests
24+
325
## [3.2.2] - 2025-10-23
426

527
### Fixed

bin/cc-web.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const program = new Command();
1111
program
1212
.name('cc-web')
1313
.description('Web-based interface for Claude Code CLI')
14-
.version('3.2.2')
14+
.version('3.3.0')
1515
.option('-p, --port <number>', 'port to run the server on', '32352')
1616
.option('--no-open', 'do not automatically open browser')
1717
.option('--auth <token>', 'authentication token for secure access')

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "claude-code-web",
3-
"version": "3.2.2",
3+
"version": "3.3.0",
44
"description": "Web-based interface for Claude Code CLI accessible via browser",
55
"main": "src/server.js",
66
"bin": {

src/agent-bridge.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ class AgentBridge {
7878
process: agentProcess,
7979
workingDir,
8080
created: new Date(),
81-
active: true
81+
active: true,
82+
killTimeout: null
8283
};
8384

8485
this.sessions.set(sessionId, session);
@@ -99,13 +100,23 @@ class AgentBridge {
99100

100101
agentProcess.onExit((exitCode, signal) => {
101102
console.log(`Agent session ${sessionId} exited with code ${exitCode}, signal ${signal}`);
103+
// Clear kill timeout if process exited naturally
104+
if (session.killTimeout) {
105+
clearTimeout(session.killTimeout);
106+
session.killTimeout = null;
107+
}
102108
session.active = false;
103109
this.sessions.delete(sessionId);
104110
onExit(exitCode, signal);
105111
});
106112

107113
agentProcess.on('error', (error) => {
108114
console.error(`Agent session ${sessionId} error:`, error);
115+
// Clear kill timeout if process errored
116+
if (session.killTimeout) {
117+
clearTimeout(session.killTimeout);
118+
session.killTimeout = null;
119+
}
109120
session.active = false;
110121
this.sessions.delete(sessionId);
111122
onError(error);
@@ -153,9 +164,15 @@ class AgentBridge {
153164
}
154165

155166
try {
167+
// Clear any existing kill timeout
168+
if (session.killTimeout) {
169+
clearTimeout(session.killTimeout);
170+
session.killTimeout = null;
171+
}
172+
156173
if (session.active && session.process) {
157174
session.process.kill('SIGTERM');
158-
setTimeout(() => {
175+
session.killTimeout = setTimeout(() => {
159176
if (session.active && session.process) {
160177
session.process.kill('SIGKILL');
161178
}

src/claude-bridge.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ class ClaudeBridge {
8585
process: claudeProcess,
8686
workingDir,
8787
created: new Date(),
88-
active: true
88+
active: true,
89+
killTimeout: null
8990
};
9091

9192
this.sessions.set(sessionId, session);
@@ -124,13 +125,23 @@ class ClaudeBridge {
124125

125126
claudeProcess.onExit((exitCode, signal) => {
126127
console.log(`Claude session ${sessionId} exited with code ${exitCode}, signal ${signal}`);
128+
// Clear kill timeout if process exited naturally
129+
if (session.killTimeout) {
130+
clearTimeout(session.killTimeout);
131+
session.killTimeout = null;
132+
}
127133
session.active = false;
128134
this.sessions.delete(sessionId);
129135
onExit(exitCode, signal);
130136
});
131137

132138
claudeProcess.on('error', (error) => {
133139
console.error(`Claude session ${sessionId} error:`, error);
140+
// Clear kill timeout if process errored
141+
if (session.killTimeout) {
142+
clearTimeout(session.killTimeout);
143+
session.killTimeout = null;
144+
}
134145
session.active = false;
135146
this.sessions.delete(sessionId);
136147
onError(error);
@@ -178,10 +189,16 @@ class ClaudeBridge {
178189
}
179190

180191
try {
192+
// Clear any existing kill timeout
193+
if (session.killTimeout) {
194+
clearTimeout(session.killTimeout);
195+
session.killTimeout = null;
196+
}
197+
181198
if (session.active && session.process) {
182199
session.process.kill('SIGTERM');
183200

184-
setTimeout(() => {
201+
session.killTimeout = setTimeout(() => {
185202
if (session.active && session.process) {
186203
session.process.kill('SIGKILL');
187204
}

src/codex-bridge.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ class CodexBridge {
8484
process: codexProcess,
8585
workingDir,
8686
created: new Date(),
87-
active: true
87+
active: true,
88+
killTimeout: null
8889
};
8990

9091
this.sessions.set(sessionId, session);
@@ -105,13 +106,23 @@ class CodexBridge {
105106

106107
codexProcess.onExit((exitCode, signal) => {
107108
console.log(`Codex session ${sessionId} exited with code ${exitCode}, signal ${signal}`);
109+
// Clear kill timeout if process exited naturally
110+
if (session.killTimeout) {
111+
clearTimeout(session.killTimeout);
112+
session.killTimeout = null;
113+
}
108114
session.active = false;
109115
this.sessions.delete(sessionId);
110116
onExit(exitCode, signal);
111117
});
112118

113119
codexProcess.on('error', (error) => {
114120
console.error(`Codex session ${sessionId} error:`, error);
121+
// Clear kill timeout if process errored
122+
if (session.killTimeout) {
123+
clearTimeout(session.killTimeout);
124+
session.killTimeout = null;
125+
}
115126
session.active = false;
116127
this.sessions.delete(sessionId);
117128
onError(error);
@@ -159,9 +170,15 @@ class CodexBridge {
159170
}
160171

161172
try {
173+
// Clear any existing kill timeout
174+
if (session.killTimeout) {
175+
clearTimeout(session.killTimeout);
176+
session.killTimeout = null;
177+
}
178+
162179
if (session.active && session.process) {
163180
session.process.kill('SIGTERM');
164-
setTimeout(() => {
181+
session.killTimeout = setTimeout(() => {
165182
if (session.active && session.process) {
166183
session.process.kill('SIGKILL');
167184
}

src/public/app.js

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,11 +1062,7 @@ class ClaudeCodeWebInterface {
10621062
}
10631063

10641064
applySettings(settings) {
1065-
// Apply token stats visibility
1066-
const usageStatsContainer = document.getElementById('usageStatsContainer');
1067-
if (usageStatsContainer) {
1068-
usageStatsContainer.style.display = settings.showTokenStats ? 'flex' : 'none';
1069-
}
1065+
// Token stats bar removed - no longer needed
10701066
// Apply theme (dark is default; light sets attribute)
10711067
if (settings.theme === 'light') {
10721068
document.documentElement.setAttribute('data-theme', 'light');
@@ -1867,52 +1863,13 @@ class ClaudeCodeWebInterface {
18671863
}
18681864

18691865
startSessionTimerUpdate() {
1870-
// Clear existing timer if any
1871-
if (this.sessionTimerInterval) {
1872-
clearInterval(this.sessionTimerInterval);
1873-
}
1874-
1875-
// Update timer every second - just show remaining time
1876-
this.sessionTimerInterval = setInterval(() => {
1877-
if (this.sessionTimer && this.sessionTimer.startTime) {
1878-
const startTime = new Date(this.sessionTimer.startTime);
1879-
const now = new Date();
1880-
const elapsedMs = now - startTime;
1881-
1882-
// Use session duration from server if available, default to 5 hours
1883-
const sessionHours = (this.sessionTimer && this.sessionTimer.sessionDurationHours) || 5;
1884-
const sessionDurationMs = sessionHours * 60 * 60 * 1000;
1885-
const remainingMs = Math.max(0, sessionDurationMs - elapsedMs);
1886-
1887-
// Format remaining time only
1888-
let displayText;
1889-
if (remainingMs > 0) {
1890-
const remainingHours = Math.floor(remainingMs / (1000 * 60 * 60));
1891-
const remainingMinutes = Math.floor((remainingMs % (1000 * 60 * 60)) / (1000 * 60));
1892-
displayText = `${remainingHours}h ${remainingMinutes}m`;
1893-
} else {
1894-
displayText = '0h 0m';
1895-
}
1896-
1897-
// Update the title element with remaining time
1898-
const titleElement = document.getElementById('usageTitle');
1899-
if (titleElement) {
1900-
titleElement.textContent = displayText;
1901-
}
1902-
}
1903-
}, 1000);
1866+
// Token usage timer removed - no UI elements to update
1867+
return;
19041868
}
19051869

19061870
updateUsageDisplay(sessionStats, dailyStats, sessionTimer, analytics, burnRate, plan, limits) {
1907-
if (!sessionStats && !dailyStats) return;
1908-
1909-
this.sessionStats = sessionStats;
1910-
this.dailyStats = dailyStats;
1911-
this.sessionTimer = sessionTimer;
1912-
this.analytics = analytics;
1913-
this.burnRate = burnRate;
1914-
this.currentPlan = plan;
1915-
this.planLimits = limits;
1871+
// Token usage display removed - no UI elements to update
1872+
return;
19161873

19171874
// Container is already visible by default
19181875

src/public/index.html

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,6 @@
5050
</head>
5151
<body>
5252
<div id="app">
53-
<!-- Usage Stats Bar -->
54-
<div class="usage-stats-bar" id="usageStatsContainer">
55-
<div class="usage-stat" data-label="Tokens:">
56-
<span class="stat-label">Tokens:</span>
57-
<span class="stat-value" id="usageTokens">0</span>
58-
</div>
59-
<div class="usage-stat" data-label="Cost:">
60-
<span class="stat-label">Cost:</span>
61-
<span class="stat-value" id="usageCost">$0.00</span>
62-
</div>
63-
<div class="usage-stat" data-label="Rate:">
64-
<span class="stat-label">Rate:</span>
65-
<span class="stat-value" id="usageRate">0 tok/min</span>
66-
</div>
67-
<div class="usage-stat" data-label="Reset:">
68-
<span class="stat-label">Reset:</span>
69-
<span class="stat-value" id="usageTitle">Loading...</span>
70-
</div>
71-
<div class="usage-stat" data-label="Model:">
72-
<span class="stat-label">Model:</span>
73-
<span class="stat-value" id="usageModel">-</span>
74-
</div>
75-
<!-- Token usage progress bar -->
76-
<div class="usage-progress" id="usageProgress" style="display: none;">
77-
<div class="usage-progress-bar" id="usageProgressBar"></div>
78-
<div class="usage-progress-text" id="usageProgressText">0%</div>
79-
</div>
80-
</div>
8153

8254
<!-- Session Tabs Bar -->
8355
<div class="session-tabs-bar" id="sessionTabsBar">

0 commit comments

Comments
 (0)