@epiphytic/claudecodeui 1.2.1 → 1.2.3

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/dist/index.html CHANGED
@@ -25,11 +25,11 @@
25
25
 
26
26
  <!-- Prevent zoom on iOS -->
27
27
  <meta name="format-detection" content="telephone=no" />
28
- <script type="module" crossorigin src="/assets/index-sqmQ9jF8.js"></script>
28
+ <script type="module" crossorigin src="/assets/index-CWwPqmRx.js"></script>
29
29
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DcyRfQm3.js">
30
30
  <link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-CJLzwpLB.js">
31
31
  <link rel="modulepreload" crossorigin href="/assets/vendor-xterm-DfaPXD3y.js">
32
- <link rel="stylesheet" crossorigin href="/assets/index-BGneYLVE.css">
32
+ <link rel="stylesheet" crossorigin href="/assets/index-BV_fwoPa.css">
33
33
  </head>
34
34
  <body>
35
35
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epiphytic/claudecodeui",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "A web-based UI for Claude Code CLI",
5
5
  "type": "module",
6
6
  "main": "server/index.js",
@@ -14,6 +14,10 @@ let cachedProjects = [];
14
14
  let cacheVersion = 0;
15
15
  let cacheTimestamp = null;
16
16
 
17
+ // Promise-based initialization waiting
18
+ let initResolvers = [];
19
+ const MAX_WAIT_MS = 30000; // 30 second timeout
20
+
17
21
  /**
18
22
  * Timeframe definitions in milliseconds
19
23
  * (Same as sessions-cache.js for consistency)
@@ -109,6 +113,14 @@ function updateProjectsCache(projects) {
109
113
 
110
114
  cacheVersion++;
111
115
  cacheTimestamp = new Date().toISOString();
116
+
117
+ // Resolve any waiting promises
118
+ if (initResolvers.length > 0) {
119
+ for (const resolve of initResolvers) {
120
+ resolve();
121
+ }
122
+ initResolvers = [];
123
+ }
112
124
  }
113
125
 
114
126
  /**
@@ -170,6 +182,35 @@ function isCacheInitialized() {
170
182
  return cacheTimestamp !== null;
171
183
  }
172
184
 
185
+ /**
186
+ * Wait for cache to be initialized
187
+ * Returns immediately if already initialized, otherwise waits up to MAX_WAIT_MS
188
+ */
189
+ function waitForInitialization() {
190
+ if (cacheTimestamp !== null) {
191
+ return Promise.resolve();
192
+ }
193
+
194
+ return new Promise((resolve, reject) => {
195
+ const timeout = setTimeout(() => {
196
+ // Remove this resolver from the list
197
+ const idx = initResolvers.indexOf(resolve);
198
+ if (idx !== -1) {
199
+ initResolvers.splice(idx, 1);
200
+ }
201
+ reject(new Error("Cache initialization timeout"));
202
+ }, MAX_WAIT_MS);
203
+
204
+ // Wrap resolver to clear timeout
205
+ const wrappedResolve = () => {
206
+ clearTimeout(timeout);
207
+ resolve();
208
+ };
209
+
210
+ initResolvers.push(wrappedResolve);
211
+ });
212
+ }
213
+
173
214
  /**
174
215
  * Get the raw cached projects (for initial load)
175
216
  */
