@equilateral_ai/mindmeld 3.5.2 → 4.0.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 (140) hide show
  1. package/hooks/session-end.js +25 -0
  2. package/hooks/session-start.js +363 -83
  3. package/hooks/session-watcher.js +585 -0
  4. package/package.json +19 -13
  5. package/scripts/init-project.js +9 -23
  6. package/src/client/dbShim.js +16 -0
  7. package/src/core/AuthManager.js +3 -2
  8. package/src/handlers/helpers/dbOperations.js +9 -46
  9. package/src/index.js +2 -217
  10. package/src/utils/piiMask.js +16 -0
  11. package/scripts/harvest.js +0 -601
  12. package/scripts/inject.js +0 -409
  13. package/scripts/mcp-bridge.js +0 -220
  14. package/scripts/repo-analyzer.js +0 -870
  15. package/src/collaboration/CollaborationPrompt.js +0 -460
  16. package/src/core/AlertEngine.js +0 -813
  17. package/src/core/AlertNotifier.js +0 -363
  18. package/src/core/CorrelationAnalyzer.js +0 -931
  19. package/src/core/CrossReferenceEngine.js +0 -624
  20. package/src/core/CurationEngine.js +0 -688
  21. package/src/core/DeprecationScheduler.js +0 -183
  22. package/src/core/LoadBearingDetector.js +0 -242
  23. package/src/core/NotificationService.js +0 -1032
  24. package/src/core/RapportOrchestrator.js +0 -632
  25. package/src/core/RelevanceDetector.js +0 -694
  26. package/src/core/StandardLifecycle.js +0 -244
  27. package/src/core/StandardsIngestion.js +0 -991
  28. package/src/core/TeamLoadBearingDetector.js +0 -431
  29. package/src/core/parsers/adrParser.js +0 -479
  30. package/src/core/parsers/cursorRulesParser.js +0 -564
  31. package/src/core/parsers/eslintParser.js +0 -439
  32. package/src/database/dbOperations.js +0 -105
  33. package/src/handlers/activity/activityGetMe.js +0 -98
  34. package/src/handlers/activity/activityGetTeam.js +0 -175
  35. package/src/handlers/admin/adminSetup.js +0 -216
  36. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  37. package/src/handlers/alerts/alertsGet.js +0 -250
  38. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  39. package/src/handlers/analytics/coachingGet.js +0 -361
  40. package/src/handlers/analytics/convergenceGet.js +0 -236
  41. package/src/handlers/analytics/developerScoreGet.js +0 -137
  42. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  43. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  44. package/src/handlers/collaborators/collaboratorList.js +0 -82
  45. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  46. package/src/handlers/collaborators/inviteAccept.js +0 -122
  47. package/src/handlers/company/companyUsersDelete.js +0 -141
  48. package/src/handlers/company/companyUsersGet.js +0 -90
  49. package/src/handlers/company/companyUsersPost.js +0 -267
  50. package/src/handlers/company/companyUsersPut.js +0 -76
  51. package/src/handlers/context/contextGet.js +0 -57
  52. package/src/handlers/context/invariantsGet.js +0 -74
  53. package/src/handlers/context/loopsGet.js +0 -82
  54. package/src/handlers/context/notesCreate.js +0 -74
  55. package/src/handlers/context/purposeGet.js +0 -78
  56. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  57. package/src/handlers/correlations/correlationsGet.js +0 -93
  58. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  59. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  60. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  61. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  62. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  63. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  64. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  65. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  66. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  67. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  68. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  69. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  70. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  71. package/src/handlers/github/githubConnectionStatus.js +0 -49
  72. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  73. package/src/handlers/github/githubOAuthCallback.js +0 -178
  74. package/src/handlers/github/githubOAuthStart.js +0 -59
  75. package/src/handlers/github/githubPatternsReview.js +0 -76
  76. package/src/handlers/github/githubReposList.js +0 -105
  77. package/src/handlers/health/healthGet.js +0 -55
  78. package/src/handlers/helpers/auditLogger.js +0 -201
  79. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  80. package/src/handlers/helpers/decisionFrames.js +0 -29
  81. package/src/handlers/helpers/errorHandler.js +0 -49
  82. package/src/handlers/helpers/index.js +0 -138
  83. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  84. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  85. package/src/handlers/helpers/predictiveCache.js +0 -51
  86. package/src/handlers/helpers/projectAccess.js +0 -88
  87. package/src/handlers/helpers/responseUtil.js +0 -55
  88. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  89. package/src/handlers/mcp/mcpHandler.js +0 -569
  90. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  91. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  92. package/src/handlers/notifications/getPreferences.js +0 -84
  93. package/src/handlers/notifications/sendNotification.js +0 -170
  94. package/src/handlers/notifications/updatePreferences.js +0 -316
  95. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  96. package/src/handlers/patterns/patternUsagePost.js +0 -182
  97. package/src/handlers/patterns/patternViolationPost.js +0 -185
  98. package/src/handlers/projects/projectCreate.js +0 -248
  99. package/src/handlers/projects/projectDelete.js +0 -82
  100. package/src/handlers/projects/projectGet.js +0 -95
  101. package/src/handlers/projects/projectUpdate.js +0 -117
  102. package/src/handlers/reports/aiLeverage.js +0 -210
  103. package/src/handlers/reports/engineeringInvestment.js +0 -132
  104. package/src/handlers/reports/riskForecast.js +0 -206
  105. package/src/handlers/reports/standardsRoi.js +0 -254
  106. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  107. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  108. package/src/handlers/scheduled/generateAlerts.js +0 -135
  109. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  110. package/src/handlers/scheduled/refreshActivity.js +0 -21
  111. package/src/handlers/scheduled/scanCompliance.js +0 -334
  112. package/src/handlers/sessions/sessionEndPost.js +0 -180
  113. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  114. package/src/handlers/standards/catalogGet.js +0 -185
  115. package/src/handlers/standards/catalogSync.js +0 -120
  116. package/src/handlers/standards/discoveriesGet.js +0 -89
  117. package/src/handlers/standards/projectStandardsGet.js +0 -129
  118. package/src/handlers/standards/projectStandardsPut.js +0 -151
  119. package/src/handlers/standards/standardsAuditGet.js +0 -65
  120. package/src/handlers/standards/standardsParseUpload.js +0 -149
  121. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  122. package/src/handlers/standards/standardsTransition.js +0 -161
  123. package/src/handlers/stripe/addonManagePost.js +0 -240
  124. package/src/handlers/stripe/billingPortalPost.js +0 -93
  125. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  126. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  127. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  128. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  129. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  130. package/src/handlers/stripe/webhookPost.js +0 -482
  131. package/src/handlers/user/apiTokenCreate.js +0 -71
  132. package/src/handlers/user/apiTokenList.js +0 -64
  133. package/src/handlers/user/userSplashAck.js +0 -91
  134. package/src/handlers/user/userSplashGet.js +0 -211
  135. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  136. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  137. package/src/handlers/users/userEntitlementsGet.js +0 -89
  138. package/src/handlers/users/userGet.js +0 -118
  139. package/src/handlers/users/userProfilePut.js +0 -77
  140. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -1,224 +0,0 @@
