tlc-claude-code 1.3.0 → 1.4.1

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 (105) hide show
  1. package/dashboard/dist/components/AuditPane.d.ts +30 -0
  2. package/dashboard/dist/components/AuditPane.js +127 -0
  3. package/dashboard/dist/components/AuditPane.test.d.ts +1 -0
  4. package/dashboard/dist/components/AuditPane.test.js +339 -0
  5. package/dashboard/dist/components/CompliancePane.d.ts +39 -0
  6. package/dashboard/dist/components/CompliancePane.js +96 -0
  7. package/dashboard/dist/components/CompliancePane.test.d.ts +1 -0
  8. package/dashboard/dist/components/CompliancePane.test.js +183 -0
  9. package/dashboard/dist/components/SSOPane.d.ts +36 -0
  10. package/dashboard/dist/components/SSOPane.js +71 -0
  11. package/dashboard/dist/components/SSOPane.test.d.ts +1 -0
  12. package/dashboard/dist/components/SSOPane.test.js +155 -0
  13. package/dashboard/dist/components/WorkspaceDocsPane.js +0 -16
  14. package/dashboard/dist/components/WorkspacePane.d.ts +1 -1
  15. package/dashboard/dist/components/ZeroRetentionPane.d.ts +44 -0
  16. package/dashboard/dist/components/ZeroRetentionPane.js +83 -0
  17. package/dashboard/dist/components/ZeroRetentionPane.test.d.ts +1 -0
  18. package/dashboard/dist/components/ZeroRetentionPane.test.js +160 -0
  19. package/package.json +1 -1
  20. package/server/lib/access-control-doc.js +541 -0
  21. package/server/lib/access-control-doc.test.js +672 -0
  22. package/server/lib/adr-generator.js +423 -0
  23. package/server/lib/adr-generator.test.js +586 -0
  24. package/server/lib/agent-progress-monitor.js +223 -0
  25. package/server/lib/agent-progress-monitor.test.js +202 -0
  26. package/server/lib/audit-attribution.js +191 -0
  27. package/server/lib/audit-attribution.test.js +359 -0
  28. package/server/lib/audit-classifier.js +202 -0
  29. package/server/lib/audit-classifier.test.js +209 -0
  30. package/server/lib/audit-command.js +275 -0
  31. package/server/lib/audit-command.test.js +325 -0
  32. package/server/lib/audit-exporter.js +380 -0
  33. package/server/lib/audit-exporter.test.js +464 -0
  34. package/server/lib/audit-logger.js +236 -0
  35. package/server/lib/audit-logger.test.js +364 -0
  36. package/server/lib/audit-query.js +257 -0
  37. package/server/lib/audit-query.test.js +352 -0
  38. package/server/lib/audit-storage.js +269 -0
  39. package/server/lib/audit-storage.test.js +272 -0
  40. package/server/lib/bulk-repo-init.js +342 -0
  41. package/server/lib/bulk-repo-init.test.js +388 -0
  42. package/server/lib/compliance-checklist.js +866 -0
  43. package/server/lib/compliance-checklist.test.js +476 -0
  44. package/server/lib/compliance-command.js +616 -0
  45. package/server/lib/compliance-command.test.js +551 -0
  46. package/server/lib/compliance-reporter.js +692 -0
  47. package/server/lib/compliance-reporter.test.js +707 -0
  48. package/server/lib/data-flow-doc.js +665 -0
  49. package/server/lib/data-flow-doc.test.js +659 -0
  50. package/server/lib/ephemeral-storage.js +249 -0
  51. package/server/lib/ephemeral-storage.test.js +254 -0
  52. package/server/lib/evidence-collector.js +627 -0
  53. package/server/lib/evidence-collector.test.js +901 -0
  54. package/server/lib/flow-diagram-generator.js +474 -0
  55. package/server/lib/flow-diagram-generator.test.js +446 -0
  56. package/server/lib/idp-manager.js +626 -0
  57. package/server/lib/idp-manager.test.js +587 -0
  58. package/server/lib/memory-exclusion.js +326 -0
  59. package/server/lib/memory-exclusion.test.js +241 -0
  60. package/server/lib/mfa-handler.js +452 -0
  61. package/server/lib/mfa-handler.test.js +490 -0
  62. package/server/lib/oauth-flow.js +375 -0
  63. package/server/lib/oauth-flow.test.js +487 -0
  64. package/server/lib/oauth-registry.js +190 -0
  65. package/server/lib/oauth-registry.test.js +306 -0
  66. package/server/lib/readme-generator.js +490 -0
  67. package/server/lib/readme-generator.test.js +493 -0
  68. package/server/lib/repo-dependency-tracker.js +261 -0
  69. package/server/lib/repo-dependency-tracker.test.js +350 -0
  70. package/server/lib/retention-policy.js +281 -0
  71. package/server/lib/retention-policy.test.js +486 -0
  72. package/server/lib/role-mapper.js +236 -0
  73. package/server/lib/role-mapper.test.js +395 -0
  74. package/server/lib/saml-provider.js +765 -0
  75. package/server/lib/saml-provider.test.js +643 -0
  76. package/server/lib/security-policy-generator.js +682 -0
  77. package/server/lib/security-policy-generator.test.js +544 -0
  78. package/server/lib/sensitive-detector.js +112 -0
  79. package/server/lib/sensitive-detector.test.js +209 -0
  80. package/server/lib/service-interaction-diagram.js +700 -0
  81. package/server/lib/service-interaction-diagram.test.js +638 -0
  82. package/server/lib/service-summary.js +553 -0
  83. package/server/lib/service-summary.test.js +619 -0
  84. package/server/lib/session-purge.js +460 -0
  85. package/server/lib/session-purge.test.js +312 -0
  86. package/server/lib/sso-command.js +544 -0
  87. package/server/lib/sso-command.test.js +552 -0
  88. package/server/lib/sso-session.js +492 -0
  89. package/server/lib/sso-session.test.js +670 -0
  90. package/server/lib/workspace-command.js +249 -0
  91. package/server/lib/workspace-command.test.js +264 -0
  92. package/server/lib/workspace-config.js +270 -0
  93. package/server/lib/workspace-config.test.js +312 -0
  94. package/server/lib/workspace-docs-command.js +547 -0
  95. package/server/lib/workspace-docs-command.test.js +692 -0
  96. package/server/lib/workspace-memory.js +451 -0
  97. package/server/lib/workspace-memory.test.js +403 -0
  98. package/server/lib/workspace-scanner.js +452 -0
  99. package/server/lib/workspace-scanner.test.js +677 -0
  100. package/server/lib/workspace-test-runner.js +315 -0
  101. package/server/lib/workspace-test-runner.test.js +294 -0
  102. package/server/lib/zero-retention-command.js +439 -0
  103. package/server/lib/zero-retention-command.test.js +448 -0
  104. package/server/lib/zero-retention.js +322 -0
  105. package/server/lib/zero-retention.test.js +258 -0
