@qiaolei81/copilot-session-viewer 0.3.4 → 0.3.5

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 (42) hide show
  1. package/bin/copilot-session-viewer +2 -2
  2. package/dist/server.min.js +99 -0
  3. package/package.json +5 -17
  4. package/public/vendor/marked.umd.min.js +8 -0
  5. package/public/vendor/purify.min.js +3 -0
  6. package/public/vendor/vue-virtual-scroller.css +1 -0
  7. package/public/vendor/vue-virtual-scroller.min.js +2 -0
  8. package/public/vendor/vue.global.prod.min.js +19 -0
  9. package/views/session-vue.ejs +5 -5
  10. package/views/time-analyze.ejs +2 -2
  11. package/lib/parsers/README.md +0 -239
  12. package/lib/parsers/base-parser.js +0 -53
  13. package/lib/parsers/claude-parser.js +0 -181
  14. package/lib/parsers/copilot-parser.js +0 -143
  15. package/lib/parsers/index.js +0 -15
  16. package/lib/parsers/parser-factory.js +0 -77
  17. package/lib/parsers/pi-mono-parser.js +0 -119
  18. package/lib/parsers/vscode-parser.js +0 -591
  19. package/server.js +0 -29
  20. package/src/app.js +0 -129
  21. package/src/config/index.js +0 -27
  22. package/src/controllers/insightController.js +0 -136
  23. package/src/controllers/sessionController.js +0 -449
  24. package/src/controllers/tagController.js +0 -113
  25. package/src/controllers/uploadController.js +0 -648
  26. package/src/middleware/common.js +0 -67
  27. package/src/middleware/rateLimiting.js +0 -62
  28. package/src/models/Session.js +0 -146
  29. package/src/routes/api.js +0 -11
  30. package/src/routes/insights.js +0 -12
  31. package/src/routes/pages.js +0 -12
  32. package/src/routes/uploads.js +0 -14
  33. package/src/schemas/event.schema.js +0 -73
  34. package/src/services/eventNormalizer.js +0 -291
  35. package/src/services/insightService.js +0 -535
  36. package/src/services/sessionRepository.js +0 -1092
  37. package/src/services/sessionService.js +0 -1919
  38. package/src/services/tagService.js +0 -205
  39. package/src/telemetry.js +0 -152
  40. package/src/utils/fileUtils.js +0 -305
  41. package/src/utils/helpers.js +0 -45
  42. package/src/utils/processManager.js +0 -85
