tlc-claude-code 1.2.29 → 1.4.0

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 (182) 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/UsagePane.d.ts +13 -0
  14. package/dashboard/dist/components/UsagePane.js +51 -0
  15. package/dashboard/dist/components/UsagePane.test.d.ts +1 -0
  16. package/dashboard/dist/components/UsagePane.test.js +142 -0
  17. package/dashboard/dist/components/WorkspaceDocsPane.d.ts +19 -0
  18. package/dashboard/dist/components/WorkspaceDocsPane.js +130 -0
  19. package/dashboard/dist/components/WorkspaceDocsPane.test.d.ts +1 -0
  20. package/dashboard/dist/components/WorkspaceDocsPane.test.js +242 -0
  21. package/dashboard/dist/components/WorkspacePane.d.ts +18 -0
  22. package/dashboard/dist/components/WorkspacePane.js +17 -0
  23. package/dashboard/dist/components/WorkspacePane.test.d.ts +1 -0
  24. package/dashboard/dist/components/WorkspacePane.test.js +84 -0
  25. package/dashboard/dist/components/ZeroRetentionPane.d.ts +44 -0
  26. package/dashboard/dist/components/ZeroRetentionPane.js +83 -0
  27. package/dashboard/dist/components/ZeroRetentionPane.test.d.ts +1 -0
  28. package/dashboard/dist/components/ZeroRetentionPane.test.js +160 -0
  29. package/package.json +1 -1
  30. package/server/lib/access-control-doc.js +541 -0
  31. package/server/lib/access-control-doc.test.js +672 -0
  32. package/server/lib/adr-generator.js +423 -0
  33. package/server/lib/adr-generator.test.js +586 -0
  34. package/server/lib/agent-progress-monitor.js +223 -0
  35. package/server/lib/agent-progress-monitor.test.js +202 -0
  36. package/server/lib/architecture-command.js +450 -0
  37. package/server/lib/architecture-command.test.js +754 -0
  38. package/server/lib/ast-analyzer.js +324 -0
  39. package/server/lib/ast-analyzer.test.js +437 -0
  40. package/server/lib/audit-attribution.js +191 -0
  41. package/server/lib/audit-attribution.test.js +359 -0
  42. package/server/lib/audit-classifier.js +202 -0
  43. package/server/lib/audit-classifier.test.js +209 -0
  44. package/server/lib/audit-command.js +275 -0
  45. package/server/lib/audit-command.test.js +325 -0
  46. package/server/lib/audit-exporter.js +380 -0
  47. package/server/lib/audit-exporter.test.js +464 -0
  48. package/server/lib/audit-logger.js +236 -0
  49. package/server/lib/audit-logger.test.js +364 -0
  50. package/server/lib/audit-query.js +257 -0
  51. package/server/lib/audit-query.test.js +352 -0
  52. package/server/lib/audit-storage.js +269 -0
  53. package/server/lib/audit-storage.test.js +272 -0
  54. package/server/lib/auth-system.test.js +4 -1
  55. package/server/lib/boundary-detector.js +427 -0
  56. package/server/lib/boundary-detector.test.js +320 -0
  57. package/server/lib/budget-alerts.js +138 -0
  58. package/server/lib/budget-alerts.test.js +235 -0
  59. package/server/lib/bulk-repo-init.js +342 -0
  60. package/server/lib/bulk-repo-init.test.js +388 -0
  61. package/server/lib/candidates-tracker.js +210 -0
  62. package/server/lib/candidates-tracker.test.js +300 -0
  63. package/server/lib/checkpoint-manager.js +251 -0
  64. package/server/lib/checkpoint-manager.test.js +474 -0
  65. package/server/lib/circular-detector.js +337 -0
  66. package/server/lib/circular-detector.test.js +353 -0
  67. package/server/lib/cohesion-analyzer.js +310 -0
  68. package/server/lib/cohesion-analyzer.test.js +447 -0
  69. package/server/lib/compliance-checklist.js +866 -0
  70. package/server/lib/compliance-checklist.test.js +476 -0
  71. package/server/lib/compliance-command.js +616 -0
  72. package/server/lib/compliance-command.test.js +551 -0
  73. package/server/lib/compliance-reporter.js +692 -0
  74. package/server/lib/compliance-reporter.test.js +707 -0
  75. package/server/lib/contract-testing.js +625 -0
  76. package/server/lib/contract-testing.test.js +342 -0
  77. package/server/lib/conversion-planner.js +469 -0
  78. package/server/lib/conversion-planner.test.js +361 -0
  79. package/server/lib/convert-command.js +351 -0
  80. package/server/lib/convert-command.test.js +608 -0
  81. package/server/lib/coupling-calculator.js +189 -0
  82. package/server/lib/coupling-calculator.test.js +509 -0
  83. package/server/lib/data-flow-doc.js +665 -0
  84. package/server/lib/data-flow-doc.test.js +659 -0
  85. package/server/lib/dependency-graph.js +367 -0
  86. package/server/lib/dependency-graph.test.js +516 -0
  87. package/server/lib/duplication-detector.js +349 -0
  88. package/server/lib/duplication-detector.test.js +401 -0
  89. package/server/lib/ephemeral-storage.js +249 -0
  90. package/server/lib/ephemeral-storage.test.js +254 -0
  91. package/server/lib/evidence-collector.js +627 -0
  92. package/server/lib/evidence-collector.test.js +901 -0
  93. package/server/lib/example-service.js +616 -0
  94. package/server/lib/example-service.test.js +397 -0
  95. package/server/lib/flow-diagram-generator.js +474 -0
  96. package/server/lib/flow-diagram-generator.test.js +446 -0
  97. package/server/lib/idp-manager.js +626 -0
  98. package/server/lib/idp-manager.test.js +587 -0
  99. package/server/lib/impact-scorer.js +184 -0
  100. package/server/lib/impact-scorer.test.js +211 -0
  101. package/server/lib/memory-exclusion.js +326 -0
  102. package/server/lib/memory-exclusion.test.js +241 -0
  103. package/server/lib/mermaid-generator.js +358 -0
  104. package/server/lib/mermaid-generator.test.js +301 -0
  105. package/server/lib/messaging-patterns.js +750 -0
  106. package/server/lib/messaging-patterns.test.js +213 -0
  107. package/server/lib/mfa-handler.js +452 -0
  108. package/server/lib/mfa-handler.test.js +490 -0
  109. package/server/lib/microservice-template.js +386 -0
  110. package/server/lib/microservice-template.test.js +325 -0
  111. package/server/lib/new-project-microservice.js +450 -0
  112. package/server/lib/new-project-microservice.test.js +600 -0
  113. package/server/lib/oauth-flow.js +375 -0
  114. package/server/lib/oauth-flow.test.js +487 -0
  115. package/server/lib/oauth-registry.js +190 -0
  116. package/server/lib/oauth-registry.test.js +306 -0
  117. package/server/lib/readme-generator.js +490 -0
  118. package/server/lib/readme-generator.test.js +493 -0
  119. package/server/lib/refactor-command.js +326 -0
  120. package/server/lib/refactor-command.test.js +528 -0
  121. package/server/lib/refactor-executor.js +254 -0
  122. package/server/lib/refactor-executor.test.js +305 -0
  123. package/server/lib/refactor-observer.js +292 -0
  124. package/server/lib/refactor-observer.test.js +422 -0
  125. package/server/lib/refactor-progress.js +193 -0
  126. package/server/lib/refactor-progress.test.js +251 -0
  127. package/server/lib/refactor-reporter.js +237 -0
  128. package/server/lib/refactor-reporter.test.js +247 -0
  129. package/server/lib/repo-dependency-tracker.js +261 -0
  130. package/server/lib/repo-dependency-tracker.test.js +350 -0
  131. package/server/lib/retention-policy.js +281 -0
  132. package/server/lib/retention-policy.test.js +486 -0
  133. package/server/lib/role-mapper.js +236 -0
  134. package/server/lib/role-mapper.test.js +395 -0
  135. package/server/lib/saml-provider.js +765 -0
  136. package/server/lib/saml-provider.test.js +643 -0
  137. package/server/lib/security-policy-generator.js +682 -0
  138. package/server/lib/security-policy-generator.test.js +544 -0
  139. package/server/lib/semantic-analyzer.js +198 -0
  140. package/server/lib/semantic-analyzer.test.js +474 -0
  141. package/server/lib/sensitive-detector.js +112 -0
  142. package/server/lib/sensitive-detector.test.js +209 -0
  143. package/server/lib/service-interaction-diagram.js +700 -0
  144. package/server/lib/service-interaction-diagram.test.js +638 -0
  145. package/server/lib/service-scaffold.js +486 -0
  146. package/server/lib/service-scaffold.test.js +373 -0
  147. package/server/lib/service-summary.js +553 -0
  148. package/server/lib/service-summary.test.js +619 -0
  149. package/server/lib/session-purge.js +460 -0
  150. package/server/lib/session-purge.test.js +312 -0
  151. package/server/lib/shared-kernel.js +578 -0
  152. package/server/lib/shared-kernel.test.js +255 -0
  153. package/server/lib/sso-command.js +544 -0
  154. package/server/lib/sso-command.test.js +552 -0
  155. package/server/lib/sso-session.js +492 -0
  156. package/server/lib/sso-session.test.js +670 -0
  157. package/server/lib/traefik-config.js +282 -0
  158. package/server/lib/traefik-config.test.js +312 -0
  159. package/server/lib/usage-command.js +218 -0
  160. package/server/lib/usage-command.test.js +391 -0
  161. package/server/lib/usage-formatter.js +192 -0
  162. package/server/lib/usage-formatter.test.js +267 -0
  163. package/server/lib/usage-history.js +122 -0
  164. package/server/lib/usage-history.test.js +206 -0
  165. package/server/lib/workspace-command.js +249 -0
  166. package/server/lib/workspace-command.test.js +264 -0
  167. package/server/lib/workspace-config.js +270 -0
  168. package/server/lib/workspace-config.test.js +312 -0
  169. package/server/lib/workspace-docs-command.js +547 -0
  170. package/server/lib/workspace-docs-command.test.js +692 -0
  171. package/server/lib/workspace-memory.js +451 -0
  172. package/server/lib/workspace-memory.test.js +403 -0
  173. package/server/lib/workspace-scanner.js +452 -0
  174. package/server/lib/workspace-scanner.test.js +677 -0
  175. package/server/lib/workspace-test-runner.js +315 -0
  176. package/server/lib/workspace-test-runner.test.js +294 -0
  177. package/server/lib/zero-retention-command.js +439 -0
  178. package/server/lib/zero-retention-command.test.js +448 -0
  179. package/server/lib/zero-retention.js +322 -0
  180. package/server/lib/zero-retention.test.js +258 -0
  181. package/server/package-lock.json +14 -0
  182. package/server/package.json +1 -0