@@ -0,0 +1,460 @@
1
+ /**
2
+ * Session Purge Manager - Automatically purge data when sessions end
3
+ *
4
+ * Provides:
5
+ * - Session-end detection and data purging
6
+ * - Policy-based retention evaluation
7
+ * - Process exit and timeout handling
8
+ * - Audit logging for purge operations
9
+ * - Graceful and forced shutdown support
10
+ */
11
+
12
+ const { getPolicy, evaluateRetention } = require('./retention-policy.js');
13
+
14
+ /**
15
+ * Session Purge Manager
16
+ *
17
+ * Manages automatic purging of session data based on session lifecycle events
18
+ */
19
+ class SessionPurgeManager {
20
+ /**
21
+ * Create a session purge manager
22
+ * @param {Object} options - Configuration options
23
+ * @param {Object} options.storage - EphemeralStorage instance to manage
24
+ * @param {string} [options.sessionId] - Current session ID
25
+ * @param {boolean} [options.auditEnabled] - Enable audit logging
26
+ * @param {Function} [options.onAuditLog] - Callback for audit log entries
27
+ * @param {Object} [options.policies] - Custom retention policies
28
+ */
29
+ constructor(options = {}) {
30
+ this.storage = options.storage;
31
+ this.sessionId = options.sessionId || this._generateSessionId();
32
+ this.auditEnabled = options.auditEnabled || false;
33
+ this.onAuditLog = options.onAuditLog || null;
34
+ this.policies = options.policies || null;
35
+
36
+ // Track purge history
37
+ this.purgeHistory = [];
38
+ this.lastPurge = null;
39
+
40
+ // Idle timer
41
+ this.idleTimer = null;
42
+ this.idleTimeout = null;
43
+ }
44
+
45
+ /**
46
+ * Generate a unique session ID
47
+ * @private
48
+ */
49
+ _generateSessionId() {
50
+ return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
51
+ }
52
+
53
+ /**
54
+ * Log an audit entry if audit is enabled
55
+ * @private
56
+ */
57
+ _logAudit(entry) {
58
+ if (this.auditEnabled && this.onAuditLog) {
59
+ const auditEntry = {
60
+ timestamp: new Date().toISOString(),
61
+ sessionId: this.sessionId,
62
+ ...entry,
63
+ };
64
+ this.onAuditLog(auditEntry);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Get all keys from storage with their values
70
+ * @private
71
+ */
72
+ _getAllEntries() {
73
+ const entries = [];
74
+ const keys = this.storage.keys();
75
+ for (const key of keys) {
76
+ const value = this.storage.get(key);
77
+ entries.push({ key, value });
78
+ }
79
+ return entries;
80
+ }
81
+
82
+ /**
83
+ * Handle session end - purge all session-specific data
84
+ * @param {Object} [options] - Purge options
85
+ * @param {boolean} [options.respectPolicies] - Evaluate retention policies before purging
86
+ * @returns {Object} Purge result with count and keys
87
+ */
88
+ onSessionEnd(options = {}) {
89
+ const purgedKeys = [];
90
+ const entries = this._getAllEntries();
91
+
92
+ for (const { key, value } of entries) {
93
+ let shouldPurge = false;
94
+
95
+ // Check if data belongs to this session
96
+ if (value && typeof value === 'object' && value.sessionId === this.sessionId) {
97
+ shouldPurge = true;
98
+ }
99
+
100
+ // If respecting policies, evaluate retention
101
+ if (options.respectPolicies && value && typeof value === 'object') {
102
+ const policyOptions = {
103
+ sensitivityLevel: value.sensitivityLevel,
104
+ dataType: value.dataType,
105
+ };
106
+ const policy = getPolicy(policyOptions, this.policies);
107
+ const decision = evaluateRetention(value, policy, {
108
+ currentSessionId: this.sessionId,
109
+ });
110
+
111
+ if (decision === 'purge') {
112
+ shouldPurge = true;
113
+ }
114
+ }
115
+
116
+ if (shouldPurge) {
117
+ this.storage.delete(key);
118
+ purgedKeys.push(key);
119
+ }
120
+ }
121
+
122
+ // Record this purge
123
+ const purgeRecord = {
124
+ timestamp: new Date().toISOString(),
125
+ sessionId: this.sessionId,
126
+ purgedKeys,
127
+ purgedCount: purgedKeys.length,
128
+ trigger: 'sessionEnd',
129
+ };
130
+ this.purgeHistory.push(purgeRecord);
131
+ this.lastPurge = purgeRecord;
132
+
133
+ // Log audit
134
+ this._logAudit({
135
+ action: 'purge',
136
+ trigger: 'sessionEnd',
137
+ purgedCount: purgedKeys.length,
138
+ keys: purgedKeys,
139
+ });
140
+
141
+ return {
142
+ purgedKeys,
143
+ purgedCount: purgedKeys.length,
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Handle process exit - purge session data
149
+ * @returns {Object} Purge result
150
+ */
151
+ onProcessExit() {
152
+ const purgedKeys = [];
153
+ const entries = this._getAllEntries();
154
+
155
+ for (const { key, value } of entries) {
156
+ // On process exit, purge session-specific data
157
+ if (value && typeof value === 'object' && value.sessionId === this.sessionId) {
158
+ this.storage.delete(key);
159
+ purgedKeys.push(key);
160
+ }
161
+ }
162
+
163
+ const purgeRecord = {
164
+ timestamp: new Date().toISOString(),
165
+ sessionId: this.sessionId,
166
+ purgedKeys,
167
+ purgedCount: purgedKeys.length,
168
+ trigger: 'processExit',
169
+ };
170
+ this.purgeHistory.push(purgeRecord);
171
+ this.lastPurge = purgeRecord;
172
+
173
+ this._logAudit({
174
+ action: 'purge',
175
+ trigger: 'processExit',
176
+ purgedCount: purgedKeys.length,
177
+ });
178
+
179
+ return {
180
+ purgedKeys,
181
+ purgedCount: purgedKeys.length,
182
+ };
183
+ }
184
+
185
+ /**
186
+ * Start idle timer - purge after inactivity
187
+ * @param {number} timeout - Idle timeout in milliseconds
188
+ */
189
+ startIdleTimer(timeout) {
190
+ this.idleTimeout = timeout;
191
+ this._scheduleIdleTimer();
192
+ }
193
+
194
+ /**
195
+ * Reset idle timer on activity
196
+ */
197
+ resetIdleTimer() {
198
+ if (this.idleTimer) {
199
+ clearTimeout(this.idleTimer);
200
+ this._scheduleIdleTimer();
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Stop idle timer
206
+ */
207
+ stopIdleTimer() {
208
+ if (this.idleTimer) {
209
+ clearTimeout(this.idleTimer);
210
+ this.idleTimer = null;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Schedule the idle timer
216
+ * @private
217
+ */
218
+ _scheduleIdleTimer() {
219
+ if (this.idleTimeout) {
220
+ this.idleTimer = setTimeout(() => {
221
+ this._onTimeout();
222
+ }, this.idleTimeout);
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Handle idle timeout
228
+ * @private
229
+ */
230
+ _onTimeout() {
231
+ const purgedKeys = [];
232
+ const entries = this._getAllEntries();
233
+
234
+ for (const { key, value } of entries) {
235
+ if (value && typeof value === 'object' && value.sessionId === this.sessionId) {
236
+ this.storage.delete(key);
237
+ purgedKeys.push(key);
238
+ }
239
+ }
240
+
241
+ const purgeRecord = {
242
+ timestamp: new Date().toISOString(),
243
+ sessionId: this.sessionId,
244
+ purgedKeys,
245
+ purgedCount: purgedKeys.length,
246
+ trigger: 'timeout',
247
+ };
248
+ this.purgeHistory.push(purgeRecord);
249
+ this.lastPurge = purgeRecord;
250
+
251
+ this._logAudit({
252
+ action: 'purge',
253
+ trigger: 'timeout',
254
+ purgedCount: purgedKeys.length,
255
+ });
256
+
257
+ return {
258
+ purgedKeys,
259
+ purgedCount: purgedKeys.length,
260
+ };
261
+ }
262
+
263
+ /**
264
+ * Purge data matching specific policy criteria
265
+ * @param {Object} criteria - Policy criteria to match
266
+ * @param {string} [criteria.sensitivityLevel] - Sensitivity level to purge
267
+ * @param {string} [criteria.dataType] - Data type to purge
268
+ * @returns {Object} Purge result
269
+ */
270
+ purgeByPolicy(criteria = {}) {
271
+ const purgedKeys = [];
272
+ const entries = this._getAllEntries();
273
+
274
+ for (const { key, value } of entries) {
275
+ if (!value || typeof value !== 'object') continue;
276
+
277
+ let shouldPurge = false;
278
+
279
+ // Match by sensitivity level
280
+ if (criteria.sensitivityLevel && value.sensitivityLevel === criteria.sensitivityLevel) {
281
+ shouldPurge = true;
282
+ }
283
+
284
+ // Match by data type
285
+ if (criteria.dataType && value.dataType === criteria.dataType) {
286
+ shouldPurge = true;
287
+ }
288
+
289
+ if (shouldPurge) {
290
+ this.storage.delete(key);
291
+ purgedKeys.push(key);
292
+ }
293
+ }
294
+
295
+ const purgeRecord = {
296
+ timestamp: new Date().toISOString(),
297
+ sessionId: this.sessionId,
298
+ purgedKeys,
299
+ purgedCount: purgedKeys.length,
300
+ trigger: 'policy',
301
+ criteria,
302
+ };
303
+ this.purgeHistory.push(purgeRecord);
304
+ this.lastPurge = purgeRecord;
305
+
306
+ this._logAudit({
307
+ action: 'purge',
308
+ trigger: 'policy',
309
+ criteria,
310
+ purgedCount: purgedKeys.length,
311
+ });
312
+
313
+ return {
314
+ purgedKeys,
315
+ purgedCount: purgedKeys.length,
316
+ };
317
+ }
318
+
319
+ /**
320
+ * Force purge all data immediately
321
+ * @returns {Object} Purge result
322
+ */
323
+ forcePurge() {
324
+ const keys = this.storage.keys();
325
+ const purgedKeys = [...keys];
326
+ const purgedCount = keys.length;
327
+
328
+ this.storage.clear();
329
+
330
+ const purgeRecord = {
331
+ timestamp: new Date().toISOString(),
332
+ sessionId: this.sessionId,
333
+ purgedKeys,
334
+ purgedCount,
335
+ trigger: 'forced',
336
+ forced: true,
337
+ };
338
+ this.purgeHistory.push(purgeRecord);
339
+ this.lastPurge = purgeRecord;
340
+
341
+ this._logAudit({
342
+ action: 'purge',
343
+ trigger: 'forced',
344
+ purgedCount,
345
+ });
346
+
347
+ return {
348
+ purgedKeys,
349
+ purgedCount,
350
+ forced: true,
351
+ };
352
+ }
353
+
354
+ /**
355
+ * Get report of what was purged
356
+ * @returns {Object} Purge report
357
+ */
358
+ getPurgeReport() {
359
+ const totalPurged = this.purgeHistory.reduce((sum, record) => sum + record.purgedCount, 0);
360
+
361
+ if (!this.lastPurge) {
362
+ return {
363
+ purgedKeys: [],
364
+ purgedCount: 0,
365
+ timestamp: null,
366
+ sessionId: this.sessionId,
367
+ totalPurged: 0,
368
+ history: [],
369
+ };
370
+ }
371
+
372
+ return {
373
+ purgedKeys: this.lastPurge.purgedKeys,
374
+ purgedCount: this.lastPurge.purgedCount,
375
+ timestamp: this.lastPurge.timestamp,
376
+ sessionId: this.sessionId,
377
+ totalPurged,
378
+ history: this.purgeHistory,
379
+ };
380
+ }
381
+
382
+ /**
383
+ * Perform graceful shutdown with timeout
384
+ * @param {Object} [options] - Shutdown options
385
+ * @param {number} [options.timeout] - Timeout in milliseconds before force purge
386
+ * @returns {Promise<Object>} Shutdown result
387
+ */
388
+ async gracefulShutdown(options = {}) {
389
+ const timeout = options.timeout || 5000;
390
+
391
+ return new Promise((resolve) => {
392
+ let timedOut = false;
393
+
394
+ // Set up timeout for force purge
395
+ const timeoutId = setTimeout(() => {
396
+ timedOut = true;
397
+ const result = this.forcePurge();
398
+ resolve({
399
+ ...result,
400
+ graceful: false,
401
+ timedOut: true,
402
+ });
403
+ }, timeout);
404
+
405
+ // Try graceful purge
406
+ try {
407
+ // Stop idle timer if running
408
+ this.stopIdleTimer();
409
+
410
+ // Purge session data
411
+ const result = this.onSessionEnd({ respectPolicies: true });
412
+
413
+ if (!timedOut) {
414
+ clearTimeout(timeoutId);
415
+ resolve({
416
+ ...result,
417
+ graceful: true,
418
+ timedOut: false,
419
+ });
420
+ }
421
+ } catch (error) {
422
+ if (!timedOut) {
423
+ clearTimeout(timeoutId);
424
+ // On error, force purge
425
+ const result = this.forcePurge();
426
+ resolve({
427
+ ...result,
428
+ graceful: false,
429
+ error: error.message,
430
+ });
431
+ }
432
+ }
433
+ });
434
+ }
435
+
436
+ /**
437
+ * Register process exit handlers
438
+ */
439
+ registerProcessHandlers() {
440
+ const exitHandler = () => this.onProcessExit();
441
+
442
+ process.on('exit', exitHandler);
443
+ process.on('SIGINT', exitHandler);
444
+ process.on('SIGTERM', exitHandler);
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Factory function to create a SessionPurgeManager
450
+ * @param {Object} options - Configuration options
451
+ * @returns {SessionPurgeManager} New instance
452
+ */
453
+ function createPurgeManager(options = {}) {
454
+ return new SessionPurgeManager(options);
455
+ }
456
+
457
+ module.exports = {
458
+ SessionPurgeManager,
459
+ createPurgeManager,
460
+ };