@@ -190,6 +231,7 @@ export {
190
231
  generateETag,
191
232
  getCacheMeta,
192
233
  isCacheInitialized,
234
+ waitForInitialization,
193
235
  getCachedProjects,
194
236
  getProjectFromCache,
195
237
  TIMEFRAME_MS,
@@ -9,6 +9,7 @@ import {
9
9
  generateETag,
10
10
  getCacheMeta,
11
11
  isCacheInitialized,
12
+ waitForInitialization,
12
13
  TIMEFRAME_MS,
13
14
  } from "../projects-cache.js";
14
15
 
@@ -26,14 +27,18 @@ const router = express.Router();
26
27
  * Headers:
27
28
  * - If-None-Match: ETag from previous response (for 304 support)
28
29
  */
29
- router.get("/list", (req, res) => {
30
+ router.get("/list", async (req, res) => {
30
31
  try {
31
- // Check if cache is initialized
32
+ // Wait for cache to be initialized (blocks until ready or timeout)
32
33
  if (!isCacheInitialized()) {
33
- return res.status(503).json({
34
- error: "Projects cache not yet initialized",
35
- message: "Please wait for initial project scan to complete",
36
- });
34
+ try {
35
+ await waitForInitialization();
36
+ } catch (err) {
37
+ return res.status(503).json({
38
+ error: "Projects cache initialization timeout",
39
+ message: err.message,
40
+ });
41
+ }
37
42
  }
38
43
 
39
44
  // Get timeframe from query (validate against known values)
@@ -13,6 +13,7 @@ import {
13
13
  generateETag,
14
14
  getCacheMeta,
15
15
  isCacheInitialized,
16
+ waitForInitialization,
16
17
  TIMEFRAME_MS,
17
18
  } from "../sessions-cache.js";
18
19
 
@@ -31,14 +32,18 @@ const router = express.Router();
31
32
  * - 304 Not Modified (if ETag matches)
32
33
  * - 200 OK with sessions data
33
34
  */
34
- router.get("/list", (req, res) => {
35
+ router.get("/list", async (req, res) => {
35
36
  try {
36
- // Check if cache is initialized
37
+ // Wait for cache to be initialized (blocks until ready or timeout)
37
38
  if (!isCacheInitialized()) {
38
- return res.status(503).json({
39
- error: "Sessions cache not yet initialized",
40
- message: "Please wait for initial project scan to complete",
41
- });
39
+ try {
40
+ await waitForInitialization();
41
+ } catch (err) {
42
+ return res.status(503).json({
43
+ error: "Sessions cache initialization timeout",
44
+ message: err.message,
45
+ });
46
+ }
42
47
  }
43
48
 
44
49
  // Get timeframe from query (validate against known values)
@@ -14,6 +14,10 @@ let cacheVersion = 0;
14
14
  let cacheTimestamp = null;
15
15
  let lastProjectsData = null;
16
16
 
17
+ // Promise-based initialization waiting
18
+ let initResolvers = [];
19
+ const MAX_WAIT_MS = 30000; // 30 second timeout
20
+
17
21
  /**
18
22
  * Timeframe definitions in milliseconds
19
23
  */
@@ -100,6 +104,14 @@ function updateSessionsCache(projects) {
100
104
  cacheVersion++;
101
105
  cacheTimestamp = new Date().toISOString();
102
106
  lastProjectsData = projects;
107
+
108
+ // Resolve any waiting promises
109
+ if (initResolvers.length > 0) {
110
+ for (const resolve of initResolvers) {
111
+ resolve();
112
+ }
113
+ initResolvers = [];
114
+ }
103
115
  }
104
116
 
105
117
  /**
@@ -157,6 +169,35 @@ function isCacheInitialized() {
157
169
  return cacheTimestamp !== null;
158
170
  }
159
171
 
172
+ /**
173
+ * Wait for cache to be initialized
174
+ * Returns immediately if already initialized, otherwise waits up to MAX_WAIT_MS
175
+ */
176
+ function waitForInitialization() {
177
+ if (cacheTimestamp !== null) {
178
+ return Promise.resolve();
179
+ }
180
+
181
+ return new Promise((resolve, reject) => {
182
+ const timeout = setTimeout(() => {
183
+ // Remove this resolver from the list
184
+ const idx = initResolvers.indexOf(resolve);
185
+ if (idx !== -1) {
186
+ initResolvers.splice(idx, 1);
187
+ }
188
+ reject(new Error("Cache initialization timeout"));
189
+ }, MAX_WAIT_MS);
190
+
191
+ // Wrap resolver to clear timeout
192
+ const wrappedResolve = () => {
193
+ clearTimeout(timeout);
194
+ resolve();
195
+ };
196
+
197
+ initResolvers.push(wrappedResolve);
198
+ });
199
+ }
200
+
160
201
  /**
161
202
  * Get the raw cached sessions (for initial load)
162
203
  */
@@ -177,6 +218,7 @@ export {
177
218
  generateETag,
178
219
  getCacheMeta,
179
220
  isCacheInitialized,
221
+ waitForInitialization,
180
222
  getCachedSessions,
181
223
  getLastProjectsData,
182
224
  TIMEFRAME_MS,