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,448 @@
1
+ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
2
+
3
+ describe('zero-retention-command', () => {
4
+ let ZeroRetentionCommand;
5
+ let parseArgs;
6
+ let command;
7
+ let mockZeroRetention;
8
+
9
+ beforeEach(async () => {
10
+ // Reset modules
11
+ vi.resetModules();
12
+
13
+ // Create mocks for zero-retention module
14
+ mockZeroRetention = {
15
+ enable: vi.fn().mockReturnValue({
16
+ success: true,
17
+ enabled: true,
18
+ subsystems: {
19
+ ephemeralStorage: true,
20
+ sessionPurge: true,
21
+ memoryExclusion: true,
22
+ },
23
+ config: {
24
+ enabled: true,
25
+ ephemeralStorage: { encrypt: true },
26
+ sessionPurge: { aggressive: true },
27
+ memoryExclusion: { excludeAll: true },
28
+ retentionPolicy: { retention: 'immediate', persist: false },
29
+ },
30
+ }),
31
+ disable: vi.fn().mockReturnValue({
32
+ success: true,
33
+ enabled: false,
34
+ }),
35
+ isEnabled: vi.fn().mockReturnValue(false),
36
+ getConfig: vi.fn().mockReturnValue({
37
+ enabled: false,
38
+ ephemeralStorage: null,
39
+ sessionPurge: null,
40
+ memoryExclusion: null,
41
+ retentionPolicy: null,
42
+ }),
43
+ validate: vi.fn().mockReturnValue({
44
+ valid: true,
45
+ conflicts: [],
46
+ warnings: [],
47
+ }),
48
+ };
49
+
50
+ // Mock the zero-retention module
51
+ vi.doMock('./zero-retention.js', () => mockZeroRetention);
52
+
53
+ // Import the module after mocking
54
+ const module = await import('./zero-retention-command.js');
55
+ ZeroRetentionCommand = module.ZeroRetentionCommand;
56
+ parseArgs = module.parseArgs;
57
+
58
+ // Create instance with mocks
59
+ command = new ZeroRetentionCommand({
60
+ zeroRetention: mockZeroRetention,
61
+ });
62
+ });
63
+
64
+ afterEach(() => {
65
+ vi.resetAllMocks();
66
+ });
67
+
68
+ describe('parseArgs', () => {
69
+ it('parses empty args', () => {
70
+ const result = parseArgs([]);
71
+ expect(result).toEqual({
72
+ subcommand: null,
73
+ set: null,
74
+ force: false,
75
+ });
76
+ });
77
+
78
+ it('parses enable subcommand', () => {
79
+ const result = parseArgs(['enable']);
80
+ expect(result.subcommand).toBe('enable');
81
+ });
82
+
83
+ it('parses disable subcommand', () => {
84
+ const result = parseArgs(['disable']);
85
+ expect(result.subcommand).toBe('disable');
86
+ });
87
+
88
+ it('parses status subcommand', () => {
89
+ const result = parseArgs(['status']);
90
+ expect(result.subcommand).toBe('status');
91
+ });
92
+
93
+ it('parses purge subcommand', () => {
94
+ const result = parseArgs(['purge']);
95
+ expect(result.subcommand).toBe('purge');
96
+ });
97
+
98
+ it('parses config subcommand', () => {
99
+ const result = parseArgs(['config']);
100
+ expect(result.subcommand).toBe('config');
101
+ });
102
+
103
+ it('parses --set flag with value', () => {
104
+ const result = parseArgs(['config', '--set', 'auditLogging=true']);
105
+ expect(result.subcommand).toBe('config');
106
+ expect(result.set).toBe('auditLogging=true');
107
+ });
108
+
109
+ it('parses --set= syntax', () => {
110
+ const result = parseArgs(['config', '--set=auditLogging=true']);
111
+ expect(result.set).toBe('auditLogging=true');
112
+ });
113
+
114
+ it('parses --force flag', () => {
115
+ const result = parseArgs(['purge', '--force']);
116
+ expect(result.subcommand).toBe('purge');
117
+ expect(result.force).toBe(true);
118
+ });
119
+ });
120
+
121
+ describe('execute', () => {
122
+ describe('enable subcommand', () => {
123
+ it('execute enable activates mode', async () => {
124
+ const result = await command.execute(['enable']);
125
+
126
+ expect(result.success).toBe(true);
127
+ expect(mockZeroRetention.enable).toHaveBeenCalled();
128
+ expect(result.output).toContain('enabled');
129
+ });
130
+
131
+ it('reports when already enabled', async () => {
132
+ mockZeroRetention.isEnabled.mockReturnValue(true);
133
+ mockZeroRetention.enable.mockReturnValue({
134
+ success: true,
135
+ enabled: true,
136
+ });
137
+
138
+ const result = await command.execute(['enable']);
139
+
140
+ expect(result.success).toBe(true);
141
+ expect(result.output).toContain('enabled');
142
+ });
143
+ });
144
+
145
+ describe('disable subcommand', () => {
146
+ it('execute disable deactivates mode', async () => {
147
+ mockZeroRetention.isEnabled.mockReturnValue(true);
148
+
149
+ const result = await command.execute(['disable']);
150
+
151
+ expect(result.success).toBe(true);
152
+ expect(mockZeroRetention.disable).toHaveBeenCalled();
153
+ expect(result.output).toContain('disabled');
154
+ });
155
+
156
+ it('reports when already disabled', async () => {
157
+ mockZeroRetention.isEnabled.mockReturnValue(false);
158
+
159
+ const result = await command.execute(['disable']);
160
+
161
+ expect(result.success).toBe(true);
162
+ expect(result.output).toContain('disabled');
163
+ });
164
+ });
165
+
166
+ describe('status subcommand', () => {
167
+ it('execute status shows current state', async () => {
168
+ mockZeroRetention.isEnabled.mockReturnValue(true);
169
+ mockZeroRetention.getConfig.mockReturnValue({
170
+ enabled: true,
171
+ ephemeralStorage: { encrypt: true },
172
+ sessionPurge: { aggressive: true },
173
+ memoryExclusion: { excludeAll: true },
174
+ retentionPolicy: { retention: 'immediate', persist: false },
175
+ });
176
+
177
+ const result = await command.execute(['status']);
178
+
179
+ expect(result.success).toBe(true);
180
+ expect(mockZeroRetention.isEnabled).toHaveBeenCalled();
181
+ expect(result.output).toContain('ENABLED');
182
+ });
183
+
184
+ it('execute status shows policy summary', async () => {
185
+ mockZeroRetention.isEnabled.mockReturnValue(true);
186
+ mockZeroRetention.getConfig.mockReturnValue({
187
+ enabled: true,
188
+ ephemeralStorage: { encrypt: true },
189
+ sessionPurge: { aggressive: true },
190
+ memoryExclusion: { excludeAll: true },
191
+ retentionPolicy: { retention: 'immediate', persist: false },
192
+ });
193
+ mockZeroRetention.validate.mockReturnValue({
194
+ valid: true,
195
+ conflicts: [],
196
+ warnings: [],
197
+ });
198
+
199
+ const result = await command.execute(['status']);
200
+
201
+ expect(result.success).toBe(true);
202
+ expect(result.output).toContain('immediate');
203
+ });
204
+
205
+ it('shows disabled state when mode is off', async () => {
206
+ mockZeroRetention.isEnabled.mockReturnValue(false);
207
+
208
+ const result = await command.execute(['status']);
209
+
210
+ expect(result.success).toBe(true);
211
+ expect(result.output).toContain('DISABLED');
212
+ });
213
+
214
+ it('shows warnings if present', async () => {
215
+ mockZeroRetention.isEnabled.mockReturnValue(true);
216
+ mockZeroRetention.getConfig.mockReturnValue({
217
+ enabled: true,
218
+ auditLogging: true,
219
+ });
220
+ mockZeroRetention.validate.mockReturnValue({
221
+ valid: true,
222
+ conflicts: [],
223
+ warnings: ['Audit logging conflicts with zero-retention'],
224
+ });
225
+
226
+ const result = await command.execute(['status']);
227
+
228
+ expect(result.success).toBe(true);
229
+ expect(result.output).toContain('Warning');
230
+ });
231
+ });
232
+
233
+ describe('purge subcommand', () => {
234
+ it('execute purge forces immediate purge', async () => {
235
+ mockZeroRetention.isEnabled.mockReturnValue(true);
236
+
237
+ // Mock the purge manager
238
+ const mockPurgeManager = {
239
+ forcePurge: vi.fn().mockReturnValue({
240
+ purgedCount: 5,
241
+ purgedKeys: ['key1', 'key2', 'key3', 'key4', 'key5'],
242
+ forced: true,
243
+ }),
244
+ };
245
+
246
+ command = new ZeroRetentionCommand({
247
+ zeroRetention: mockZeroRetention,
248
+ purgeManager: mockPurgeManager,
249
+ });
250
+
251
+ const result = await command.execute(['purge']);
252
+
253
+ expect(result.success).toBe(true);
254
+ expect(mockPurgeManager.forcePurge).toHaveBeenCalled();
255
+ expect(result.output).toContain('purge');
256
+ });
257
+
258
+ it('requires enabled mode for purge', async () => {
259
+ mockZeroRetention.isEnabled.mockReturnValue(false);
260
+
261
+ const result = await command.execute(['purge']);
262
+
263
+ expect(result.success).toBe(false);
264
+ expect(result.error).toContain('not enabled');
265
+ });
266
+ });
267
+
268
+ describe('config subcommand', () => {
269
+ it('execute config shows configuration', async () => {
270
+ mockZeroRetention.getConfig.mockReturnValue({
271
+ enabled: true,
272
+ ephemeralStorage: { encrypt: true },
273
+ sessionPurge: { aggressive: true },
274
+ memoryExclusion: { excludeAll: true },
275
+ retentionPolicy: { retention: 'immediate', persist: false },
276
+ });
277
+
278
+ const result = await command.execute(['config']);
279
+
280
+ expect(result.success).toBe(true);
281
+ expect(mockZeroRetention.getConfig).toHaveBeenCalled();
282
+ expect(result.output).toContain('ephemeralStorage');
283
+ });
284
+
285
+ it('execute config --set updates settings', async () => {
286
+ mockZeroRetention.isEnabled.mockReturnValue(true);
287
+ mockZeroRetention.enable.mockReturnValue({
288
+ success: true,
289
+ enabled: true,
290
+ config: {
291
+ enabled: true,
292
+ auditLogging: true,
293
+ },
294
+ });
295
+
296
+ const result = await command.execute(['config', '--set', 'auditLogging=true']);
297
+
298
+ expect(result.success).toBe(true);
299
+ expect(mockZeroRetention.enable).toHaveBeenCalledWith(
300
+ expect.objectContaining({ auditLogging: true })
301
+ );
302
+ });
303
+
304
+ it('handles invalid --set value', async () => {
305
+ const result = await command.execute(['config', '--set', 'invalidSyntax']);
306
+
307
+ expect(result.success).toBe(false);
308
+ expect(result.error).toContain('Invalid');
309
+ });
310
+ });
311
+
312
+ describe('error handling', () => {
313
+ it('shows help for unknown subcommand', async () => {
314
+ const result = await command.execute(['unknown']);
315
+
316
+ expect(result.success).toBe(false);
317
+ expect(result.error).toContain('Unknown');
318
+ });
319
+
320
+ it('shows help when no subcommand', async () => {
321
+ const result = await command.execute([]);
322
+
323
+ expect(result.success).toBe(true);
324
+ expect(result.output).toContain('Usage');
325
+ });
326
+
327
+ it('handles errors gracefully', async () => {
328
+ mockZeroRetention.enable.mockImplementation(() => {
329
+ throw new Error('Enable failed');
330
+ });
331
+
332
+ const result = await command.execute(['enable']);
333
+
334
+ expect(result.success).toBe(false);
335
+ expect(result.error).toContain('Enable failed');
336
+ });
337
+ });
338
+ });
339
+
340
+ describe('formatStatus', () => {
341
+ it('formatStatus returns readable output', () => {
342
+ const status = {
343
+ enabled: true,
344
+ subsystems: {
345
+ ephemeralStorage: true,
346
+ sessionPurge: true,
347
+ memoryExclusion: true,
348
+ },
349
+ config: {
350
+ retentionPolicy: { retention: 'immediate', persist: false },
351
+ },
352
+ validation: {
353
+ valid: true,
354
+ conflicts: [],
355
+ warnings: [],
356
+ },
357
+ };
358
+
359
+ const formatted = command.formatStatus(status);
360
+
361
+ expect(formatted).toContain('Zero-Retention Mode');
362
+ expect(formatted).toContain('ENABLED');
363
+ expect(formatted).toContain('ephemeralStorage');
364
+ });
365
+
366
+ it('formatStatus shows disabled state', () => {
367
+ const status = {
368
+ enabled: false,
369
+ subsystems: {},
370
+ config: {},
371
+ validation: { valid: true, conflicts: [], warnings: [] },
372
+ };
373
+
374
+ const formatted = command.formatStatus(status);
375
+
376
+ expect(formatted).toContain('DISABLED');
377
+ });
378
+
379
+ it('formatStatus shows conflicts', () => {
380
+ const status = {
381
+ enabled: true,
382
+ subsystems: {},
383
+ config: {},
384
+ validation: {
385
+ valid: false,
386
+ conflicts: ['Persistence enabled with zero-retention'],
387
+ warnings: [],
388
+ },
389
+ };
390
+
391
+ const formatted = command.formatStatus(status);
392
+
393
+ expect(formatted).toContain('Conflict');
394
+ expect(formatted).toContain('Persistence');
395
+ });
396
+
397
+ it('formatStatus shows warnings', () => {
398
+ const status = {
399
+ enabled: true,
400
+ subsystems: {},
401
+ config: {},
402
+ validation: {
403
+ valid: true,
404
+ conflicts: [],
405
+ warnings: ['Audit logging conflicts'],
406
+ },
407
+ };
408
+
409
+ const formatted = command.formatStatus(status);
410
+
411
+ expect(formatted).toContain('Warning');
412
+ });
413
+ });
414
+
415
+ describe('formatConfig', () => {
416
+ it('formatConfig returns readable config', () => {
417
+ const config = {
418
+ enabled: true,
419
+ ephemeralStorage: { encrypt: true, basePath: null },
420
+ sessionPurge: { aggressive: true },
421
+ memoryExclusion: { excludeAll: true },
422
+ retentionPolicy: { retention: 'immediate', persist: false },
423
+ };
424
+
425
+ const formatted = command.formatConfig(config);
426
+
427
+ expect(formatted).toContain('Configuration');
428
+ expect(formatted).toContain('ephemeralStorage');
429
+ expect(formatted).toContain('sessionPurge');
430
+ expect(formatted).toContain('memoryExclusion');
431
+ expect(formatted).toContain('retentionPolicy');
432
+ });
433
+
434
+ it('formatConfig shows disabled config', () => {
435
+ const config = {
436
+ enabled: false,
437
+ ephemeralStorage: null,
438
+ sessionPurge: null,
439
+ memoryExclusion: null,
440
+ retentionPolicy: null,
441
+ };
442
+
443
+ const formatted = command.formatConfig(config);
444
+
445
+ expect(formatted).toContain('not configured');
446
+ });
447
+ });
448
+ });