@@ -0,0 +1,375 @@
1
+ /**
2
+ * OAuth 2.0 Flow Handler
3
+ *
4
+ * Handles OAuth 2.0 authorization code flow with PKCE support.
5
+ * Uses oauth-registry for provider configuration.
6
+ */
7
+
8
+ const crypto = require('crypto');
9
+
10
+ /**
11
+ * Default state expiry time (10 minutes).
12
+ */
13
+ const DEFAULT_STATE_EXPIRY_MS = 10 * 60 * 1000;
14
+
15
+ /**
16
+ * Generates PKCE code verifier and challenge.
17
+ *
18
+ * @returns {object} PKCE parameters
19
+ * @returns {string} returns.codeVerifier - Random 43-128 character string
20
+ * @returns {string} returns.codeChallenge - base64url(sha256(codeVerifier))
21
+ * @returns {string} returns.codeChallengeMethod - Always 'S256'
22
+ */
23
+ function generatePKCE() {
24
+ // Generate random bytes and encode as base64url (43-128 chars)
25
+ // 32 bytes = 43 base64 chars, 96 bytes = 128 base64 chars
26
+ const verifierBytes = crypto.randomBytes(32);
27
+ const codeVerifier = verifierBytes
28
+ .toString('base64')
29
+ .replace(/\+/g, '-')
30
+ .replace(/\//g, '_')
31
+ .replace(/=/g, '');
32
+
33
+ // SHA256 hash of verifier, then base64url encode
34
+ const hash = crypto.createHash('sha256').update(codeVerifier).digest();
35
+ const codeChallenge = hash
36
+ .toString('base64')
37
+ .replace(/\+/g, '-')
38
+ .replace(/\//g, '_')
39
+ .replace(/=/g, '');
40
+
41
+ return {
42
+ codeVerifier,
43
+ codeChallenge,
44
+ codeChallengeMethod: 'S256',
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Generates an OAuth state parameter with CSRF protection.
50
+ *
51
+ * State format (base64 encoded JSON):
52
+ * {
53
+ * nonce: 32-char hex string,
54
+ * provider: provider name,
55
+ * timestamp: epoch ms,
56
+ * codeVerifier: PKCE verifier (if usePKCE)
57
+ * }
58
+ *
59
+ * @param {string} provider - Provider name
60
+ * @param {object} [options] - Options
61
+ * @param {boolean} [options.usePKCE] - Include PKCE code verifier
62
+ * @returns {string} Base64 encoded state
63
+ */
64
+ function generateState(provider, options = {}) {
65
+ const stateData = {
66
+ nonce: crypto.randomBytes(16).toString('hex'),
67
+ provider,
68
+ timestamp: Date.now(),
69
+ };
70
+
71
+ if (options.usePKCE) {
72
+ const pkce = generatePKCE();
73
+ stateData.codeVerifier = pkce.codeVerifier;
74
+ }
75
+
76
+ return Buffer.from(JSON.stringify(stateData)).toString('base64');
77
+ }
78
+
79
+ /**
80
+ * Validates an OAuth state parameter.
81
+ *
82
+ * @param {string} state - Base64 encoded state from callback
83
+ * @param {string} expectedProvider - Expected provider name
84
+ * @param {object} [options] - Options
85
+ * @param {number} [options.maxAgeMs] - Maximum state age in ms (default 10 min)
86
+ * @returns {object} Validation result
87
+ * @returns {boolean} returns.valid - Whether state is valid
88
+ * @returns {string} [returns.error] - Error message if invalid
89
+ * @returns {string} [returns.provider] - Provider from state
90
+ * @returns {string} [returns.codeVerifier] - PKCE code verifier if present
91
+ */
92
+ function validateState(state, expectedProvider, options = {}) {
93
+ const maxAgeMs = options.maxAgeMs || DEFAULT_STATE_EXPIRY_MS;
94
+
95
+ try {
96
+ const decoded = JSON.parse(Buffer.from(state, 'base64').toString('utf-8'));
97
+
98
+ // Check provider matches
99
+ if (decoded.provider !== expectedProvider) {
100
+ return {
101
+ valid: false,
102
+ error: 'State provider mismatch - possible CSRF attack',
103
+ };
104
+ }
105
+
106
+ // Check expiry
107
+ const age = Date.now() - decoded.timestamp;
108
+ if (age > maxAgeMs) {
109
+ return {
110
+ valid: false,
111
+ error: 'State expired',
112
+ };
113
+ }
114
+
115
+ return {
116
+ valid: true,
117
+ provider: decoded.provider,
118
+ codeVerifier: decoded.codeVerifier,
119
+ };
120
+ } catch (err) {
121
+ return {
122
+ valid: false,
123
+ error: 'Invalid state format',
124
+ };
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Creates an OAuth flow handler.
130
+ *
131
+ * @param {object} registry - OAuth registry instance from createOAuthRegistry()
132
+ * @returns {object} OAuth flow handler
133
+ */
134
+ function createOAuthFlow(registry) {
135
+ /**
136
+ * Generates an authorization URL for the OAuth flow.
137
+ *
138
+ * @param {string} providerName - Provider name
139
+ * @param {object} options - Options
140
+ * @param {string} options.redirectUri - Callback URL
141
+ * @param {string[]} [options.scopes] - Override default scopes
142
+ * @param {boolean} [options.usePKCE] - Use PKCE extension
143
+ * @returns {object} Authorization data
144
+ * @returns {string} returns.url - Authorization URL
145
+ * @returns {string} returns.state - State parameter for validation
146
+ * @returns {string} [returns.codeVerifier] - PKCE code verifier (if usePKCE)
147
+ */
148
+ function getAuthorizationUrl(providerName, options = {}) {
149
+ const provider = registry.getProvider(providerName);
150
+ if (!provider) {
151
+ throw new Error(`Provider not found: ${providerName}`);
152
+ }
153
+
154
+ if (!options.redirectUri) {
155
+ throw new Error('redirectUri is required');
156
+ }
157
+
158
+ // Generate state (with PKCE if requested)
159
+ const state = generateState(providerName, { usePKCE: options.usePKCE });
160
+
161
+ // Build URL params
162
+ const params = new URLSearchParams({
163
+ client_id: provider.clientId,
164
+ redirect_uri: options.redirectUri,
165
+ response_type: 'code',
166
+ state,
167
+ });
168
+
169
+ // Add scopes
170
+ const scopes = options.scopes || provider.scopes || [];
171
+ if (scopes.length > 0) {
172
+ params.set('scope', scopes.join(' '));
173
+ }
174
+
175
+ // Add PKCE if requested
176
+ let codeVerifier;
177
+ if (options.usePKCE) {
178
+ const stateData = JSON.parse(Buffer.from(state, 'base64').toString('utf-8'));
179
+ codeVerifier = stateData.codeVerifier;
180
+
181
+ // Generate challenge from verifier
182
+ const hash = crypto.createHash('sha256').update(codeVerifier).digest();
183
+ const codeChallenge = hash
184
+ .toString('base64')
185
+ .replace(/\+/g, '-')
186
+ .replace(/\//g, '_')
187
+ .replace(/=/g, '');
188
+
189
+ params.set('code_challenge', codeChallenge);
190
+ params.set('code_challenge_method', 'S256');
191
+ }
192
+
193
+ const url = `${provider.authUrl}?${params.toString()}`;
194
+
195
+ const result = { url, state };
196
+ if (codeVerifier) {
197
+ result.codeVerifier = codeVerifier;
198
+ }
199
+
200
+ return result;
201
+ }
202
+
203
+ /**
204
+ * Exchanges an authorization code for tokens.
205
+ *
206
+ * @param {string} providerName - Provider name
207
+ * @param {object} options - Options
208
+ * @param {string} options.code - Authorization code
209
+ * @param {string} options.redirectUri - Redirect URI (must match authorize request)
210
+ * @param {string} [options.codeVerifier] - PKCE code verifier
211
+ * @returns {Promise<object>} Token response
212
+ */
213
+ async function exchangeCode(providerName, options) {
214
+ const provider = registry.getProvider(providerName);
215
+ if (!provider) {
216
+ throw new Error(`Provider not found: ${providerName}`);
217
+ }
218
+
219
+ const body = new URLSearchParams({
220
+ grant_type: 'authorization_code',
221
+ code: options.code,
222
+ client_id: provider.clientId,
223
+ client_secret: provider.clientSecret,
224
+ redirect_uri: options.redirectUri,
225
+ });
226
+
227
+ if (options.codeVerifier) {
228
+ body.set('code_verifier', options.codeVerifier);
229
+ }
230
+
231
+ let response;
232
+ try {
233
+ response = await fetch(provider.tokenUrl, {
234
+ method: 'POST',
235
+ headers: {
236
+ 'Content-Type': 'application/x-www-form-urlencoded',
237
+ Accept: 'application/json',
238
+ },
239
+ body: body.toString(),
240
+ });
241
+ } catch (err) {
242
+ throw new Error(`Network error: ${err.message}`);
243
+ }
244
+
245
+ const data = await response.json();
246
+
247
+ if (!response.ok) {
248
+ throw new Error(`${data.error}: ${data.error_description || 'Token exchange failed'}`);
249
+ }
250
+
251
+ return {
252
+ accessToken: data.access_token,
253
+ refreshToken: data.refresh_token,
254
+ tokenType: data.token_type,
255
+ expiresIn: data.expires_in,
256
+ scope: data.scope,
257
+ };
258
+ }
259
+
260
+ /**
261
+ * Refreshes an access token using a refresh token.
262
+ *
263
+ * @param {string} providerName - Provider name
264
+ * @param {object} options - Options
265
+ * @param {string} options.refreshToken - Refresh token
266
+ * @returns {Promise<object>} New token response
267
+ */
268
+ async function refreshToken(providerName, options) {
269
+ const provider = registry.getProvider(providerName);
270
+ if (!provider) {
271
+ throw new Error(`Provider not found: ${providerName}`);
272
+ }
273
+
274
+ const body = new URLSearchParams({
275
+ grant_type: 'refresh_token',
276
+ refresh_token: options.refreshToken,
277
+ client_id: provider.clientId,
278
+ client_secret: provider.clientSecret,
279
+ });
280
+
281
+ let response;
282
+ try {
283
+ response = await fetch(provider.tokenUrl, {
284
+ method: 'POST',
285
+ headers: {
286
+ 'Content-Type': 'application/x-www-form-urlencoded',
287
+ Accept: 'application/json',
288
+ },
289
+ body: body.toString(),
290
+ });
291
+ } catch (err) {
292
+ throw new Error(`Network error: ${err.message}`);
293
+ }
294
+
295
+ const data = await response.json();
296
+
297
+ if (!response.ok) {
298
+ throw new Error(`${data.error}: ${data.error_description || 'Token refresh failed'}`);
299
+ }
300
+
301
+ return {
302
+ accessToken: data.access_token,
303
+ refreshToken: data.refresh_token,
304
+ tokenType: data.token_type,
305
+ expiresIn: data.expires_in,
306
+ scope: data.scope,
307
+ };
308
+ }
309
+
310
+ /**
311
+ * Handles an OAuth callback request.
312
+ *
313
+ * @param {string} providerName - Expected provider name
314
+ * @param {object} params - Callback parameters
315
+ * @param {string} [params.code] - Authorization code (if successful)
316
+ * @param {string} params.state - State parameter
317
+ * @param {string} params.redirectUri - Redirect URI for token exchange
318
+ * @param {string} [params.error] - Error code (if failed)
319
+ * @param {string} [params.error_description] - Error description
320
+ * @returns {Promise<object>} Callback result
321
+ */
322
+ async function handleCallback(providerName, params) {
323
+ // Check for OAuth error in callback
324
+ if (params.error) {
325
+ return {
326
+ success: false,
327
+ error: params.error,
328
+ errorDescription: params.error_description,
329
+ };
330
+ }
331
+
332
+ // Validate state
333
+ const stateResult = validateState(params.state, providerName);
334
+ if (!stateResult.valid) {
335
+ return {
336
+ success: false,
337
+ error: stateResult.error,
338
+ };
339
+ }
340
+
341
+ // Exchange code for tokens
342
+ try {
343
+ const tokens = await exchangeCode(providerName, {
344
+ code: params.code,
345
+ redirectUri: params.redirectUri,
346
+ codeVerifier: stateResult.codeVerifier,
347
+ });
348
+
349
+ return {
350
+ success: true,
351
+ provider: providerName,
352
+ tokens,
353
+ };
354
+ } catch (err) {
355
+ return {
356
+ success: false,
357
+ error: err.message,
358
+ };
359
+ }
360
+ }
361
+
362
+ return {
363
+ getAuthorizationUrl,
364
+ exchangeCode,
365
+ refreshToken,
366
+ handleCallback,
367
+ };
368
+ }
369
+
370
+ module.exports = {
371
+ createOAuthFlow,
372
+ generateState,
373
+ validateState,
374
+ generatePKCE,
375
+ };