@equilateral_ai/mindmeld 3.5.3 → 4.0.2

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 (138) hide show
  1. package/hooks/session-start.js +312 -85
  2. package/package.json +21 -13
  3. package/scripts/init-project.js +9 -23
  4. package/scripts/repo-analyzer.js +118 -2
  5. package/src/client/dbShim.js +16 -0
  6. package/src/core/AuthManager.js +3 -2
  7. package/src/handlers/helpers/dbOperations.js +9 -46
  8. package/src/index.js +2 -217
  9. package/src/utils/piiMask.js +16 -0
  10. package/scripts/inject.js +0 -409
  11. package/scripts/mcp-bridge.js +0 -220
  12. package/scripts/standards.js +0 -285
  13. package/src/collaboration/CollaborationPrompt.js +0 -460
  14. package/src/core/AlertEngine.js +0 -813
  15. package/src/core/AlertNotifier.js +0 -363
  16. package/src/core/CorrelationAnalyzer.js +0 -931
  17. package/src/core/CrossReferenceEngine.js +0 -624
  18. package/src/core/CurationEngine.js +0 -688
  19. package/src/core/DeprecationScheduler.js +0 -183
  20. package/src/core/LoadBearingDetector.js +0 -242
  21. package/src/core/NotificationService.js +0 -1032
  22. package/src/core/RapportOrchestrator.js +0 -632
  23. package/src/core/RelevanceDetector.js +0 -694
  24. package/src/core/StandardLifecycle.js +0 -244
  25. package/src/core/StandardsIngestion.js +0 -991
  26. package/src/core/TeamLoadBearingDetector.js +0 -431
  27. package/src/core/parsers/adrParser.js +0 -479
  28. package/src/core/parsers/cursorRulesParser.js +0 -564
  29. package/src/core/parsers/eslintParser.js +0 -439
  30. package/src/database/dbOperations.js +0 -105
  31. package/src/handlers/activity/activityGetMe.js +0 -98
  32. package/src/handlers/activity/activityGetTeam.js +0 -175
  33. package/src/handlers/admin/adminSetup.js +0 -216
  34. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  35. package/src/handlers/alerts/alertsGet.js +0 -250
  36. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  37. package/src/handlers/analytics/coachingGet.js +0 -361
  38. package/src/handlers/analytics/convergenceGet.js +0 -236
  39. package/src/handlers/analytics/developerScoreGet.js +0 -137
  40. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  41. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  42. package/src/handlers/collaborators/collaboratorList.js +0 -82
  43. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  44. package/src/handlers/collaborators/inviteAccept.js +0 -122
  45. package/src/handlers/company/companyUsersDelete.js +0 -141
  46. package/src/handlers/company/companyUsersGet.js +0 -90
  47. package/src/handlers/company/companyUsersPost.js +0 -267
  48. package/src/handlers/company/companyUsersPut.js +0 -76
  49. package/src/handlers/context/contextGet.js +0 -57
  50. package/src/handlers/context/invariantsGet.js +0 -74
  51. package/src/handlers/context/loopsGet.js +0 -82
  52. package/src/handlers/context/notesCreate.js +0 -74
  53. package/src/handlers/context/purposeGet.js +0 -78
  54. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  55. package/src/handlers/correlations/correlationsGet.js +0 -93
  56. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  57. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  58. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  59. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  60. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  61. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  62. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  63. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  64. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  65. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  66. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  67. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  68. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  69. package/src/handlers/github/githubConnectionStatus.js +0 -49
  70. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  71. package/src/handlers/github/githubOAuthCallback.js +0 -178
  72. package/src/handlers/github/githubOAuthStart.js +0 -59
  73. package/src/handlers/github/githubPatternsReview.js +0 -76
  74. package/src/handlers/github/githubReposList.js +0 -105
  75. package/src/handlers/health/healthGet.js +0 -55
  76. package/src/handlers/helpers/auditLogger.js +0 -201
  77. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  78. package/src/handlers/helpers/decisionFrames.js +0 -29
  79. package/src/handlers/helpers/errorHandler.js +0 -49
  80. package/src/handlers/helpers/index.js +0 -138
  81. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  82. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  83. package/src/handlers/helpers/predictiveCache.js +0 -51
  84. package/src/handlers/helpers/projectAccess.js +0 -88
  85. package/src/handlers/helpers/responseUtil.js +0 -55
  86. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  87. package/src/handlers/mcp/mcpHandler.js +0 -569
  88. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  89. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  90. package/src/handlers/notifications/getPreferences.js +0 -84
  91. package/src/handlers/notifications/sendNotification.js +0 -170
  92. package/src/handlers/notifications/updatePreferences.js +0 -316
  93. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  94. package/src/handlers/patterns/patternUsagePost.js +0 -182
  95. package/src/handlers/patterns/patternViolationPost.js +0 -185
  96. package/src/handlers/projects/projectCreate.js +0 -248
  97. package/src/handlers/projects/projectDelete.js +0 -82
  98. package/src/handlers/projects/projectGet.js +0 -95
  99. package/src/handlers/projects/projectUpdate.js +0 -117
  100. package/src/handlers/reports/aiLeverage.js +0 -210
  101. package/src/handlers/reports/engineeringInvestment.js +0 -132
  102. package/src/handlers/reports/riskForecast.js +0 -206
  103. package/src/handlers/reports/standardsRoi.js +0 -254
  104. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  105. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  106. package/src/handlers/scheduled/generateAlerts.js +0 -135
  107. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  108. package/src/handlers/scheduled/refreshActivity.js +0 -21
  109. package/src/handlers/scheduled/scanCompliance.js +0 -334
  110. package/src/handlers/sessions/sessionEndPost.js +0 -180
  111. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  112. package/src/handlers/standards/catalogGet.js +0 -185
  113. package/src/handlers/standards/catalogSync.js +0 -120
  114. package/src/handlers/standards/discoveriesGet.js +0 -89
  115. package/src/handlers/standards/projectStandardsGet.js +0 -129
  116. package/src/handlers/standards/projectStandardsPut.js +0 -151
  117. package/src/handlers/standards/standardsAuditGet.js +0 -65
  118. package/src/handlers/standards/standardsParseUpload.js +0 -149
  119. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  120. package/src/handlers/standards/standardsTransition.js +0 -161
  121. package/src/handlers/stripe/addonManagePost.js +0 -240
  122. package/src/handlers/stripe/billingPortalPost.js +0 -93
  123. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  124. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  125. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  126. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  127. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  128. package/src/handlers/stripe/webhookPost.js +0 -482
  129. package/src/handlers/user/apiTokenCreate.js +0 -71
  130. package/src/handlers/user/apiTokenList.js +0 -64
  131. package/src/handlers/user/userSplashAck.js +0 -91
  132. package/src/handlers/user/userSplashGet.js +0 -211
  133. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  134. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  135. package/src/handlers/users/userEntitlementsGet.js +0 -89
  136. package/src/handlers/users/userGet.js +0 -118
  137. package/src/handlers/users/userProfilePut.js +0 -77
  138. 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);