1
- /**
2
- * Control Tower Handler
3
- * Unified org health dashboard for enterprise tier
4
- *
5
- * GET /api/enterprise/control-tower
6
- * Auth: Cognito JWT required, Enterprise tier
7
- *
8
- * Aggregates cross-project health metrics:
9
- * - Organization health score
10
- * - Standards compliance across projects
11
- * - Risk indicators (stale devs, violations, coverage gaps)
12
- * - Governance activity (audit events, knowledge items)
13
- * - Project-level breakdown
14
- */
15
-
16
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
17
-
18
- async function getControlTower({ requestContext, queryStringParameters }) {
19
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
20
-
21
- if (!email) {
22
- return createErrorResponse(401, 'Authentication required');
23
- }
24
-
25
- // Verify enterprise admin/manager access
26
- const accessCheck = await executeQuery(`
27
- SELECT ue.company_id, ue.admin, ue.manager, c.subscription_tier, co.company_name
28
- FROM rapport.user_entitlements ue
29
- JOIN rapport.clients c ON ue.client_id = c.client_id
30
- JOIN rapport.companies co ON ue.company_id = co.company_id
31
- WHERE ue.email_address = $1
32
- AND (ue.admin = true OR ue.manager = true)
33
- LIMIT 1
34
- `, [email]);
35
-
36
- if (accessCheck.rows.length === 0) {
37
- return createErrorResponse(403, 'Admin or Manager access required for Control Tower');
38
- }
39
-
40
- const { company_id: companyId, company_name: companyName } = accessCheck.rows[0];
41
- const periodDays = 30;
42
- const periodStart = new Date();
43
- periodStart.setDate(periodStart.getDate() - periodDays);
44
-
45
- // 1. Team health metrics
46
- let teamHealth = { total: 0, active: 0, stale: 0, very_stale: 0, no_sessions: 0 };
47
- try {
48
- const teamResult = await executeQuery(`
49
- SELECT
50
- COUNT(DISTINCT ue.email_address) as total,
51
- COUNT(DISTINCT CASE WHEN mda.sessions_last_30d > 0 AND mda.commits_last_30d > 0 THEN ue.email_address END) as active,
52
- COUNT(DISTINCT CASE WHEN mda.days_since_last_commit BETWEEN 8 AND 14 THEN ue.email_address END) as stale,
53
- COUNT(DISTINCT CASE WHEN mda.days_since_last_commit > 14 THEN ue.email_address END) as very_stale,
54
- COUNT(DISTINCT CASE WHEN mda.sessions_last_30d = 0 OR mda.sessions_last_30d IS NULL THEN ue.email_address END) as no_sessions
55
- FROM rapport.user_entitlements ue
56
- LEFT JOIN rapport.mv_developer_activity mda ON ue.email_address = mda.email_address
57
- WHERE ue.company_id = $1
58
- `, [companyId]);
59
- if (teamResult.rows[0]) {
60
- teamHealth = {
61
- total: parseInt(teamResult.rows[0].total) || 0,
62
- active: parseInt(teamResult.rows[0].active) || 0,
63
- stale: parseInt(teamResult.rows[0].stale) || 0,
64
- very_stale: parseInt(teamResult.rows[0].very_stale) || 0,
65
- no_sessions: parseInt(teamResult.rows[0].no_sessions) || 0
66
- };
67
- }
68
- } catch (e) {
69
- console.log('[ControlTower] Team health query failed:', e.message);
70
- }
71
-
72
- // 2. Project coverage
73
- let projects = [];
74
- try {
75
- const projectResult = await executeQuery(`
76
- SELECT
77
- p.project_id,
78
- p.project_name,
79
- p.repo_url,
80
- COUNT(DISTINCT s.session_id) as sessions_30d,
81
- COUNT(DISTINCT ss.standard_id) as standards_used,
82
- MAX(s.started_at) as last_session
83
- FROM rapport.projects p
84
- LEFT JOIN rapport.sessions s ON p.project_id = s.project_id AND s.started_at >= $2
85
- LEFT JOIN rapport.session_standards ss ON s.session_id = ss.session_id AND ss.created_at >= $2
86
- WHERE p.company_id = $1
87
- GROUP BY p.project_id, p.project_name, p.repo_url
88
- ORDER BY sessions_30d DESC
89
- `, [companyId, periodStart]);
90
- projects = projectResult.rows.map(row => ({
91
- project_id: row.project_id,
92
- project_name: row.project_name,
93
- repo_connected: !!row.repo_url,
94
- sessions_30d: parseInt(row.sessions_30d) || 0,
95
- standards_used: parseInt(row.standards_used) || 0,
96
- last_session: row.last_session,
97
- status: parseInt(row.sessions_30d) > 0 ? 'active' : 'inactive'
98
- }));
99
- } catch (e) {
100
- console.log('[ControlTower] Projects query failed:', e.message);
101
- }
102
-
103
- // 3. Standards coverage (how many unique standards used across org)
104
- let standardsCoverage = { total_injections: 0, unique_standards: 0, sessions_with_standards: 0 };
105
- try {
106
- const stdResult = await executeQuery(`
107
- SELECT
108
- COUNT(*) as total_injections,
109
- COUNT(DISTINCT ss.standard_id) as unique_standards,
110
- COUNT(DISTINCT ss.session_id) as sessions_with_standards
111
- FROM rapport.session_standards ss
112
- JOIN rapport.sessions s ON ss.session_id = s.session_id
113
- JOIN rapport.projects p ON s.project_id = p.project_id
114
- WHERE p.company_id = $1
115
- AND ss.created_at >= $2
116
- `, [companyId, periodStart]);
117
- if (stdResult.rows[0]) {
118
- standardsCoverage = {
119
- total_injections: parseInt(stdResult.rows[0].total_injections) || 0,
120
- unique_standards: parseInt(stdResult.rows[0].unique_standards) || 0,
121
- sessions_with_standards: parseInt(stdResult.rows[0].sessions_with_standards) || 0
122
- };
123
- }
124
- } catch (e) {
125
- console.log('[ControlTower] Standards coverage query failed:', e.message);
126
- }
127
-
128
- // 4. Governance activity (audit events, knowledge items)
129
- let governance = { audit_events_30d: 0, knowledge_items: 0, published_knowledge: 0 };
130
- try {
131
- const auditResult = await executeQuery(`
132
- SELECT COUNT(*) as count
133
- FROM rapport.unified_audit_log
134
- WHERE client_id = (
135
- SELECT client_id FROM rapport.user_entitlements WHERE company_id = $1 LIMIT 1
136
- )
137
- AND created_at >= $2
138
- `, [companyId, periodStart]);
139
- governance.audit_events_30d = parseInt(auditResult.rows[0]?.count) || 0;
140
- } catch (e) {
141
- console.log('[ControlTower] Audit events query failed:', e.message);
142
- }
143
-
144
- try {
145
- const knowledgeResult = await executeQuery(`
146
- SELECT
147
- COUNT(*) as total,
148
- COUNT(CASE WHEN status = 'published' THEN 1 END) as published
149
- FROM rapport.curated_knowledge
150
- WHERE client_id = (
151
- SELECT client_id FROM rapport.user_entitlements WHERE company_id = $1 LIMIT 1
152
- )
153
- `, [companyId]);
154
- governance.knowledge_items = parseInt(knowledgeResult.rows[0]?.total) || 0;
155
- governance.published_knowledge = parseInt(knowledgeResult.rows[0]?.published) || 0;
156
- } catch (e) {
157
- console.log('[ControlTower] Knowledge query failed:', e.message);
158
- }
159
-
160
- // 5. Risk indicators
161
- const risks = [];
162
- if (teamHealth.very_stale > 0) {
163
- risks.push({
164
- severity: 'high',
165
- category: 'team',
166
- message: `${teamHealth.very_stale} developer${teamHealth.very_stale > 1 ? 's' : ''} inactive for 14+ days`
167
- });
168
- }
169
- if (teamHealth.no_sessions > teamHealth.total * 0.3 && teamHealth.total > 1) {
170
- risks.push({
171
- severity: 'medium',
172
- category: 'adoption',
173
- message: `${teamHealth.no_sessions} of ${teamHealth.total} developers have no AI sessions in 30 days`
174
- });
175
- }
176
- const projectsWithoutStandards = projects.filter(p => p.sessions_30d > 0 && p.standards_used === 0);
177
- if (projectsWithoutStandards.length > 0) {
178
- risks.push({
179
- severity: 'medium',
180
- category: 'coverage',
181
- message: `${projectsWithoutStandards.length} active project${projectsWithoutStandards.length > 1 ? 's' : ''} with no standards injection`
182
- });
183
- }
184
- const inactiveProjects = projects.filter(p => p.status === 'inactive');
185
- if (inactiveProjects.length > projects.length * 0.5 && projects.length > 1) {
186
- risks.push({
187
- severity: 'low',
188
- category: 'projects',
189
- message: `${inactiveProjects.length} of ${projects.length} projects had no sessions in 30 days`
190
- });
191
- }
192
-
193
- // 6. Calculate org health score (0-100)
194
- let healthScore = 50; // baseline
195
- if (teamHealth.total > 0) {
196
- const activeRate = teamHealth.active / teamHealth.total;
197
- healthScore = Math.round(activeRate * 40); // up to 40 points for team activity
198
- }
199
- if (projects.length > 0) {
200
- const coveredProjects = projects.filter(p => p.sessions_30d > 0).length;
201
- healthScore += Math.round((coveredProjects / projects.length) * 30); // up to 30 for project coverage
202
- }
203
- if (standardsCoverage.unique_standards > 0) {
204
- healthScore += Math.min(standardsCoverage.unique_standards * 2, 20); // up to 20 for standards breadth
205
- }
206
- if (governance.published_knowledge > 0) {
207
- healthScore += Math.min(governance.published_knowledge, 10); // up to 10 for knowledge curation
208
- }
209
- healthScore = Math.min(healthScore, 100);
210
-
211
- return createSuccessResponse({
212
- company_name: companyName,
213
- health_score: healthScore,
214
- team: teamHealth,
215
- projects,
216
- standards_coverage: standardsCoverage,
217
- governance,
218
- risks,
219
- period_days: periodDays,
220
- period_start: periodStart.toISOString()
221
- }, 'Control Tower data retrieved');
222
- }
223
-
224
- exports.handler = wrapHandler(getControlTower);
@@ -1,108 +0,0 @@
1
- /**
2
- * Enterprise Audit Log Get Handler
3
- *
4
- * GET /api/enterprise/audit?page=&pageSize=&action=&resourceType=&startDate=&endDate=
5
- * Returns: { Records: AuditLogEntry[] } + meta { Total_Records, Page, Page_Size, Request_ID, Timestamp }
6
- * Auth: Cognito JWT required, enterprise tier only
7
- */
8
-
9
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
10
-
11
- async function getEnterpriseAudit({ queryStringParameters, requestContext }) {
12
- try {
13
- const Request_ID = requestContext.requestId;
14
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
15
- if (!email) {
16
- return createErrorResponse(401, 'Authentication required');
17
- }
18
-
19
- // Look up client_id and verify enterprise tier
20
- const userResult = await executeQuery(
21
- `SELECT u.client_id, c.subscription_tier
22
- FROM rapport.users u
23
- JOIN rapport.clients c ON c.client_id = u.client_id
24
- WHERE u.email_address = $1 LIMIT 1`,
25
- [email]
26
- );
27
- if (!userResult.rows.length) {
28
- return createErrorResponse(403, 'User not found');
29
- }
30
- if (userResult.rows[0].subscription_tier !== 'enterprise') {
31
- return createErrorResponse(403, 'Enterprise subscription required');
32
- }
33
- const clientId = userResult.rows[0].client_id;
34
-
35
- const params = queryStringParameters || {};
36
- const page = Math.max(1, parseInt(params.page) || 1);
37
- const pageSize = Math.min(100, Math.max(1, parseInt(params.pageSize) || 20));
38
- const offset = (page - 1) * pageSize;
39
-
40
- // Build WHERE clause
41
- const conditions = ['client_id = $1'];
42
- const values = [clientId];
43
- let paramIndex = 2;
44
-
45
- if (params.action) {
46
- conditions.push(`event_type = $${paramIndex}`);
47
- values.push(params.action);
48
- paramIndex++;
49
- }
50
-
51
- if (params.resourceType) {
52
- conditions.push(`entity_type = $${paramIndex}`);
53
- values.push(params.resourceType);
54
- paramIndex++;
55
- }
56
-
57
- if (params.startDate) {
58
- conditions.push(`created_at >= $${paramIndex}`);
59
- values.push(params.startDate);
60
- paramIndex++;
61
- }
62
-
63
- if (params.endDate) {
64
- conditions.push(`created_at <= $${paramIndex}`);
65
- values.push(params.endDate);
66
- paramIndex++;
67
- }
68
-
69
- const whereClause = conditions.join(' AND ');
70
-
71
- // Get total count
72
- const countResult = await executeQuery(
73
- `SELECT COUNT(*) as total FROM rapport.unified_audit_log WHERE ${whereClause}`,
74
- values
75
- );
76
- const total = parseInt(countResult.rows[0].total);
77
-
78
- // Get paginated records, mapping columns to frontend AuditLogEntry type
79
- const dataResult = await executeQuery(
80
- `SELECT
81
- audit_id,
82
- client_id,
83
- actor_email,
84
- event_type as action,
85
- entity_type as resource_type,
86
- entity_id as resource_id,
87
- before_state as old_value,
88
- after_state as new_value,
89
- created_at
90
- FROM rapport.unified_audit_log
91
- WHERE ${whereClause}
92
- ORDER BY created_at DESC
93
- LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
94
- [...values, pageSize, offset]
95
- );
96
-
97
- return createSuccessResponse(
98
- { Records: dataResult.rows },
99
- 'Audit log retrieved',
100
- { Total_Records: total, Page: page, Page_Size: pageSize, Request_ID, Timestamp: new Date().toISOString() }
101
- );
102
-
103
- } catch (error) {
104
- return handleError(error);
105
- }
106
- }
107
-
108
- exports.handler = wrapHandler(getEnterpriseAudit);
@@ -1,85 +0,0 @@
1
- /**
2
- * Enterprise Contributors Get Handler
3
- *
4
- * GET /api/enterprise/contributors?period=
5
- * Returns: { Records: [contributor stats] } + meta
6
- * Auth: Cognito JWT required, enterprise tier only
7
- */
8
-
9
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
10
-
11
- async function getEnterpriseContributors({ queryStringParameters, requestContext }) {
12
- try {
13
- const Request_ID = requestContext.requestId;
14
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
15
- if (!email) {
16
- return createErrorResponse(401, 'Authentication required');
17
- }
18
-
19
- const userResult = await executeQuery(
20
- `SELECT u.client_id, c.subscription_tier
21
- FROM rapport.users u
22
- JOIN rapport.clients c ON c.client_id = u.client_id
23
- WHERE u.email_address = $1 LIMIT 1`,
24
- [email]
25
- );
26
- if (!userResult.rows.length) {
27
- return createErrorResponse(403, 'User not found');
28
- }
29
- if (userResult.rows[0].subscription_tier !== 'enterprise') {
30
- return createErrorResponse(403, 'Enterprise subscription required');
31
- }
32
- const clientId = userResult.rows[0].client_id;
33
-
34
- const params = queryStringParameters || {};
35
- const period = params.period || '30d';
36
-
37
- // Calculate date filter
38
- let intervalDays;
39
- switch (period) {
40
- case '7d': intervalDays = 7; break;
41
- case '90d': intervalDays = 90; break;
42
- case '30d':
43
- default: intervalDays = 30; break;
44
- }
45
-
46
- // Aggregate contributions across audit trail and knowledge curation
47
- const result = await executeQuery(
48
- `WITH audit_activity AS (
49
- SELECT
50
- actor_email as contributor_email,
51
- COUNT(*) FILTER (WHERE event_type = 'DATA_CREATE' AND entity_type = 'STANDARD') as discoveries,
52
- COUNT(*) FILTER (WHERE event_type = 'DATA_UPDATE' AND entity_type = 'STANDARD') as refinements,
53
- COUNT(*) FILTER (WHERE event_type = 'DATA_UPDATE' AND entity_type = 'STANDARD'
54
- AND (after_state->>'status') = 'approved') as verifications,
55
- COUNT(*) FILTER (WHERE event_type = 'DATA_CREATE' AND entity_type = 'KNOWLEDGE') as curations
56
- FROM rapport.unified_audit_log
57
- WHERE client_id = $1
58
- AND created_at >= NOW() - ($2 || ' days')::interval
59
- AND actor_email IS NOT NULL
60
- GROUP BY actor_email
61
- )
62
- SELECT
63
- contributor_email,
64
- COALESCE(discoveries, 0) as discoveries,
65
- COALESCE(refinements, 0) as refinements,
66
- COALESCE(verifications, 0) as verifications,
67
- COALESCE(curations, 0) as curations,
68
- COALESCE(discoveries * 3 + refinements * 2 + verifications * 2 + curations * 4, 0) as total_weight
69
- FROM audit_activity
70
- ORDER BY total_weight DESC`,
71
- [clientId, String(intervalDays)]
72
- );
73
-
74
- return createSuccessResponse(
75
- { Records: result.rows },
76
- 'Contributors retrieved',
77
- { Total_Records: result.rowCount, Request_ID, Timestamp: new Date().toISOString() }
78
- );
79
-
80
- } catch (error) {
81
- return handleError(error);
82
- }
83
- }
84
-
85
- exports.handler = wrapHandler(getEnterpriseContributors);
@@ -1,53 +0,0 @@
1
- /**
2
- * Enterprise Knowledge Categories Get Handler
3
- *
4
- * GET /api/enterprise/knowledge/categories
5
- * Returns: { Records: [{ category_id, name, description }] } + meta
6
- * Auth: Cognito JWT required, enterprise tier only
7
- */
8
-
9
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
10
-
11
- async function getEnterpriseKnowledgeCategories({ requestContext }) {
12
- try {
13
- const Request_ID = requestContext.requestId;
14
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
15
- if (!email) {
16
- return createErrorResponse(401, 'Authentication required');
17
- }
18
-
19
- const userResult = await executeQuery(
20
- `SELECT u.client_id, c.subscription_tier
21
- FROM rapport.users u
22
- JOIN rapport.clients c ON c.client_id = u.client_id
23
- WHERE u.email_address = $1 LIMIT 1`,
24
- [email]
25
- );
26
- if (!userResult.rows.length) {
27
- return createErrorResponse(403, 'User not found');
28
- }
29
- if (userResult.rows[0].subscription_tier !== 'enterprise') {
30
- return createErrorResponse(403, 'Enterprise subscription required');
31
- }
32
- const clientId = userResult.rows[0].client_id;
33
-
34
- const result = await executeQuery(
35
- `SELECT category_id, name, description
36
- FROM rapport.knowledge_categories
37
- WHERE client_id = $1
38
- ORDER BY name`,
39
- [clientId]
40
- );
41
-
42
- return createSuccessResponse(
43
- { Records: result.rows },
44
- 'Categories retrieved',
45
- { Total_Records: result.rowCount, Request_ID, Timestamp: new Date().toISOString() }
46
- );
47
-
48
- } catch (error) {
49
- return handleError(error);
50
- }
51
- }
52
-
53
- exports.handler = wrapHandler(getEnterpriseKnowledgeCategories);
@@ -1,77 +0,0 @@
1
- /**
2
- * Enterprise Knowledge Create Handler
3
- *
4
- * POST /api/enterprise/knowledge
5
- * Body: { title, content, category?, tags?, invariant_id? }
6
- * Returns: { Records: [CuratedKnowledge] } + meta
7
- * Auth: Cognito JWT required, enterprise tier only
8
- */
9
-
10
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, logModificationEvent, AuditEventType, EntityType } = require('./helpers');
11
-
12
- async function createEnterpriseKnowledge({ body, requestContext }) {
13
- try {
14
- const Request_ID = requestContext.requestId;
15
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
16
- if (!email) {
17
- return createErrorResponse(401, 'Authentication required');
18
- }
19
-
20
- const userResult = await executeQuery(
21
- `SELECT u.client_id, c.subscription_tier
22
- FROM rapport.users u
23
- JOIN rapport.clients c ON c.client_id = u.client_id
24
- WHERE u.email_address = $1 LIMIT 1`,
25
- [email]
26
- );
27
- if (!userResult.rows.length) {
28
- return createErrorResponse(403, 'User not found');
29
- }
30
- if (userResult.rows[0].subscription_tier !== 'enterprise') {
31
- return createErrorResponse(403, 'Enterprise subscription required');
32
- }
33
- const clientId = userResult.rows[0].client_id;
34
-
35
- const requestBody = body || {};
36
- const { title, content, category, tags, invariant_id } = requestBody;
37
-
38
- if (!title || !content) {
39
- return createErrorResponse(400, 'title and content are required');
40
- }
41
-
42
- const result = await executeQuery(
43
- `INSERT INTO rapport.curated_knowledge
44
- (client_id, invariant_id, title, content, category, tags, curator_email, status)
45
- VALUES ($1, $2, $3, $4, $5, $6, $7, 'draft')
46
- RETURNING *`,
47
- [
48
- clientId,
49
- invariant_id || null,
50
- title,
51
- content,
52
- category || null,
53
- tags || [],
54
- email,
55
- ]
56
- );
57
-
58
- const created = result.rows[0];
59
-
60
- await logModificationEvent(AuditEventType.DATA_CREATE, EntityType.KNOWLEDGE, String(created.knowledge_id), {
61
- requestContext,
62
- client_id: clientId,
63
- after_state: { title, category, status: 'draft' },
64
- });
65
-
66
- return createSuccessResponse(
67
- { Records: [created] },
68
- 'Knowledge item created',
69
- { Total_Records: 1, Request_ID, Timestamp: new Date().toISOString() }
70
- );
71
-
72
- } catch (error) {
73
- return handleError(error);
74
- }
75
- }
76
-
77
- exports.handler = wrapHandler(createEnterpriseKnowledge);
@@ -1,71 +0,0 @@
1
- /**
2
- * Enterprise Knowledge Delete Handler
3
- *
4
- * DELETE /api/enterprise/knowledge?knowledge_id=xxx
5
- * Returns: { Records: [] } + meta
6
- * Auth: Cognito JWT required, enterprise tier only
7
- */
8
-
9
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, logModificationEvent, AuditEventType, EntityType } = require('./helpers');
10
-
11
- async function deleteEnterpriseKnowledge({ queryStringParameters, requestContext }) {
12
- try {
13
- const Request_ID = requestContext.requestId;
14
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
15
- if (!email) {
16
- return createErrorResponse(401, 'Authentication required');
17
- }
18
-
19
- const userResult = await executeQuery(
20
- `SELECT u.client_id, c.subscription_tier
21
- FROM rapport.users u
22
- JOIN rapport.clients c ON c.client_id = u.client_id
23
- WHERE u.email_address = $1 LIMIT 1`,
24
- [email]
25
- );
26
- if (!userResult.rows.length) {
27
- return createErrorResponse(403, 'User not found');
28
- }
29
- if (userResult.rows[0].subscription_tier !== 'enterprise') {
30
- return createErrorResponse(403, 'Enterprise subscription required');
31
- }
32
- const clientId = userResult.rows[0].client_id;
33
-
34
- const knowledgeId = (queryStringParameters || {}).knowledge_id;
35
- if (!knowledgeId) {
36
- return createErrorResponse(400, 'knowledge_id is required');
37
- }
38
-
39
- // Fetch current state for audit trail
40
- const current = await executeQuery(
41
- 'SELECT * FROM rapport.curated_knowledge WHERE knowledge_id = $1 AND client_id = $2',
42
- [knowledgeId, clientId]
43
- );
44
- if (!current.rows.length) {
45
- return createErrorResponse(404, 'Knowledge item not found');
46
- }
47
- const beforeState = current.rows[0];
48
-
49
- await executeQuery(
50
- 'DELETE FROM rapport.curated_knowledge WHERE knowledge_id = $1 AND client_id = $2',
51
- [knowledgeId, clientId]
52
- );
53
-
54
- await logModificationEvent(AuditEventType.DATA_DELETE, EntityType.KNOWLEDGE, String(knowledgeId), {
55
- requestContext,
56
- client_id: clientId,
57
- before_state: { title: beforeState.title, status: beforeState.status },
58
- });
59
-
60
- return createSuccessResponse(
61
- { Records: [] },
62
- 'Knowledge item deleted',
63
- { Total_Records: 0, Request_ID, Timestamp: new Date().toISOString() }
64
- );
65
-
66
- } catch (error) {
67
- return handleError(error);
68
- }
69
- }
70
-
71
- exports.handler = wrapHandler(deleteEnterpriseKnowledge);