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