@qiaolei81/copilot-session-viewer 0.3.3 → 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 (60) hide show
  1. package/bin/copilot-session-viewer +2 -2
  2. package/dist/server.min.js +99 -0
  3. package/package.json +14 -3
  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/.nycrc +0 -29
  12. package/AGENTS.md +0 -109
  13. package/CHANGELOG.md +0 -313
  14. package/CONTRIBUTING.md +0 -104
  15. package/RELEASE.md +0 -146
  16. package/docs/API.md +0 -471
  17. package/docs/DEVELOPMENT.md +0 -556
  18. package/docs/INSTALLATION.md +0 -329
  19. package/docs/README.md +0 -102
  20. package/docs/TROUBLESHOOTING.md +0 -630
  21. package/docs/images/homepage.png +0 -0
  22. package/docs/images/session-detail.png +0 -0
  23. package/docs/images/time-analysis.png +0 -0
  24. package/docs/unified-event-format-design.md +0 -844
  25. package/docs/unified-event-format-implementation.md +0 -350
  26. package/eslint.config.mjs +0 -133
  27. package/examples/parser-usage.js +0 -114
  28. package/lib/parsers/README.md +0 -239
  29. package/lib/parsers/base-parser.js +0 -53
  30. package/lib/parsers/claude-parser.js +0 -181
  31. package/lib/parsers/copilot-parser.js +0 -143
  32. package/lib/parsers/index.js +0 -15
  33. package/lib/parsers/parser-factory.js +0 -77
  34. package/lib/parsers/pi-mono-parser.js +0 -119
  35. package/lib/parsers/vscode-parser.js +0 -591
  36. package/scripts/release.sh +0 -43
  37. package/server.js +0 -29
  38. package/src/app.js +0 -129
  39. package/src/config/index.js +0 -27
  40. package/src/controllers/insightController.js +0 -136
  41. package/src/controllers/sessionController.js +0 -449
  42. package/src/controllers/tagController.js +0 -113
  43. package/src/controllers/uploadController.js +0 -648
  44. package/src/middleware/common.js +0 -67
  45. package/src/middleware/rateLimiting.js +0 -62
  46. package/src/models/Session.js +0 -146
  47. package/src/routes/api.js +0 -11
  48. package/src/routes/insights.js +0 -12
  49. package/src/routes/pages.js +0 -12
  50. package/src/routes/uploads.js +0 -14
  51. package/src/schemas/event.schema.js +0 -73
  52. package/src/services/eventNormalizer.js +0 -291
  53. package/src/services/insightService.js +0 -535
  54. package/src/services/sessionRepository.js +0 -1092
  55. package/src/services/sessionService.js +0 -1919
  56. package/src/services/tagService.js +0 -205
  57. package/src/telemetry.js +0 -152
  58. package/src/utils/fileUtils.js +0 -305
  59. package/src/utils/helpers.js +0 -45
  60. package/src/utils/processManager.js +0 -85
@@ -1,43 +0,0 @@
1
- #!/bin/bash
2
- # Bump version and create GitHub release
3
- # Usage: ./release.sh [patch|minor|major]
4
-
5
- set -e
6
-
7
- # Default to patch if no argument
8
- VERSION_TYPE=${1:-patch}
9
-
10
- # Validate version type
11
- if [[ ! "$VERSION_TYPE" =~ ^(patch|minor|major)$ ]]; then
12
- echo "❌ Invalid version type: $VERSION_TYPE"
13
- echo "Usage: $0 [patch|minor|major]"
14
- exit 1
15
- fi
16
-
17
- # Ensure working directory is clean
18
- if [[ -n $(git status -s) ]]; then
19
- echo "❌ Working directory is not clean. Commit or stash changes first."
20
- exit 1
21
- fi
22
-
23
- # Bump version
24
- echo "📦 Bumping $VERSION_TYPE version..."
25
- npm version $VERSION_TYPE -m "chore: release v%s"
26
-
27
- # Get new version
28
- NEW_VERSION=$(node -p "require('./package.json').version")
29
- echo "✅ Version bumped to: $NEW_VERSION"
30
-
31
- # Push tag and code
32
- echo "🚀 Pushing to GitHub..."
33
- git push --follow-tags
34
-
35
- # Create GitHub release
36
- echo "🎉 Creating GitHub release..."
37
- gh release create "v$NEW_VERSION" \
38
- --title "v$NEW_VERSION" \
39
- --generate-notes \
40
- --verify-tag
41
-
42
- echo "✅ Release v$NEW_VERSION created!"
43
- echo "🔗 https://github.com/qiaolei81/copilot-session-viewer/releases/tag/v$NEW_VERSION"
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;