package/server.js DELETED
@@ -1,29 +0,0 @@
1
- // IMPORTANT: Initialize telemetry FIRST, before any other requires (especially Express)
2
- require('./src/telemetry');
3
-
4
- const createApp = require('./src/app');
5
- const config = require('./src/config');
6
- const processManager = require('./src/utils/processManager');
7
-
8
- // Create the Express app
9
- const app = createApp();
10
-
11
- // Export app for testing
12
- module.exports = app;
13
-
14
- // Start server only if not being required by tests
15
- if (require.main === module) {
16
- const server = app.listen(config.PORT, () => {
17
- console.log(`🚀 Copilot Session Viewer running at http://localhost:${config.PORT}`);
18
- console.log(`🔧 Environment: ${config.NODE_ENV}`);
19
- console.log(`⚡ Active processes: ${processManager.getActiveCount()}`);
20
- });
21
-
22
- // Graceful shutdown
23
- process.on('SIGTERM', () => {
24
- console.log('📛 SIGTERM received, closing server...');
25
- server.close(() => {
26
- console.log('✅ Server closed');
27
- });
28
- });
29
- }
package/src/app.js DELETED
@@ -1,129 +0,0 @@
1
- const express = require('express');
2
- const path = require('path');
3
- const compression = require('compression');
4
- const helmet = require('helmet');
5
-
6
- // Configuration
7
- const config = require('./config');
8
-
9
- // Middleware
10
- // Rate limiting disabled for local development
11
- // const { globalLimiter, insightGenerationLimiter, insightAccessLimiter, uploadLimiter } = require('./middleware/rateLimiting');
12
- const { requestTimeout, developmentCors, errorHandler, notFoundHandler, telemetryLocals } = require('./middleware/common');
13
-
14
- // Controllers
15
- const SessionController = require('./controllers/sessionController');
16
- const InsightController = require('./controllers/insightController');
17
- const UploadController = require('./controllers/uploadController');
18
- const TagController = require('./controllers/tagController');
19
-
20
- function createApp(options = {}) {
21
- const app = express();
22
-
23
- // Disable Express's automatic ETag generation (prevents 304 on live/active session files)
24
- app.set('etag', false);
25
-
26
- // Create controller instances (with optional dependency injection)
27
- const sessionController = new SessionController(options.sessionService);
28
- const insightController = new InsightController(options.insightService, options.sessionService);
29
- const uploadController = new UploadController();
30
- const tagController = new TagController(options.tagService);
31
-
32
- // Minimal security headers for local development tool
33
- // Custom CSP without upgrade-insecure-requests
34
- app.use((req, res, next) => {
35
- res.setHeader(
36
- 'Content-Security-Policy',
37
- "default-src 'self'; " +
38
- "style-src 'self' 'unsafe-inline' https: http:; " +
39
- "font-src 'self' https: http:; " +
40
- "script-src 'self' 'unsafe-inline' 'unsafe-eval' https: http:; " +
41
- "img-src 'self' data: https: http:; " +
42
- "connect-src 'self' https: http:"
43
- );
44
- next();
45
- });
46
-
47
- // Other helmet protections (without CSP and HSTS)
48
- app.use(helmet({
49
- contentSecurityPolicy: false,
50
- hsts: false,
51
- referrerPolicy: false,
52
- crossOriginEmbedderPolicy: false,
53
- crossOriginOpenerPolicy: false,
54
- crossOriginResourcePolicy: false
55
- }));
56
-
57
- app.use(compression({
58
- level: 1, // Fast compression (speed > ratio for local use)
59
- threshold: 1024, // Compress responses > 1KB
60
- filter: (req, res) => {
61
- // Skip compression for large JSON API responses (handled separately)
62
- if (req.path.includes('/events') && res.getHeader('Content-Type')?.includes('application/json')) {
63
- return false;
64
- }
65
- return compression.filter(req, res);
66
- }
67
- }));
68
- app.use(express.json({ limit: '1mb' }));
69
- app.use(express.urlencoded({ extended: true }));
70
- app.use(requestTimeout);
71
- app.use(telemetryLocals);
72
-
73
- // CORS in development
74
- if (config.NODE_ENV === 'development') {
75
- app.use(developmentCors);
76
- }
77
-
78
- // Rate limiting - DISABLED for local development
79
- // app.use(globalLimiter);
80
-
81
- // Static files
82
- app.use('/public', express.static(path.join(__dirname, '../public')));
83
-
84
- // View engine
85
- app.set('view engine', 'ejs');
86
- app.set('views', path.join(__dirname, '../views'));
87
-
88
- // Routes with controllers
89
-
90
- // Page routes
91
- app.get('/', sessionController.getHomepage.bind(sessionController));
92
- app.get('/session/:id', sessionController.getSessionDetail.bind(sessionController));
93
- app.get('/session/:id/time-analyze', sessionController.getTimeAnalysis.bind(sessionController));
94
- app.get('/session/:id/export', sessionController.exportSession.bind(sessionController));
95
-
96
- // API routes (more specific routes first)
97
- app.get('/api/sessions/load-more', sessionController.loadMoreSessions.bind(sessionController));
98
- app.get('/api/sessions', sessionController.getSessions.bind(sessionController));
99
- app.get('/api/sessions/:id/events', sessionController.getSessionEvents.bind(sessionController));
100
- app.get('/api/sessions/:id/timeline', sessionController.getTimeline.bind(sessionController));
101
-
102
- // Tag routes
103
- app.get('/api/tags', tagController.getAllTags.bind(tagController));
104
- app.get('/api/sessions/:id/tags', tagController.getSessionTags.bind(tagController));
105
- app.put('/api/sessions/:id/tags', tagController.setSessionTags.bind(tagController));
106
-
107
- // Upload routes
108
- app.get('/session/:id/share', uploadController.shareSession.bind(uploadController));
109
- app.post('/session/import',
110
- (req, res, next) => uploadController.getUploadMiddleware()(req, res, next),
111
- uploadController.importSession.bind(uploadController)
112
- );
113
-
114
- // Insight routes (rate limiting disabled)
115
- app.post('/session/:id/insight', insightController.generateInsight.bind(insightController));
116
- app.get('/session/:id/insight', insightController.getInsightStatus.bind(insightController));
117
- app.delete('/session/:id/insight', insightController.deleteInsight.bind(insightController));
118
-
119
- // Upload rate limiting - DISABLED
120
- // app.use('/session/import', uploadLimiter);
121
-
122
- // Error handling
123
- app.use(notFoundHandler);
124
- app.use(errorHandler);
125
-
126
- return app;
127
- }
128
-
129
- module.exports = createApp;
@@ -1,27 +0,0 @@
1
- /**
2
- * Application Configuration Constants
3
- */
4
-
5
- module.exports = {
6
- // Server
7
- PORT: process.env.PORT || 3838,
8
- NODE_ENV: process.env.NODE_ENV || 'production', // Default to production for security
9
-
10
- // Logging
11
- LOG_LEVEL: process.env.LOG_LEVEL || (process.env.NODE_ENV === 'development' ? 'DEBUG' : 'INFO'),
12
-
13
- // Insight Generation
14
- INSIGHT_TIMEOUT_MS: 5 * 60 * 1000, // 5 minutes
15
- INSIGHT_CACHE_TTL_MS: 24 * 60 * 60 * 1000, // 24 hours
16
-
17
- // File Upload
18
- MAX_UPLOAD_SIZE: 10 * 1024 * 1024, // 10MB (reduced from 50MB for security)
19
-
20
- // Session Repository
21
- SESSION_CACHE_TTL_MS: 30 * 1000, // 30 seconds
22
-
23
- // Request Limits - More lenient for better UX
24
- RATE_LIMIT_WINDOW_MS: 15 * 60 * 1000, // 15 minutes
25
- RATE_LIMIT_MAX_REQUESTS: 15, // Increased from 5 to 15 for better UX
26
- REQUEST_TIMEOUT_MS: 30 * 1000 // 30 seconds
27
- };
@@ -1,136 +0,0 @@
1
- const InsightService = require('../services/insightService');
2
- const { isValidSessionId } = require('../utils/helpers');
3
- const { trackEvent, trackMetric, trackException } = require('../telemetry');
4
-
5
- class InsightController {
6
- constructor(insightService = null, sessionService = null) {
7
- if (insightService) {
8
- this.insightService = insightService;
9
- } else {
10
- // Use default multi-source configuration
11
- this.insightService = new InsightService();
12
- }
13
-
14
- // SessionService for getting session metadata (source)
15
- if (sessionService) {
16
- this.sessionService = sessionService;
17
- } else {
18
- const SessionService = require('../services/sessionService');
19
- this.sessionService = new SessionService();
20
- }
21
- }
22
-
23
- // Generate or get insight
24
- async generateInsight(req, res) {
25
- try {
26
- const sessionId = req.params.id;
27
- const forceRegenerate = req.body?.force === true;
28
-
29
- if (!isValidSessionId(sessionId)) {
30
- return res.status(400).json({ error: 'Invalid session ID' });
31
- }
32
-
33
- // Get session to determine source and directory
34
- const session = await this.sessionService.getSessionById(sessionId);
35
- if (!session) {
36
- return res.status(404).json({ error: 'Session not found' });
37
- }
38
-
39
- if (!session.directory) {
40
- return res.status(400).json({ error: 'Session directory not available' });
41
- }
42
-
43
- const startTime = Date.now();
44
- const result = await this.insightService.generateInsight(session.id, session.directory, session.source, forceRegenerate);
45
- const durationMs = Date.now() - startTime;
46
-
47
- // Track InsightGenerated event
48
- trackEvent('InsightGenerated', {
49
- sessionId,
50
- source: session.source || 'unknown',
51
- durationMs: durationMs.toString()
52
- });
53
-
54
- // Track InsightGenerationTime metric
55
- trackMetric('InsightGenerationTime', durationMs, { sessionId, source: session.source || 'unknown' });
56
-
57
- res.json(result);
58
- } catch (err) {
59
- console.error('Error generating insight:', err);
60
-
61
- // Track insight generation failure
62
- trackException(err, {
63
- sessionId: req.params.id,
64
- operation: 'generateInsight'
65
- });
66
-
67
- res.status(500).json({ error: err.message || 'Error generating insight' });
68
- }
69
- }
70
-
71
- // Get insight status
72
- async getInsightStatus(req, res) {
73
- try {
74
- const sessionId = req.params.id;
75
-
76
- if (!isValidSessionId(sessionId)) {
77
- return res.status(400).json({ error: 'Invalid session ID' });
78
- }
79
-
80
- // Get session to determine directory
81
- const session = await this.sessionService.getSessionById(sessionId);
82
- if (!session) {
83
- return res.status(404).json({ error: 'Session not found' });
84
- }
85
-
86
- if (!session.directory) {
87
- return res.status(400).json({ error: 'Session directory not available' });
88
- }
89
-
90
- const result = await this.insightService.getInsightStatus(session.id, session.directory, session.source);
91
-
92
- // Track InsightViewed event if insight is ready
93
- if (result.status === 'ready' && result.report) {
94
- trackEvent('InsightViewed', { sessionId });
95
- }
96
-
97
- res.json(result);
98
- } catch (err) {
99
- console.error('Error getting insight status:', err);
100
- res.status(500).json({ error: 'Error getting insight status' });
101
- }
102
- }
103
-
104
- // Delete insight
105
- async deleteInsight(req, res) {
106
- try {
107
- const sessionId = req.params.id;
108
-
109
- if (!isValidSessionId(sessionId)) {
110
- return res.status(400).json({ error: 'Invalid session ID' });
111
- }
112
-
113
- // Get session to determine directory
114
- const session = await this.sessionService.getSessionById(sessionId);
115
- if (!session) {
116
- return res.status(404).json({ error: 'Session not found' });
117
- }
118
-
119
- if (!session.directory) {
120
- return res.status(400).json({ error: 'Session directory not available' });
121
- }
122
-
123
- const result = await this.insightService.deleteInsight(session.id, session.directory, session.source);
124
-
125
- // Track InsightDeleted event
126
- trackEvent('InsightDeleted', { sessionId });
127
-
128
- res.json(result);
129
- } catch (err) {
130
- console.error('Error deleting insight:', err);
131
- res.status(500).json({ error: 'Error deleting insight' });
132
- }
133
- }
134
- }
135
-
136
- module.exports = InsightController;