@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,87 +0,0 @@
1
- /**
2
- * Enterprise Knowledge Get Handler
3
- *
4
- * GET /api/enterprise/knowledge?page=&pageSize=&status=&category=
5
- * Returns: { Records: CuratedKnowledge[] } + 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 getEnterpriseKnowledge({ 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 page = Math.max(1, parseInt(params.page) || 1);
36
- const pageSize = Math.min(100, Math.max(1, parseInt(params.pageSize) || 20));
37
- const offset = (page - 1) * pageSize;
38
-
39
- const conditions = ['client_id = $1'];
40
- const values = [clientId];
41
- let paramIndex = 2;
42
-
43
- if (params.status) {
44
- conditions.push(`status = $${paramIndex}`);
45
- values.push(params.status);
46
- paramIndex++;
47
- }
48
-
49
- if (params.category) {
50
- conditions.push(`category = $${paramIndex}`);
51
- values.push(params.category);
52
- paramIndex++;
53
- }
54
-
55
- const whereClause = conditions.join(' AND ');
56
-
57
- const countResult = await executeQuery(
58
- `SELECT COUNT(*) as total FROM rapport.curated_knowledge WHERE ${whereClause}`,
59
- values
60
- );
61
- const total = parseInt(countResult.rows[0].total);
62
-
63
- const dataResult = await executeQuery(
64
- `SELECT
65
- knowledge_id, client_id, invariant_id,
66
- title, content, category, tags,
67
- curator_email, status,
68
- created_at, published_at
69
- FROM rapport.curated_knowledge
70
- WHERE ${whereClause}
71
- ORDER BY created_at DESC
72
- LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
73
- [...values, pageSize, offset]
74
- );
75
-
76
- return createSuccessResponse(
77
- { Records: dataResult.rows },
78
- 'Knowledge items retrieved',
79
- { Total_Records: total, Page: page, Page_Size: pageSize, Request_ID, Timestamp: new Date().toISOString() }
80
- );
81
-
82
- } catch (error) {
83
- return handleError(error);
84
- }
85
- }
86
-
87
- exports.handler = wrapHandler(getEnterpriseKnowledge);
@@ -1,122 +0,0 @@
1
- /**
2
- * Enterprise Knowledge Update Handler
3
- *
4
- * PUT /api/enterprise/knowledge
5
- * Body: { knowledge_id, title?, content?, category?, tags?, status? }
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 updateEnterpriseKnowledge({ 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 { knowledge_id, title, content, category, tags, status } = requestBody;
37
-
38
- if (!knowledge_id) {
39
- return createErrorResponse(400, 'knowledge_id is required');
40
- }
41
-
42
- // Fetch current state for audit trail
43
- const current = await executeQuery(
44
- 'SELECT * FROM rapport.curated_knowledge WHERE knowledge_id = $1 AND client_id = $2',
45
- [knowledge_id, clientId]
46
- );
47
- if (!current.rows.length) {
48
- return createErrorResponse(404, 'Knowledge item not found');
49
- }
50
- const beforeState = current.rows[0];
51
-
52
- // Build dynamic update
53
- const updates = [];
54
- const values = [];
55
- let paramIndex = 1;
56
-
57
- if (title !== undefined) {
58
- updates.push(`title = $${paramIndex}`);
59
- values.push(title);
60
- paramIndex++;
61
- }
62
- if (content !== undefined) {
63
- updates.push(`content = $${paramIndex}`);
64
- values.push(content);
65
- paramIndex++;
66
- }
67
- if (category !== undefined) {
68
- updates.push(`category = $${paramIndex}`);
69
- values.push(category || null);
70
- paramIndex++;
71
- }
72
- if (tags !== undefined) {
73
- updates.push(`tags = $${paramIndex}`);
74
- values.push(tags);
75
- paramIndex++;
76
- }
77
- if (status !== undefined) {
78
- updates.push(`status = $${paramIndex}`);
79
- values.push(status);
80
- paramIndex++;
81
-
82
- // Set published_at when transitioning to published
83
- if (status === 'published' && beforeState.status !== 'published') {
84
- updates.push('published_at = NOW()');
85
- }
86
- }
87
-
88
- if (updates.length === 0) {
89
- return createErrorResponse(400, 'No fields to update');
90
- }
91
-
92
- updates.push('updated_at = NOW()');
93
-
94
- const result = await executeQuery(
95
- `UPDATE rapport.curated_knowledge
96
- SET ${updates.join(', ')}
97
- WHERE knowledge_id = $${paramIndex} AND client_id = $${paramIndex + 1}
98
- RETURNING *`,
99
- [...values, knowledge_id, clientId]
100
- );
101
-
102
- const updated = result.rows[0];
103
-
104
- await logModificationEvent(AuditEventType.DATA_UPDATE, EntityType.KNOWLEDGE, String(knowledge_id), {
105
- requestContext,
106
- client_id: clientId,
107
- before_state: { title: beforeState.title, status: beforeState.status, category: beforeState.category },
108
- after_state: { title: updated.title, status: updated.status, category: updated.category },
109
- });
110
-
111
- return createSuccessResponse(
112
- { Records: [updated] },
113
- 'Knowledge item updated',
114
- { Total_Records: 1, Request_ID, Timestamp: new Date().toISOString() }
115
- );
116
-
117
- } catch (error) {
118
- return handleError(error);
119
- }
120
- }
121
-
122
- exports.handler = wrapHandler(updateEnterpriseKnowledge);
@@ -1,77 +0,0 @@
1
- /**
2
- * Enterprise Onboarding Complete Handler
3
- *
4
- * POST /api/enterprise/onboarding/complete
5
- * Auth: Cognito JWT required, enterprise tier only
6
- *
7
- * Body: { enabled_categories?: Record<string, boolean> }
8
- * Marks onboarding as completed and optionally saves standards preferences.
9
- */
10
-
11
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
12
-
13
- async function enterpriseOnboardingComplete({ requestContext, body }) {
14
- try {
15
- const Request_ID = requestContext.requestId;
16
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
17
- if (!email) {
18
- return createErrorResponse(401, 'Authentication required');
19
- }
20
-
21
- // Look up client_id and company_id
22
- const userResult = await executeQuery(
23
- `SELECT u.client_id, ue.company_id
24
- FROM rapport.users u
25
- JOIN rapport.user_entitlements ue ON ue.email_address = u.email_address
26
- WHERE u.email_address = $1
27
- LIMIT 1`,
28
- [email]
29
- );
30
- if (!userResult.rows.length) {
31
- return createErrorResponse(403, 'User not found or no company assigned');
32
- }
33
-
34
- const { client_id, company_id } = userResult.rows[0];
35
- if (!company_id) {
36
- return createErrorResponse(400, 'No company associated with this account');
37
- }
38
-
39
- // Verify enterprise tier
40
- const clientResult = await executeQuery(
41
- 'SELECT subscription_tier FROM rapport.clients WHERE client_id = $1',
42
- [client_id]
43
- );
44
- if (!clientResult.rows.length || clientResult.rows[0].subscription_tier !== 'enterprise') {
45
- return createErrorResponse(403, 'Enterprise subscription required');
46
- }
47
-
48
- // Save standards preferences if provided
49
- const enabledCategories = body?.enabled_categories;
50
- const standardsConfigured = enabledCategories && Object.keys(enabledCategories).length > 0;
51
-
52
- // Mark onboarding as completed
53
- await executeQuery(
54
- `INSERT INTO rapport.enterprise_onboarding_status
55
- (company_id, step_completed, standards_configured, completed, completed_at, completed_by)
56
- VALUES ($1, 5, $2, true, NOW(), $3)
57
- ON CONFLICT (company_id) DO UPDATE SET
58
- step_completed = 5,
59
- standards_configured = COALESCE($2, rapport.enterprise_onboarding_status.standards_configured),
60
- completed = true,
61
- completed_at = NOW(),
62
- completed_by = $3`,
63
- [company_id, standardsConfigured || false, email]
64
- );
65
-
66
- return createSuccessResponse(
67
- { Records: [{ company_id, completed: true }] },
68
- 'Enterprise onboarding completed',
69
- { Request_ID, Timestamp: new Date().toISOString() }
70
- );
71
-
72
- } catch (error) {
73
- return handleError(error);
74
- }
75
- }
76
-
77
- exports.handler = wrapHandler(enterpriseOnboardingComplete);
@@ -1,138 +0,0 @@
1
- /**
2
- * Enterprise Onboarding Invite Handler
3
- *
4
- * POST /api/enterprise/onboarding/invite
5
- * Auth: Cognito JWT required, enterprise tier only
6
- *
7
- * Body: { invitations: [{ email, role }] }
8
- * Creates invitation records for team members. Max 10 per request.
9
- */
10
-
11
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
12
-
13
- async function enterpriseOnboardingInvite({ requestContext, body }) {
14
- try {
15
- const Request_ID = requestContext.requestId;
16
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
17
- if (!email) {
18
- return createErrorResponse(401, 'Authentication required');
19
- }
20
-
21
- // Look up client_id and company_id
22
- const userResult = await executeQuery(
23
- `SELECT u.client_id, ue.company_id
24
- FROM rapport.users u
25
- JOIN rapport.user_entitlements ue ON ue.email_address = u.email_address
26
- WHERE u.email_address = $1
27
- LIMIT 1`,
28
- [email]
29
- );
30
- if (!userResult.rows.length) {
31
- return createErrorResponse(403, 'User not found or no company assigned');
32
- }
33
-
34
- const { client_id, company_id } = userResult.rows[0];
35
- if (!company_id) {
36
- return createErrorResponse(400, 'No company associated with this account');
37
- }
38
-
39
- // Verify enterprise tier
40
- const clientResult = await executeQuery(
41
- 'SELECT subscription_tier, seat_count FROM rapport.clients WHERE client_id = $1',
42
- [client_id]
43
- );
44
- if (!clientResult.rows.length || clientResult.rows[0].subscription_tier !== 'enterprise') {
45
- return createErrorResponse(403, 'Enterprise subscription required');
46
- }
47
-
48
- const seatCount = clientResult.rows[0].seat_count || 25;
49
-
50
- // Validate invitations
51
- const invitations = body?.invitations;
52
- if (!Array.isArray(invitations) || invitations.length === 0) {
53
- return createErrorResponse(400, 'invitations array is required');
54
- }
55
- if (invitations.length > 10) {
56
- return createErrorResponse(400, 'Maximum 10 invitations per request');
57
- }
58
-
59
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
60
- const validRoles = ['admin', 'member'];
61
-
62
- for (const inv of invitations) {
63
- if (!inv.email || !emailRegex.test(inv.email)) {
64
- return createErrorResponse(400, `Invalid email: ${inv.email || '(empty)'}`);
65
- }
66
- if (!inv.role || !validRoles.includes(inv.role)) {
67
- return createErrorResponse(400, `Invalid role for ${inv.email}: must be admin or member`);
68
- }
69
- }
70
-
71
- // Check seat capacity
72
- const existingResult = await executeQuery(
73
- `SELECT COUNT(*) as count FROM rapport.user_entitlements WHERE company_id = $1`,
74
- [company_id]
75
- );
76
- const existingUsers = parseInt(existingResult.rows[0].count, 10);
77
-
78
- const pendingResult = await executeQuery(
79
- `SELECT COUNT(*) as count FROM rapport.enterprise_invitations
80
- WHERE company_id = $1 AND status = 'pending'`,
81
- [company_id]
82
- );
83
- const pendingInvites = parseInt(pendingResult.rows[0].count, 10);
84
-
85
- const totalAfterInvite = existingUsers + pendingInvites + invitations.length;
86
- if (totalAfterInvite > seatCount) {
87
- return createErrorResponse(400,
88
- `Seat limit exceeded. You have ${seatCount} seats, ${existingUsers} users, ${pendingInvites} pending invites. Cannot add ${invitations.length} more.`
89
- );
90
- }
91
-
92
- // Insert invitations (skip duplicates)
93
- const inserted = [];
94
- for (const inv of invitations) {
95
- try {
96
- const result = await executeQuery(
97
- `INSERT INTO rapport.enterprise_invitations
98
- (client_id, company_id, email, role, invited_by, status, created_at)
99
- VALUES ($1, $2, $3, $4, $5, 'pending', NOW())
100
- ON CONFLICT (company_id, email) DO UPDATE SET
101
- role = EXCLUDED.role,
102
- invited_by = EXCLUDED.invited_by,
103
- status = 'pending',
104
- created_at = NOW()
105
- RETURNING invitation_id, email, role, status`,
106
- [client_id, company_id, inv.email.toLowerCase(), inv.role, email]
107
- );
108
- if (result.rows.length) {
109
- inserted.push(result.rows[0]);
110
- }
111
- } catch (insertErr) {
112
- console.log(`[EnterpriseInvite] Failed to insert invite for ${inv.email}:`, insertErr.message);
113
- }
114
- }
115
-
116
- // Update onboarding status
117
- await executeQuery(
118
- `INSERT INTO rapport.enterprise_onboarding_status
119
- (company_id, step_completed, invites_sent, completed_by)
120
- VALUES ($1, 2, $2, $3)
121
- ON CONFLICT (company_id) DO UPDATE SET
122
- step_completed = GREATEST(rapport.enterprise_onboarding_status.step_completed, 2),
123
- invites_sent = $2`,
124
- [company_id, inserted.length, email]
125
- );
126
-
127
- return createSuccessResponse(
128
- { Records: inserted, sent: inserted.length },
129
- `${inserted.length} invitation(s) created`,
130
- { Request_ID, Timestamp: new Date().toISOString() }
131
- );
132
-
133
- } catch (error) {
134
- return handleError(error);
135
- }
136
- }
137
-
138
- exports.handler = wrapHandler(enterpriseOnboardingInvite);
@@ -1,128 +0,0 @@
1
- /**
2
- * Enterprise Onboarding Setup Handler
3
- *
4
- * POST /api/enterprise/onboarding/setup
5
- * Auth: Cognito JWT required, enterprise tier only
6
- *
7
- * Body: { company_name, domain, industry?, company_size? }
8
- * Updates the company profile and marks onboarding step 1 complete.
9
- */
10
-
11
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
12
-
13
- async function enterpriseOnboardingSetup({ requestContext, body }) {
14
- try {
15
- const Request_ID = requestContext.requestId;
16
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
17
- if (!email) {
18
- return createErrorResponse(401, 'Authentication required');
19
- }
20
-
21
- // Look up client_id and company_id for the authenticated user
22
- const userResult = await executeQuery(
23
- `SELECT u.client_id, ue.company_id
24
- FROM rapport.users u
25
- JOIN rapport.user_entitlements ue ON ue.email_address = u.email_address
26
- WHERE u.email_address = $1
27
- LIMIT 1`,
28
- [email]
29
- );
30
- if (!userResult.rows.length) {
31
- return createErrorResponse(403, 'User not found or no company assigned');
32
- }
33
-
34
- const { client_id, company_id: currentCompanyId } = userResult.rows[0];
35
- if (!currentCompanyId) {
36
- return createErrorResponse(400, 'No company associated with this account');
37
- }
38
-
39
- // Verify enterprise tier
40
- const clientResult = await executeQuery(
41
- 'SELECT subscription_tier FROM rapport.clients WHERE client_id = $1',
42
- [client_id]
43
- );
44
- if (!clientResult.rows.length || clientResult.rows[0].subscription_tier !== 'enterprise') {
45
- return createErrorResponse(403, 'Enterprise subscription required');
46
- }
47
-
48
- // Validate required fields
49
- const companyName = body?.company_name;
50
- const domain = body?.domain;
51
- if (!companyName || !domain) {
52
- return createErrorResponse(400, 'company_name and domain are required');
53
- }
54
-
55
- const industry = body?.industry || null;
56
- const companySize = body?.company_size || null;
57
-
58
- // Generate a proper enterprise company_id from company name
59
- const enterpriseCompanyId = `${companyName.replace(/[^a-zA-Z0-9]/g, '_').toUpperCase()}_MAIN`;
60
- let company_id = currentCompanyId;
61
-
62
- if (enterpriseCompanyId !== currentCompanyId) {
63
- // Create the enterprise company (or update if it already exists)
64
- await executeQuery(
65
- `INSERT INTO rapport.companies (company_id, client_id, company_name, company_status, domain, industry, company_size)
66
- VALUES ($1, $2, $3, 'Active', $4, $5, $6)
67
- ON CONFLICT (company_id) DO UPDATE SET
68
- company_name = $3, domain = $4, industry = $5, company_size = $6, last_updated = NOW()`,
69
- [enterpriseCompanyId, client_id, companyName, domain, industry, companySize]
70
- );
71
-
72
- // Migrate user entitlement from personal to enterprise company
73
- await executeQuery(
74
- `UPDATE rapport.user_entitlements
75
- SET company_id = $1
76
- WHERE email_address = $2 AND company_id = $3`,
77
- [enterpriseCompanyId, email, currentCompanyId]
78
- );
79
-
80
- // Clean up orphaned personal company if nothing else references it
81
- await executeQuery(
82
- `DELETE FROM rapport.companies
83
- WHERE company_id = $1
84
- AND NOT EXISTS (
85
- SELECT 1 FROM rapport.user_entitlements WHERE company_id = $1
86
- )
87
- AND NOT EXISTS (
88
- SELECT 1 FROM rapport.projects WHERE company_id = $1
89
- )`,
90
- [currentCompanyId]
91
- );
92
-
93
- company_id = enterpriseCompanyId;
94
- console.log(`Migrated company: ${currentCompanyId} -> ${enterpriseCompanyId}`);
95
- } else {
96
- // Same company_id, just update profile
97
- await executeQuery(
98
- `UPDATE rapport.companies
99
- SET company_name = $1, domain = $2, industry = $3, company_size = $4, last_updated = NOW()
100
- WHERE company_id = $5`,
101
- [companyName, domain, industry, companySize, company_id]
102
- );
103
- }
104
-
105
- // Upsert onboarding status
106
- await executeQuery(
107
- `INSERT INTO rapport.enterprise_onboarding_status
108
- (company_id, step_completed, org_configured, started_at, completed_by)
109
- VALUES ($1, 1, true, NOW(), $2)
110
- ON CONFLICT (company_id) DO UPDATE SET
111
- step_completed = GREATEST(rapport.enterprise_onboarding_status.step_completed, 1),
112
- org_configured = true,
113
- completed_by = $2`,
114
- [company_id, email]
115
- );
116
-
117
- return createSuccessResponse(
118
- { Records: [{ company_id, company_name: companyName, domain, step_completed: 1 }] },
119
- 'Organization profile saved',
120
- { Request_ID, Timestamp: new Date().toISOString() }
121
- );
122
-
123
- } catch (error) {
124
- return handleError(error);
125
- }
126
- }
127
-
128
- exports.handler = wrapHandler(enterpriseOnboardingSetup);
@@ -1,88 +0,0 @@
1
- /**
2
- * Enterprise Onboarding Status Handler
3
- *
4
- * GET /api/enterprise/onboarding/status
5
- * Auth: Cognito JWT required
6
- *
7
- * Returns the enterprise onboarding completion state for the user's company.
8
- * Used by Dashboard to decide whether to redirect to enterprise onboarding.
9
- */
10
-
11
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
12
-
13
- async function getEnterpriseOnboardingStatus({ requestContext }) {
14
- try {
15
- const Request_ID = requestContext.requestId;
16
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
17
- if (!email) {
18
- return createErrorResponse(401, 'Authentication required');
19
- }
20
-
21
- // Look up client_id and company_id
22
- const userResult = await executeQuery(
23
- `SELECT u.client_id, ue.company_id
24
- FROM rapport.users u
25
- JOIN rapport.user_entitlements ue ON ue.email_address = u.email_address
26
- WHERE u.email_address = $1
27
- LIMIT 1`,
28
- [email]
29
- );
30
- if (!userResult.rows.length) {
31
- return createSuccessResponse(
32
- { Records: [{ completed: false, step_completed: 0 }] },
33
- 'No company found',
34
- { Request_ID, Timestamp: new Date().toISOString() }
35
- );
36
- }
37
-
38
- const { client_id, company_id } = userResult.rows[0];
39
- if (!company_id) {
40
- return createSuccessResponse(
41
- { Records: [{ completed: false, step_completed: 0 }] },
42
- 'No company assigned',
43
- { Request_ID, Timestamp: new Date().toISOString() }
44
- );
45
- }
46
-
47
- // Check if enterprise tier
48
- const clientResult = await executeQuery(
49
- 'SELECT subscription_tier FROM rapport.clients WHERE client_id = $1',
50
- [client_id]
51
- );
52
- if (!clientResult.rows.length || clientResult.rows[0].subscription_tier !== 'enterprise') {
53
- return createSuccessResponse(
54
- { Records: [{ completed: true, step_completed: 5, not_enterprise: true }] },
55
- 'Not an enterprise account',
56
- { Request_ID, Timestamp: new Date().toISOString() }
57
- );
58
- }
59
-
60
- // Get onboarding status
61
- const statusResult = await executeQuery(
62
- `SELECT *
63
- FROM rapport.enterprise_onboarding_status
64
- WHERE company_id = $1`,
65
- [company_id]
66
- );
67
-
68
- if (!statusResult.rows.length) {
69
- return createSuccessResponse(
70
- { Records: [{ completed: false, step_completed: 0, company_id }] },
71
- 'Onboarding not started',
72
- { Request_ID, Timestamp: new Date().toISOString() }
73
- );
74
- }
75
-
76
- const status = statusResult.rows[0];
77
- return createSuccessResponse(
78
- { Records: [{ ...status, company_id }] },
79
- 'Onboarding status retrieved',
80
- { Request_ID, Timestamp: new Date().toISOString() }
81
- );
82
-
83
- } catch (error) {
84
- return handleError(error);
85
- }
86
- }
87
-
88
- exports.handler = wrapHandler(getEnterpriseOnboardingStatus);
@@ -1,49 +0,0 @@
1
- /**
2
- * GitHub Connection Status Handler
3
- * Returns current GitHub connection status
4
- *
5
- * GET /api/github/status (CognitoAuthorizer)
6
- */
7
-
8
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
9
-
10
- async function githubConnectionStatus({ requestContext }) {
11
- try {
12
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
13
-
14
- if (!email) {
15
- return createErrorResponse(401, 'Authentication required');
16
- }
17
-
18
- // Check for active connection
19
- const connResult = await executeQuery(`
20
- SELECT github_username, connected_at, last_used_at
21
- FROM rapport.github_connections
22
- WHERE email_address = $1 AND revoked = FALSE
23
- `, [email]);
24
-
25
- const connected = connResult.rowCount > 0;
26
- const github_username = connected ? connResult.rows[0].github_username : null;
27
-
28
- // Count projects with GitHub connected
29
- const projectsResult = await executeQuery(`
30
- SELECT COUNT(*) as count FROM rapport.projects p
31
- JOIN rapport.project_collaborators pc ON p.project_id = pc.project_id
32
- WHERE pc.email_address = $1
33
- AND p.repo_url IS NOT NULL
34
- AND p.archived = FALSE
35
- `, [email]);
36
-
37
- const projects_connected = parseInt(projectsResult.rows[0].count);
38
-
39
- return createSuccessResponse(
40
- { connected, github_username, projects_connected },
41
- 'Status retrieved'
42
- );
43
- } catch (error) {
44
- console.error('GitHub Status Error:', error);
45
- return createErrorResponse(500, 'Failed to get connection status');
46
- }
47
- }
48
-
49
- exports.handler = wrapHandler(githubConnectionStatus);