@qiaolei81/copilot-session-viewer 0.2.5 → 0.2.6

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.
@@ -2,7 +2,7 @@ const BaseSessionParser = require('./base-parser');
2
2
  const CopilotSessionParser = require('./copilot-parser');
3
3
  const ClaudeSessionParser = require('./claude-parser');
4
4
  const PiMonoParser = require('./pi-mono-parser');
5
- const VsCodeParser = require('./vscode-parser');
5
+ // const VsCodeParser = require('./vscode-parser'); // TODO: VSCode parser disabled
6
6
  const ParserFactory = require('./parser-factory');
7
7
 
8
8
  module.exports = {
@@ -10,6 +10,6 @@ module.exports = {
10
10
  CopilotSessionParser,
11
11
  ClaudeSessionParser,
12
12
  PiMonoParser,
13
- VsCodeParser,
13
+ // VsCodeParser, // TODO: VSCode parser disabled
14
14
  ParserFactory
15
15
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qiaolei81/copilot-session-viewer",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Web UI for viewing GitHub Copilot CLI session logs",
5
5
  "author": "Lei Qiao <qiaolei81@gmail.com>",
6
6
  "license": "MIT",
package/src/app.js CHANGED
@@ -19,6 +19,9 @@ const UploadController = require('./controllers/uploadController');
19
19
  function createApp(options = {}) {
20
20
  const app = express();
21
21
 
22
+ // Disable Express's automatic ETag generation (prevents 304 on live/active session files)
23
+ app.set('etag', false);
24
+
22
25
  // Create controller instances (with optional dependency injection)
23
26
  const sessionController = new SessionController(options.sessionService);
24
27
  const insightController = new InsightController(options.insightService, options.sessionService);
@@ -162,25 +162,12 @@ class SessionController {
162
162
  }
163
163
  }
164
164
 
165
- // Get session metadata for ETag generation
165
+ // Get session (needed for findById, no caching)
166
166
  const session = await this.sessionService.sessionRepository.findById(sessionId);
167
167
  if (!session) {
168
168
  return res.status(404).json({ error: 'Session not found' });
169
169
  }
170
170
 
171
- // Generate ETag from session ID + timestamp + pagination params (if used)
172
- const crypto = require('crypto');
173
- const etagBase = isPaginationRequested
174
- ? `${sessionId}-${session.updatedAt || session.createdAt}-${limit}-${offset}`
175
- : `${sessionId}-${session.updatedAt || session.createdAt}`;
176
- const etag = crypto.createHash('md5').update(etagBase).digest('hex');
177
-
178
- // Check If-None-Match header (client cache)
179
- const clientEtag = req.headers['if-none-match'];
180
- if (clientEtag === etag) {
181
- return res.status(304).end(); // Not Modified - use cached version
182
- }
183
-
184
171
  // Load events (with or without pagination)
185
172
  if (isPaginationRequested) {
186
173
  result = await this.sessionService.getSessionEvents(sessionId, { limit, offset });
@@ -190,10 +177,9 @@ class SessionController {
190
177
  result = events; // Direct array
191
178
  }
192
179
 
193
- // Set caching headers
180
+ // No caching for events - session files are live/active
194
181
  res.set({
195
- 'ETag': etag,
196
- 'Cache-Control': 'private, max-age=0, no-cache', // Disable cache during development
182
+ 'Cache-Control': 'no-store',
197
183
  'Vary': 'Accept-Encoding'
198
184
  });
199
185
 
@@ -39,11 +39,12 @@ class SessionRepository {
39
39
  dir: process.env.PI_MONO_SESSION_DIR ||
40
40
  path.join(os.homedir(), '.pi', 'agent', 'sessions')
41
41
  },
42
- {
43
- type: 'vscode',
44
- dir: process.env.VSCODE_WORKSPACE_STORAGE_DIR ||
45
- path.join(os.homedir(), 'Library', 'Application Support', 'Code', 'User', 'workspaceStorage')
46
- }
42
+ // TODO: VSCode parser disabled
43
+ // {
44
+ // type: 'vscode',
45
+ // dir: process.env.VSCODE_WORKSPACE_STORAGE_DIR ||
46
+ // path.join(os.homedir(), 'Library', 'Application Support', 'Code', 'User', 'workspaceStorage')
47
+ // }
47
48
  ];
48
49
  }
49
50
 
@@ -110,12 +111,10 @@ class SessionRepository {
110
111
  if (stats.isDirectory()) {
111
112
  return this._scanPiMonoDir(fullPath, entry);
112
113
  }
113
- } else if (source.type === 'vscode') {
114
- // VSCode: workspaceStorage/<hash>/chatSessions/<uuid>.json
115
- // Each top-level entry is a workspace hash directory
116
- if (stats.isDirectory()) {
117
- return this._scanVsCodeWorkspaceDir(fullPath);
118
- }
114
+ // } else if (source.type === 'vscode') { // TODO: VSCode disabled
115
+ // if (stats.isDirectory()) {
116
+ // return this._scanVsCodeWorkspaceDir(fullPath);
117
+ // }
119
118
  }
120
119
  return null;
121
120
  });
