@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.
- package/hooks/session-start.js +312 -85
- package/package.json +20 -14
- package/scripts/init-project.js +9 -23
- package/src/client/dbShim.js +16 -0
- package/src/core/AuthManager.js +3 -2
- package/src/handlers/helpers/dbOperations.js +9 -46
- package/src/index.js +2 -217
- package/src/utils/piiMask.js +16 -0
- package/scripts/harvest.js +0 -601
- package/scripts/inject.js +0 -409
- package/scripts/mcp-bridge.js +0 -220
- package/scripts/repo-analyzer.js +0 -870
- package/scripts/standards.js +0 -285
- package/src/collaboration/CollaborationPrompt.js +0 -460
- package/src/core/AlertEngine.js +0 -813
- package/src/core/AlertNotifier.js +0 -363
- package/src/core/CorrelationAnalyzer.js +0 -931
- package/src/core/CrossReferenceEngine.js +0 -624
- package/src/core/CurationEngine.js +0 -688
- package/src/core/DeprecationScheduler.js +0 -183
- package/src/core/LoadBearingDetector.js +0 -242
- package/src/core/NotificationService.js +0 -1032
- package/src/core/RapportOrchestrator.js +0 -632
- package/src/core/RelevanceDetector.js +0 -694
- package/src/core/StandardLifecycle.js +0 -244
- package/src/core/StandardsIngestion.js +0 -991
- package/src/core/TeamLoadBearingDetector.js +0 -431
- package/src/core/parsers/adrParser.js +0 -479
- package/src/core/parsers/cursorRulesParser.js +0 -564
- package/src/core/parsers/eslintParser.js +0 -439
- package/src/database/dbOperations.js +0 -105
- package/src/handlers/activity/activityGetMe.js +0 -98
- package/src/handlers/activity/activityGetTeam.js +0 -175
- package/src/handlers/admin/adminSetup.js +0 -216
- package/src/handlers/alerts/alertsAcknowledge.js +0 -92
- package/src/handlers/alerts/alertsGet.js +0 -250
- package/src/handlers/analytics/activitySummaryGet.js +0 -234
- package/src/handlers/analytics/coachingGet.js +0 -361
- package/src/handlers/analytics/convergenceGet.js +0 -236
- package/src/handlers/analytics/developerScoreGet.js +0 -137
- package/src/handlers/collaborators/collaboratorAdd.js +0 -200
- package/src/handlers/collaborators/collaboratorInvite.js +0 -219
- package/src/handlers/collaborators/collaboratorList.js +0 -82
- package/src/handlers/collaborators/collaboratorRemove.js +0 -128
- package/src/handlers/collaborators/inviteAccept.js +0 -122
- package/src/handlers/company/companyUsersDelete.js +0 -141
- package/src/handlers/company/companyUsersGet.js +0 -90
- package/src/handlers/company/companyUsersPost.js +0 -267
- package/src/handlers/company/companyUsersPut.js +0 -76
- package/src/handlers/context/contextGet.js +0 -57
- package/src/handlers/context/invariantsGet.js +0 -74
- package/src/handlers/context/loopsGet.js +0 -82
- package/src/handlers/context/notesCreate.js +0 -74
- package/src/handlers/context/purposeGet.js +0 -78
- package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
- package/src/handlers/correlations/correlationsGet.js +0 -93
- package/src/handlers/correlations/correlationsProjectGet.js +0 -153
- package/src/handlers/enterprise/controlTowerGet.js +0 -224
- package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
- package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
- package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
- package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
- package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
- package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
- package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
- package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
- package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
- package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
- package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
- package/src/handlers/github/githubConnectionStatus.js +0 -49
- package/src/handlers/github/githubDiscoverPatterns.js +0 -621
- package/src/handlers/github/githubOAuthCallback.js +0 -178
- package/src/handlers/github/githubOAuthStart.js +0 -59
- package/src/handlers/github/githubPatternsReview.js +0 -76
- package/src/handlers/github/githubReposList.js +0 -105
- package/src/handlers/health/healthGet.js +0 -55
- package/src/handlers/helpers/auditLogger.js +0 -201
- package/src/handlers/helpers/checkSuperAdmin.js +0 -84
- package/src/handlers/helpers/decisionFrames.js +0 -29
- package/src/handlers/helpers/errorHandler.js +0 -49
- package/src/handlers/helpers/index.js +0 -138
- package/src/handlers/helpers/lambdaWrapper.js +0 -60
- package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
- package/src/handlers/helpers/predictiveCache.js +0 -51
- package/src/handlers/helpers/projectAccess.js +0 -88
- package/src/handlers/helpers/responseUtil.js +0 -55
- package/src/handlers/helpers/subscriptionTiers.js +0 -1168
- package/src/handlers/mcp/mcpHandler.js +0 -569
- package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
- package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
- package/src/handlers/notifications/getPreferences.js +0 -84
- package/src/handlers/notifications/sendNotification.js +0 -170
- package/src/handlers/notifications/updatePreferences.js +0 -316
- package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
- package/src/handlers/patterns/patternUsagePost.js +0 -182
- package/src/handlers/patterns/patternViolationPost.js +0 -185
- package/src/handlers/projects/projectCreate.js +0 -248
- package/src/handlers/projects/projectDelete.js +0 -82
- package/src/handlers/projects/projectGet.js +0 -95
- package/src/handlers/projects/projectUpdate.js +0 -117
- package/src/handlers/reports/aiLeverage.js +0 -210
- package/src/handlers/reports/engineeringInvestment.js +0 -132
- package/src/handlers/reports/riskForecast.js +0 -206
- package/src/handlers/reports/standardsRoi.js +0 -254
- package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
- package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
- package/src/handlers/scheduled/generateAlerts.js +0 -135
- package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
- package/src/handlers/scheduled/refreshActivity.js +0 -21
- package/src/handlers/scheduled/scanCompliance.js +0 -334
- package/src/handlers/sessions/sessionEndPost.js +0 -180
- package/src/handlers/sessions/sessionStandardsPost.js +0 -171
- package/src/handlers/standards/catalogGet.js +0 -185
- package/src/handlers/standards/catalogSync.js +0 -120
- package/src/handlers/standards/discoveriesGet.js +0 -89
- package/src/handlers/standards/projectStandardsGet.js +0 -129
- package/src/handlers/standards/projectStandardsPut.js +0 -151
- package/src/handlers/standards/standardsAuditGet.js +0 -65
- package/src/handlers/standards/standardsParseUpload.js +0 -149
- package/src/handlers/standards/standardsRelevantPost.js +0 -405
- package/src/handlers/standards/standardsTransition.js +0 -161
- package/src/handlers/stripe/addonManagePost.js +0 -240
- package/src/handlers/stripe/billingPortalPost.js +0 -93
- package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
- package/src/handlers/stripe/seatsUpdatePost.js +0 -185
- package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
- package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
- package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
- package/src/handlers/stripe/webhookPost.js +0 -482
- package/src/handlers/user/apiTokenCreate.js +0 -71
- package/src/handlers/user/apiTokenList.js +0 -64
- package/src/handlers/user/userSplashAck.js +0 -91
- package/src/handlers/user/userSplashGet.js +0 -211
- package/src/handlers/users/cognitoPostConfirmation.js +0 -186
- package/src/handlers/users/cognitoPreSignUp.js +0 -114
- package/src/handlers/users/userEntitlementsGet.js +0 -89
- package/src/handlers/users/userGet.js +0 -118
- package/src/handlers/users/userProfilePut.js +0 -77
- package/src/handlers/webhooks/githubWebhook.js +0 -215
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Company Users Invite Handler
|
|
3
|
-
* Invites a user to a company by creating an entitlement and sending an invite email
|
|
4
|
-
* Supports two billing modes: self_pays (invitee pays) or admin_pays (license on admin's subscription)
|
|
5
|
-
*
|
|
6
|
-
* POST /api/company/users
|
|
7
|
-
* Body: { company_id, email, role, billing }
|
|
8
|
-
* Auth: Cognito JWT required
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, getTierConfig } = require('./helpers');
|
|
12
|
-
const { SESClient, SendEmailCommand } = require('@aws-sdk/client-ses');
|
|
13
|
-
const Stripe = require('stripe');
|
|
14
|
-
|
|
15
|
-
const ses = new SESClient({ region: process.env.AWS_REGION || 'us-east-2' });
|
|
16
|
-
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Invite user to company
|
|
20
|
-
* Requires admin access to the company
|
|
21
|
-
*/
|
|
22
|
-
async function inviteCompanyUser({ body: requestBody = {}, requestContext }) {
|
|
23
|
-
try {
|
|
24
|
-
const Request_ID = requestContext.requestId;
|
|
25
|
-
const callerEmail = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
26
|
-
const { company_id, email, role, billing = 'self_pays' } = requestBody;
|
|
27
|
-
|
|
28
|
-
if (!callerEmail) {
|
|
29
|
-
return createErrorResponse(401, 'Authentication required');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!company_id || !email) {
|
|
33
|
-
return createErrorResponse(400, 'company_id and email are required');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!['admin_pays', 'self_pays'].includes(billing)) {
|
|
37
|
-
return createErrorResponse(400, 'billing must be admin_pays or self_pays');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Verify caller is admin of this company
|
|
41
|
-
const adminQuery = `
|
|
42
|
-
SELECT ue.admin, ue.client_id, u.first_name, u.last_name
|
|
43
|
-
FROM rapport.user_entitlements ue
|
|
44
|
-
LEFT JOIN rapport.users u ON u.email_address = ue.email_address
|
|
45
|
-
WHERE ue.email_address = $1 AND ue.company_id = $2
|
|
46
|
-
`;
|
|
47
|
-
const adminCheck = await executeQuery(adminQuery, [callerEmail, company_id]);
|
|
48
|
-
|
|
49
|
-
if (adminCheck.rowCount === 0 || !adminCheck.rows[0].admin) {
|
|
50
|
-
return createErrorResponse(403, 'Admin access required to invite users');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const clientId = adminCheck.rows[0].client_id;
|
|
54
|
-
const inviterName = [adminCheck.rows[0].first_name, adminCheck.rows[0].last_name].filter(Boolean).join(' ') || callerEmail;
|
|
55
|
-
|
|
56
|
-
// Get client details for seat limits and Stripe info
|
|
57
|
-
const clientQuery = `
|
|
58
|
-
SELECT subscription_tier, seat_count, client_name, stripe_subscription_id, license_count
|
|
59
|
-
FROM rapport.clients WHERE client_id = $1
|
|
60
|
-
`;
|
|
61
|
-
const clientResult = await executeQuery(clientQuery, [clientId]);
|
|
62
|
-
const clientRecord = clientResult.rows[0];
|
|
63
|
-
const tierConfig = getTierConfig(clientRecord?.subscription_tier || 'free');
|
|
64
|
-
const maxCollaborators = tierConfig?.maxCollaborators;
|
|
65
|
-
const teamName = clientRecord?.client_name || 'their team';
|
|
66
|
-
const inviterTier = clientRecord?.subscription_tier || 'team';
|
|
67
|
-
|
|
68
|
-
// Check seat limits before allowing invite
|
|
69
|
-
if (maxCollaborators !== null) {
|
|
70
|
-
const countQuery = `
|
|
71
|
-
SELECT COUNT(*) as current_count
|
|
72
|
-
FROM rapport.user_entitlements WHERE company_id = $1
|
|
73
|
-
`;
|
|
74
|
-
const countResult = await executeQuery(countQuery, [company_id]);
|
|
75
|
-
const currentCount = parseInt(countResult.rows[0].current_count) || 0;
|
|
76
|
-
|
|
77
|
-
if (currentCount >= maxCollaborators) {
|
|
78
|
-
return createErrorResponse(403, `Seat limit reached (${currentCount}/${maxCollaborators}). Upgrade your plan to add more team members.`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// For admin_pays, verify admin has an active Stripe subscription
|
|
83
|
-
if (billing === 'admin_pays' && !clientRecord?.stripe_subscription_id) {
|
|
84
|
-
return createErrorResponse(400, 'You need an active subscription to add licensed users. Subscribe first, then invite with "Add to my subscription".');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Ensure invited user exists in users table (create pending record if not)
|
|
88
|
-
await executeQuery(`
|
|
89
|
-
INSERT INTO rapport.users (email_address, user_status, active, create_date)
|
|
90
|
-
VALUES ($1, 'Pending', true, NOW())
|
|
91
|
-
ON CONFLICT (email_address) DO NOTHING
|
|
92
|
-
`, [email]);
|
|
93
|
-
|
|
94
|
-
// Create entitlement for invited user
|
|
95
|
-
const isAdmin = role === 'admin';
|
|
96
|
-
const billingType = billing === 'admin_pays' ? 'admin_paid' : 'self_paid';
|
|
97
|
-
const insertQuery = `
|
|
98
|
-
INSERT INTO rapport.user_entitlements (
|
|
99
|
-
email_address, client_id, company_id, admin, member, billing_type
|
|
100
|
-
) VALUES ($1, $2, $3, $4, true, $5)
|
|
101
|
-
ON CONFLICT (email_address, company_id) DO UPDATE SET
|
|
102
|
-
admin = EXCLUDED.admin,
|
|
103
|
-
member = true,
|
|
104
|
-
billing_type = EXCLUDED.billing_type
|
|
105
|
-
RETURNING email_address, admin, member, billing_type
|
|
106
|
-
`;
|
|
107
|
-
const result = await executeQuery(insertQuery, [email, clientId, company_id, isAdmin, billingType]);
|
|
108
|
-
|
|
109
|
-
// If admin_pays, update license count and Stripe subscription quantity
|
|
110
|
-
let stripeUpdated = false;
|
|
111
|
-
if (billing === 'admin_pays') {
|
|
112
|
-
// Increment license_count
|
|
113
|
-
const updateResult = await executeQuery(`
|
|
114
|
-
UPDATE rapport.clients
|
|
115
|
-
SET license_count = COALESCE(license_count, 1) + 1, last_updated = CURRENT_TIMESTAMP
|
|
116
|
-
WHERE client_id = $1
|
|
117
|
-
RETURNING license_count
|
|
118
|
-
`, [clientId]);
|
|
119
|
-
const newLicenseCount = updateResult.rows[0].license_count;
|
|
120
|
-
|
|
121
|
-
// Update Stripe subscription quantity
|
|
122
|
-
try {
|
|
123
|
-
const subscription = await stripe.subscriptions.retrieve(clientRecord.stripe_subscription_id);
|
|
124
|
-
const item = subscription.items.data[0];
|
|
125
|
-
if (item) {
|
|
126
|
-
await stripe.subscriptions.update(clientRecord.stripe_subscription_id, {
|
|
127
|
-
items: [{ id: item.id, quantity: newLicenseCount }],
|
|
128
|
-
proration_behavior: 'none'
|
|
129
|
-
});
|
|
130
|
-
stripeUpdated = true;
|
|
131
|
-
console.log(`[License] Updated Stripe quantity to ${newLicenseCount} for ${clientId}`);
|
|
132
|
-
}
|
|
133
|
-
} catch (stripeError) {
|
|
134
|
-
console.error('[License] Stripe update failed:', stripeError.message);
|
|
135
|
-
// Revert license_count since Stripe failed
|
|
136
|
-
await executeQuery(`
|
|
137
|
-
UPDATE rapport.clients
|
|
138
|
-
SET license_count = COALESCE(license_count, 2) - 1, last_updated = CURRENT_TIMESTAMP
|
|
139
|
-
WHERE client_id = $1
|
|
140
|
-
`, [clientId]);
|
|
141
|
-
return createErrorResponse(500, 'Failed to update subscription. Please try again or contact support.');
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Send invite email
|
|
146
|
-
const appUrl = process.env.APP_URL || 'https://app.mindmeld.dev';
|
|
147
|
-
// Admin-paid users don't need to go through checkout, so no ?tier= param
|
|
148
|
-
const signupUrl = billing === 'admin_pays'
|
|
149
|
-
? `${appUrl}/signup`
|
|
150
|
-
: `${appUrl}/signup?tier=${inviterTier}`;
|
|
151
|
-
let emailSent = false;
|
|
152
|
-
|
|
153
|
-
// Customize email message based on billing type
|
|
154
|
-
const billingNote = billing === 'admin_pays'
|
|
155
|
-
? 'Your subscription is covered — just sign up and start using MindMeld.'
|
|
156
|
-
: 'MindMeld brings intelligent standards and patterns directly into your AI coding sessions — helping your team write better code, faster.';
|
|
157
|
-
|
|
158
|
-
const emailParams = {
|
|
159
|
-
Source: process.env.EMAIL_FROM || 'noreply@mindmeld.dev',
|
|
160
|
-
Destination: {
|
|
161
|
-
ToAddresses: [email]
|
|
162
|
-
},
|
|
163
|
-
Message: {
|
|
164
|
-
Subject: {
|
|
165
|
-
Data: `${inviterName} invited you to join ${teamName} on MindMeld`,
|
|
166
|
-
Charset: 'UTF-8'
|
|
167
|
-
},
|
|
168
|
-
Body: {
|
|
169
|
-
Html: {
|
|
170
|
-
Data: `
|
|
171
|
-
<!DOCTYPE html>
|
|
172
|
-
<html>
|
|
173
|
-
<head>
|
|
174
|
-
<meta charset="utf-8">
|
|
175
|
-
<style>
|
|
176
|
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
|
|
177
|
-
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
178
|
-
.header { text-align: center; margin-bottom: 30px; }
|
|
179
|
-
.logo { font-size: 24px; font-weight: bold; color: #2563eb; }
|
|
180
|
-
.content { background: #f8fafc; border-radius: 8px; padding: 30px; margin-bottom: 20px; }
|
|
181
|
-
.button { display: inline-block; background: #2563eb; color: white; padding: 12px 24px; border-radius: 6px; text-decoration: none; font-weight: 500; }
|
|
182
|
-
.footer { text-align: center; color: #64748b; font-size: 14px; }
|
|
183
|
-
.role-badge { display: inline-block; background: #e0e7ff; color: #3730a3; padding: 4px 12px; border-radius: 20px; font-size: 14px; }
|
|
184
|
-
</style>
|
|
185
|
-
</head>
|
|
186
|
-
<body>
|
|
187
|
-
<div class="container">
|
|
188
|
-
<div class="header">
|
|
189
|
-
<div class="logo">MindMeld</div>
|
|
190
|
-
</div>
|
|
191
|
-
<div class="content">
|
|
192
|
-
<p>Hi there,</p>
|
|
193
|
-
<p><strong>${inviterName}</strong> has invited you to join <strong>${teamName}</strong> on MindMeld.</p>
|
|
194
|
-
<p>Your role: <span class="role-badge">${isAdmin ? 'Admin' : 'Member'}</span></p>
|
|
195
|
-
<p>${billingNote}</p>
|
|
196
|
-
<p style="margin-top: 30px;">
|
|
197
|
-
<a href="${signupUrl}" class="button">Get Started</a>
|
|
198
|
-
</p>
|
|
199
|
-
<p style="margin-top: 20px; font-size: 14px; color: #64748b;">
|
|
200
|
-
Already have an account? <a href="${appUrl}" style="color: #2563eb;">Sign in</a> — your team access will be ready.
|
|
201
|
-
</p>
|
|
202
|
-
</div>
|
|
203
|
-
<div class="footer">
|
|
204
|
-
<p>MindMeld - Intelligent standards for AI coding</p>
|
|
205
|
-
<p>Powered by Equilateral AI</p>
|
|
206
|
-
</div>
|
|
207
|
-
</div>
|
|
208
|
-
</body>
|
|
209
|
-
</html>
|
|
210
|
-
`,
|
|
211
|
-
Charset: 'UTF-8'
|
|
212
|
-
},
|
|
213
|
-
Text: {
|
|
214
|
-
Data: `${inviterName} has invited you to join ${teamName} on MindMeld.
|
|
215
|
-
|
|
216
|
-
Your role: ${isAdmin ? 'Admin' : 'Member'}
|
|
217
|
-
|
|
218
|
-
${billingNote}
|
|
219
|
-
|
|
220
|
-
Get started: ${signupUrl}
|
|
221
|
-
|
|
222
|
-
Already have an account? Sign in at ${appUrl} — your team access will be ready.
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
MindMeld - Intelligent standards for AI coding
|
|
226
|
-
Powered by Equilateral AI
|
|
227
|
-
`,
|
|
228
|
-
Charset: 'UTF-8'
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
await ses.send(new SendEmailCommand(emailParams));
|
|
236
|
-
emailSent = true;
|
|
237
|
-
} catch (sesError) {
|
|
238
|
-
console.error('SES Error sending invite:', sesError);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const responseRecords = result.rows.map(r => ({
|
|
242
|
-
...r,
|
|
243
|
-
email_sent: emailSent,
|
|
244
|
-
stripe_updated: stripeUpdated
|
|
245
|
-
}));
|
|
246
|
-
|
|
247
|
-
const message = billing === 'admin_pays'
|
|
248
|
-
? `${email} added to your subscription (license ${stripeUpdated ? 'updated' : 'pending'})`
|
|
249
|
-
: emailSent ? `Invitation sent to ${email}` : `User ${email} added to team (email delivery failed — share the link manually)`;
|
|
250
|
-
|
|
251
|
-
return createSuccessResponse(
|
|
252
|
-
{ Records: responseRecords },
|
|
253
|
-
message,
|
|
254
|
-
{
|
|
255
|
-
Total_Records: result.rowCount,
|
|
256
|
-
Request_ID,
|
|
257
|
-
Timestamp: new Date().toISOString()
|
|
258
|
-
}
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
} catch (error) {
|
|
262
|
-
console.error('Handler Error:', error);
|
|
263
|
-
return handleError(error);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
exports.handler = wrapHandler(inviteCompanyUser);
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Company Users Update Handler
|
|
3
|
-
* Updates a user's role within a company
|
|
4
|
-
*
|
|
5
|
-
* PUT /api/company/users
|
|
6
|
-
* Body: { company_id, email, role }
|
|
7
|
-
* Auth: Cognito JWT required
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError } = require('./helpers');
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Update user role in company
|
|
14
|
-
* Requires admin access to the company
|
|
15
|
-
*/
|
|
16
|
-
async function updateCompanyUser({ body: requestBody = {}, requestContext }) {
|
|
17
|
-
try {
|
|
18
|
-
const Request_ID = requestContext.requestId;
|
|
19
|
-
const callerEmail = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
20
|
-
const { company_id, email, role } = requestBody;
|
|
21
|
-
|
|
22
|
-
if (!callerEmail) {
|
|
23
|
-
return createErrorResponse(401, 'Authentication required');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!company_id || !email || !role) {
|
|
27
|
-
return createErrorResponse(400, 'company_id, email, and role are required');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Verify caller is admin of this company
|
|
31
|
-
const adminQuery = `
|
|
32
|
-
SELECT admin FROM rapport.user_entitlements
|
|
33
|
-
WHERE email_address = $1 AND company_id = $2
|
|
34
|
-
`;
|
|
35
|
-
const adminCheck = await executeQuery(adminQuery, [callerEmail, company_id]);
|
|
36
|
-
|
|
37
|
-
if (adminCheck.rowCount === 0 || !adminCheck.rows[0].admin) {
|
|
38
|
-
return createErrorResponse(403, 'Admin access required to update user roles');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Prevent self-demotion from admin
|
|
42
|
-
if (email === callerEmail && role !== 'admin') {
|
|
43
|
-
return createErrorResponse(400, 'Cannot remove your own admin role');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Update the entitlement
|
|
47
|
-
const isAdmin = role === 'admin';
|
|
48
|
-
const updateQuery = `
|
|
49
|
-
UPDATE rapport.user_entitlements
|
|
50
|
-
SET admin = $1
|
|
51
|
-
WHERE email_address = $2 AND company_id = $3
|
|
52
|
-
RETURNING email_address, admin, member
|
|
53
|
-
`;
|
|
54
|
-
const result = await executeQuery(updateQuery, [isAdmin, email, company_id]);
|
|
55
|
-
|
|
56
|
-
if (result.rowCount === 0) {
|
|
57
|
-
return createErrorResponse(404, `User ${email} not found in this company`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return createSuccessResponse(
|
|
61
|
-
{ Records: result.rows },
|
|
62
|
-
`User ${email} role updated to ${role}`,
|
|
63
|
-
{
|
|
64
|
-
Total_Records: result.rowCount,
|
|
65
|
-
Request_ID,
|
|
66
|
-
Timestamp: new Date().toISOString()
|
|
67
|
-
}
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.error('Handler Error:', error);
|
|
72
|
-
return handleError(error);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
exports.handler = wrapHandler(updateCompanyUser);
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Context Get Handler
|
|
3
|
-
* Retrieves full user context for a scope (invariants, purpose, notes, loops)
|
|
4
|
-
* Used by mobile/iOS apps for session injection
|
|
5
|
-
*
|
|
6
|
-
* GET /api/context?scope=jarvis
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, checkSuperAdmin } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Get user context for mobile injection
|
|
13
|
-
*/
|
|
14
|
-
async function getContext({ queryStringParameters: queryParams = {}, requestContext }) {
|
|
15
|
-
try {
|
|
16
|
-
const Request_ID = requestContext.requestId;
|
|
17
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
18
|
-
|
|
19
|
-
if (!email) {
|
|
20
|
-
return createErrorResponse(401, 'Unauthorized');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Gate to super admins only (internal/beta endpoint)
|
|
24
|
-
await checkSuperAdmin.requireSuperAdmin(email);
|
|
25
|
-
|
|
26
|
-
const scope = queryParams.scope || 'jarvis';
|
|
27
|
-
|
|
28
|
-
// Use the database function for efficient retrieval
|
|
29
|
-
const query = `SELECT rapport.get_user_context($1, $2) as context`;
|
|
30
|
-
const result = await executeQuery(query, [email, scope]);
|
|
31
|
-
|
|
32
|
-
const context = result.rows[0]?.context || {
|
|
33
|
-
invariants: { agent_level: [], relationship_level: [] },
|
|
34
|
-
purpose: null,
|
|
35
|
-
recent_notes: [],
|
|
36
|
-
active_loops: []
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
return createSuccessResponse(
|
|
40
|
-
{
|
|
41
|
-
scope,
|
|
42
|
-
...context
|
|
43
|
-
},
|
|
44
|
-
'Context retrieved successfully',
|
|
45
|
-
{
|
|
46
|
-
Request_ID,
|
|
47
|
-
Timestamp: new Date().toISOString()
|
|
48
|
-
}
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
} catch (error) {
|
|
52
|
-
console.error('Handler Error:', error);
|
|
53
|
-
return handleError(error);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
exports.handler = wrapHandler(getContext);
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Invariants Get Handler
|
|
3
|
-
* Retrieves user invariants for a scope
|
|
4
|
-
*
|
|
5
|
-
* GET /api/context/invariants?scope=jarvis
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, checkSuperAdmin } = require('./helpers');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Get user invariants
|
|
12
|
-
*/
|
|
13
|
-
async function getInvariants({ queryStringParameters: queryParams = {}, requestContext }) {
|
|
14
|
-
try {
|
|
15
|
-
const Request_ID = requestContext.requestId;
|
|
16
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
17
|
-
|
|
18
|
-
if (!email) {
|
|
19
|
-
return createErrorResponse(401, 'Unauthorized');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Gate to super admins only (internal/beta endpoint)
|
|
23
|
-
await checkSuperAdmin.requireSuperAdmin(email);
|
|
24
|
-
|
|
25
|
-
const scope = queryParams.scope || 'global';
|
|
26
|
-
|
|
27
|
-
// Get agent-level invariants (global only)
|
|
28
|
-
const agentQuery = `
|
|
29
|
-
SELECT invariant_text as text, maturity, tier
|
|
30
|
-
FROM rapport.user_invariants
|
|
31
|
-
WHERE email_address = $1
|
|
32
|
-
AND scope = 'global'
|
|
33
|
-
AND level = 'agent'
|
|
34
|
-
AND archived_at IS NULL
|
|
35
|
-
ORDER BY
|
|
36
|
-
CASE tier WHEN 'critical' THEN 1 WHEN 'important' THEN 2 ELSE 3 END,
|
|
37
|
-
created_at
|
|
38
|
-
`;
|
|
39
|
-
const agentResult = await executeQuery(agentQuery, [email]);
|
|
40
|
-
|
|
41
|
-
// Get relationship-level invariants (scope-specific)
|
|
42
|
-
const relationshipQuery = `
|
|
43
|
-
SELECT invariant_text as text, maturity, tier
|
|
44
|
-
FROM rapport.user_invariants
|
|
45
|
-
WHERE email_address = $1
|
|
46
|
-
AND scope = $2
|
|
47
|
-
AND level = 'relationship'
|
|
48
|
-
AND archived_at IS NULL
|
|
49
|
-
ORDER BY
|
|
50
|
-
CASE tier WHEN 'critical' THEN 1 WHEN 'important' THEN 2 ELSE 3 END,
|
|
51
|
-
created_at
|
|
52
|
-
`;
|
|
53
|
-
const relationshipResult = await executeQuery(relationshipQuery, [email, scope]);
|
|
54
|
-
|
|
55
|
-
return createSuccessResponse(
|
|
56
|
-
{
|
|
57
|
-
scope,
|
|
58
|
-
agent_level: agentResult.rows,
|
|
59
|
-
relationship_level: relationshipResult.rows
|
|
60
|
-
},
|
|
61
|
-
`${agentResult.rowCount} agent + ${relationshipResult.rowCount} relationship invariants`,
|
|
62
|
-
{
|
|
63
|
-
Request_ID,
|
|
64
|
-
Timestamp: new Date().toISOString()
|
|
65
|
-
}
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error('Handler Error:', error);
|
|
70
|
-
return handleError(error);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
exports.handler = wrapHandler(getInvariants);
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Loops Get Handler
|
|
3
|
-
* Retrieves active loops for a user
|
|
4
|
-
*
|
|
5
|
-
* GET /api/context/loops
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, checkSuperAdmin } = require('./helpers');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Get active loops
|
|
12
|
-
*/
|
|
13
|
-
async function getLoops({ queryStringParameters: queryParams = {}, requestContext }) {
|
|
14
|
-
try {
|
|
15
|
-
const Request_ID = requestContext.requestId;
|
|
16
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
17
|
-
|
|
18
|
-
if (!email) {
|
|
19
|
-
return createErrorResponse(401, 'Unauthorized');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Gate to super admins only (internal/beta endpoint)
|
|
23
|
-
await checkSuperAdmin.requireSuperAdmin(email);
|
|
24
|
-
|
|
25
|
-
const includeCompleted = queryParams.include_completed === 'true';
|
|
26
|
-
|
|
27
|
-
let query = `
|
|
28
|
-
SELECT
|
|
29
|
-
loop_id,
|
|
30
|
-
project_scope,
|
|
31
|
-
task_description,
|
|
32
|
-
completion_promise,
|
|
33
|
-
max_iterations,
|
|
34
|
-
current_iteration,
|
|
35
|
-
status,
|
|
36
|
-
promise_found,
|
|
37
|
-
started_at,
|
|
38
|
-
updated_at,
|
|
39
|
-
completed_at
|
|
40
|
-
FROM rapport.active_loops
|
|
41
|
-
WHERE email_address = $1
|
|
42
|
-
`;
|
|
43
|
-
|
|
44
|
-
if (!includeCompleted) {
|
|
45
|
-
query += ` AND status = 'active'`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
query += ` ORDER BY started_at DESC LIMIT 20`;
|
|
49
|
-
|
|
50
|
-
const result = await executeQuery(query, [email]);
|
|
51
|
-
|
|
52
|
-
return createSuccessResponse(
|
|
53
|
-
{
|
|
54
|
-
loops: result.rows.map(row => ({
|
|
55
|
-
id: row.loop_id,
|
|
56
|
-
scope: row.project_scope,
|
|
57
|
-
task: row.task_description,
|
|
58
|
-
promise: row.completion_promise,
|
|
59
|
-
iteration: row.current_iteration,
|
|
60
|
-
max_iterations: row.max_iterations,
|
|
61
|
-
status: row.status,
|
|
62
|
-
promise_found: row.promise_found,
|
|
63
|
-
started_at: row.started_at,
|
|
64
|
-
updated_at: row.updated_at,
|
|
65
|
-
completed_at: row.completed_at
|
|
66
|
-
})),
|
|
67
|
-
count: result.rowCount
|
|
68
|
-
},
|
|
69
|
-
`${result.rowCount} loops`,
|
|
70
|
-
{
|
|
71
|
-
Request_ID,
|
|
72
|
-
Timestamp: new Date().toISOString()
|
|
73
|
-
}
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
} catch (error) {
|
|
77
|
-
console.error('Handler Error:', error);
|
|
78
|
-
return handleError(error);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
exports.handler = wrapHandler(getLoops);
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notes Create Handler
|
|
3
|
-
* Creates a new context note from mobile/iOS
|
|
4
|
-
*
|
|
5
|
-
* POST /api/context/notes
|
|
6
|
-
* Body: { content: "...", scope: "jarvis", tags: ["mobile"] }
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, handleError, checkSuperAdmin } = require('./helpers');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Create context note
|
|
13
|
-
*/
|
|
14
|
-
async function createNote({ body, requestContext }) {
|
|
15
|
-
try {
|
|
16
|
-
const Request_ID = requestContext.requestId;
|
|
17
|
-
const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
|
|
18
|
-
|
|
19
|
-
if (!email) {
|
|
20
|
-
return createErrorResponse(401, 'Unauthorized');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Gate to super admins only (internal/beta endpoint)
|
|
24
|
-
await checkSuperAdmin.requireSuperAdmin(email);
|
|
25
|
-
|
|
26
|
-
const data = typeof body === 'string' ? JSON.parse(body) : body;
|
|
27
|
-
|
|
28
|
-
if (!data.content || data.content.trim() === '') {
|
|
29
|
-
return createErrorResponse(400, 'Content is required');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const scope = data.scope || 'global';
|
|
33
|
-
const tags = data.tags || [];
|
|
34
|
-
const source = data.source || 'mobile';
|
|
35
|
-
const metadata = data.metadata || {};
|
|
36
|
-
|
|
37
|
-
const query = `
|
|
38
|
-
INSERT INTO rapport.context_notes
|
|
39
|
-
(email_address, scope, content, tags, source, metadata)
|
|
40
|
-
VALUES ($1, $2, $3, $4, $5, $6)
|
|
41
|
-
RETURNING note_id, created_at
|
|
42
|
-
`;
|
|
43
|
-
|
|
44
|
-
const result = await executeQuery(query, [
|
|
45
|
-
email,
|
|
46
|
-
scope,
|
|
47
|
-
data.content.trim(),
|
|
48
|
-
JSON.stringify(tags),
|
|
49
|
-
source,
|
|
50
|
-
JSON.stringify(metadata)
|
|
51
|
-
]);
|
|
52
|
-
|
|
53
|
-
const note = result.rows[0];
|
|
54
|
-
|
|
55
|
-
return createSuccessResponse(
|
|
56
|
-
{
|
|
57
|
-
note_id: note.note_id,
|
|
58
|
-
scope,
|
|
59
|
-
created_at: note.created_at
|
|
60
|
-
},
|
|
61
|
-
`Note created: ${note.note_id}`,
|
|
62
|
-
{
|
|
63
|
-
Request_ID,
|
|
64
|
-
Timestamp: new Date().toISOString()
|
|
65
|
-
}
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error('Handler Error:', error);
|
|
70
|
-
return handleError(error);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
exports.handler = wrapHandler(createNote);
|