@@ -275,8 +274,8 @@ class SessionRepository {
275
274
  session = await this._findClaudeSession(sessionId, source.dir);
276
275
  } else if (source.type === 'pi-mono') {
277
276
  session = await this._findPiMonoSession(sessionId, source.dir);
278
- } else if (source.type === 'vscode') {
279
- session = await this._findVsCodeSession(sessionId, source.dir);
277
+ // } else if (source.type === 'vscode') { // TODO: VSCode disabled
278
+ // session = await this._findVsCodeSession(sessionId, source.dir);
280
279
  }
281
280
 
282
281
  if (session) return session;
@@ -151,27 +151,24 @@ class SessionService {
151
151
  console.error('Error searching Pi-Mono sessions:', err);
152
152
  return [];
153
153
  }
154
- } else if (session.source === 'vscode') {
155
- // VSCode format: read JSON file directly, convert to event array via VsCodeParser
156
- const { VsCodeParser } = require('../../lib/parsers');
157
- const vscodeParser = new VsCodeParser();
158
- try {
159
- const raw = await fs.promises.readFile(session.filePath, 'utf-8');
160
- let sessionJson;
161
- if (session.filePath.endsWith('.jsonl')) {
162
- sessionJson = this.sessionRepository._parseVsCodeJsonl(raw);
163
- } else {
164
- sessionJson = JSON.parse(raw);
165
- }
166
- if (!sessionJson) return [];
167
- const parsed = vscodeParser.parseVsCode(sessionJson);
168
- // Convert tool.invocation events → assistant.message with data.tools
169
- // so frontend can render them using the same tool-list component
170
- return this._expandVsCodeEvents(parsed.allEvents);
171
- } catch (err) {
172
- console.error('Error reading VSCode session:', err);
173
- return [];
174
- }
154
+ // } else if (session.source === 'vscode') { // TODO: VSCode disabled
155
+ // const { VsCodeParser } = require('../../lib/parsers');
156
+ // const vscodeParser = new VsCodeParser();
157
+ // try {
158
+ // const raw = await fs.promises.readFile(session.filePath, 'utf-8');
159
+ // let sessionJson;
160
+ // if (session.filePath.endsWith('.jsonl')) {
161
+ // sessionJson = this.sessionRepository._parseVsCodeJsonl(raw);
162
+ // } else {
163
+ // sessionJson = JSON.parse(raw);
164
+ // }
165
+ // if (!sessionJson) return [];
166
+ // const parsed = vscodeParser.parseVsCode(sessionJson);
167
+ // return this._expandVsCodeEvents(parsed.allEvents);
168
+ // } catch (err) {
169
+ // console.error('Error reading VSCode session:', err);
170
+ // return [];
171
+ // }
175
172
  }
176
173
 
177
174
 
@@ -1864,6 +1864,15 @@
1864
1864
  }
1865
1865
 
1866
1866
  console.log('[Navigation] Events loaded:', loadedEvents.value.length);
1867
+
1868
+ // Update 'Updated' time from last event timestamp (more accurate than file mtime)
1869
+ if (loadedEvents.value.length > 0) {
1870
+ const lastEvent = loadedEvents.value[loadedEvents.value.length - 1];
1871
+ const lastTime = lastEvent.timestamp || lastEvent.time || lastEvent.data?.timestamp;
1872
+ if (lastTime) {
1873
+ metadata.value.updated = new Date(lastTime);
1874
+ }
1875
+ }
1867
1876
 
1868
1877
  // Check for URL query parameters and jump to event AFTER events are loaded
1869
1878
  const urlParams = new URLSearchParams(window.location.search);