@nordsym/apiclaw 2.2.0 → 2.3.0

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 (176) hide show
  1. package/README.md +15 -2
  2. package/dist/bin-http.js +0 -0
  3. package/dist/bin.bundled.js +79288 -0
  4. package/dist/gateway-client.d.ts.map +1 -1
  5. package/dist/gateway-client.js +24 -2
  6. package/dist/gateway-client.js.map +1 -1
  7. package/dist/index.bundled.js +61263 -0
  8. package/dist/index.js +2 -2
  9. package/dist/index.js.map +1 -1
  10. package/package.json +7 -2
  11. package/.claude/settings.local.json +0 -13
  12. package/.env.prod +0 -1
  13. package/apiclaw-README.md +0 -494
  14. package/convex/_generated/api.d.ts +0 -145
  15. package/convex/_generated/api.js +0 -23
  16. package/convex/_generated/dataModel.d.ts +0 -60
  17. package/convex/_generated/server.d.ts +0 -143
  18. package/convex/_generated/server.js +0 -93
  19. package/convex/_listWorkspaces.ts +0 -13
  20. package/convex/adminActivate.ts +0 -53
  21. package/convex/adminStats.ts +0 -306
  22. package/convex/agents.ts +0 -939
  23. package/convex/analytics.ts +0 -187
  24. package/convex/apiKeys.ts +0 -220
  25. package/convex/backfillAnalytics.ts +0 -272
  26. package/convex/backfillSearchLogs.ts +0 -35
  27. package/convex/billing.ts +0 -834
  28. package/convex/capabilities.ts +0 -157
  29. package/convex/chains.ts +0 -1318
  30. package/convex/credits.ts +0 -211
  31. package/convex/crons.ts +0 -65
  32. package/convex/debugFilestackLogs.ts +0 -16
  33. package/convex/debugGetToken.ts +0 -18
  34. package/convex/directCall.ts +0 -713
  35. package/convex/earnProgress.ts +0 -753
  36. package/convex/email.ts +0 -329
  37. package/convex/feedback.ts +0 -265
  38. package/convex/funnel.ts +0 -431
  39. package/convex/guards.ts +0 -174
  40. package/convex/http.ts +0 -3756
  41. package/convex/inbound.ts +0 -32
  42. package/convex/logs.ts +0 -701
  43. package/convex/migrateFilestack.ts +0 -81
  44. package/convex/migratePartnersProd.ts +0 -174
  45. package/convex/migratePratham.ts +0 -126
  46. package/convex/migrateProviderWorkspaces.ts +0 -175
  47. package/convex/mou.ts +0 -91
  48. package/convex/nurture.ts +0 -355
  49. package/convex/providerKeys.ts +0 -289
  50. package/convex/providers.ts +0 -1135
  51. package/convex/purchases.ts +0 -183
  52. package/convex/ratelimit.ts +0 -104
  53. package/convex/schema.ts +0 -926
  54. package/convex/searchLogs.ts +0 -265
  55. package/convex/seedAPILayerAPIs.ts +0 -191
  56. package/convex/seedDirectCallConfigs.ts +0 -336
  57. package/convex/seedPratham.ts +0 -149
  58. package/convex/spendAlerts.ts +0 -442
  59. package/convex/stripeActions.ts +0 -607
  60. package/convex/teams.ts +0 -243
  61. package/convex/telemetry.ts +0 -81
  62. package/convex/tsconfig.json +0 -25
  63. package/convex/updateAPIStatus.ts +0 -44
  64. package/convex/usage.ts +0 -260
  65. package/convex/usageReports.ts +0 -357
  66. package/convex/waitlist.ts +0 -55
  67. package/convex/webhooks.ts +0 -494
  68. package/convex/workspaceSettings.ts +0 -143
  69. package/convex/workspaces.ts +0 -1331
  70. package/convex.json +0 -3
  71. package/direct-test.mjs +0 -51
  72. package/email-templates/filestack-provider-outreach.html +0 -162
  73. package/email-templates/partnership-template.html +0 -116
  74. package/email-templates/pratham-draft-preview.txt +0 -57
  75. package/email-templates/pratham-partnership-draft.html +0 -141
  76. package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
  77. package/reports/pipeline/PIPELINE-REPORT.json +0 -153
  78. package/reports/pipeline/acquire_apisguru.json +0 -17
  79. package/reports/pipeline/capabilities.json +0 -38
  80. package/reports/pipeline/discover_azure_recursive.json +0 -1551
  81. package/reports/pipeline/discover_github.json +0 -25
  82. package/reports/pipeline/discover_github_repos.json +0 -49
  83. package/reports/pipeline/discover_swaggerhub.json +0 -24
  84. package/reports/pipeline/discover_well_known.json +0 -23
  85. package/reports/pipeline/fetch_specs.json +0 -19
  86. package/reports/pipeline/generate_providers.json +0 -14
  87. package/reports/pipeline/match_registry.json +0 -11
  88. package/reports/pipeline/parse_specs.json +0 -17
  89. package/reports/pipeline/promote_candidates.json +0 -34
  90. package/reports/pipeline/validate.json +0 -30
  91. package/reports/pipeline/validate_smoke_details.json +0 -3835
  92. package/reports/session-report-2026-04-05.html +0 -433
  93. package/seed-apis-direct.mjs +0 -106
  94. package/src/access-control.ts +0 -174
  95. package/src/adapters/base.ts +0 -364
  96. package/src/adapters/claude-desktop.ts +0 -41
  97. package/src/adapters/cline.ts +0 -88
  98. package/src/adapters/continue.ts +0 -91
  99. package/src/adapters/cursor.ts +0 -43
  100. package/src/adapters/custom.ts +0 -188
  101. package/src/adapters/detect.ts +0 -202
  102. package/src/adapters/index.ts +0 -47
  103. package/src/adapters/windsurf.ts +0 -44
  104. package/src/bin-http.ts +0 -45
  105. package/src/bin.ts +0 -34
  106. package/src/capability-router.ts +0 -331
  107. package/src/chainExecutor.ts +0 -730
  108. package/src/chainResolver.test.ts +0 -246
  109. package/src/chainResolver.ts +0 -658
  110. package/src/cli/commands/demo.ts +0 -109
  111. package/src/cli/commands/doctor.ts +0 -435
  112. package/src/cli/commands/index.ts +0 -9
  113. package/src/cli/commands/login.ts +0 -203
  114. package/src/cli/commands/mcp-install.ts +0 -373
  115. package/src/cli/commands/restore.ts +0 -333
  116. package/src/cli/commands/setup.ts +0 -297
  117. package/src/cli/commands/uninstall.ts +0 -240
  118. package/src/cli/index.ts +0 -148
  119. package/src/cli.ts +0 -370
  120. package/src/confirmation.ts +0 -296
  121. package/src/credentials.ts +0 -455
  122. package/src/credits.ts +0 -329
  123. package/src/crypto.ts +0 -75
  124. package/src/discovery.ts +0 -568
  125. package/src/enterprise/env.ts +0 -156
  126. package/src/enterprise/index.ts +0 -7
  127. package/src/enterprise/script-generator.ts +0 -481
  128. package/src/execute-dynamic.ts +0 -617
  129. package/src/execute.ts +0 -2386
  130. package/src/funnel-client.ts +0 -168
  131. package/src/funnel.test.ts +0 -187
  132. package/src/gateway-client.ts +0 -192
  133. package/src/hivr-whitelist.ts +0 -110
  134. package/src/http-api.ts +0 -286
  135. package/src/http-server-minimal.ts +0 -154
  136. package/src/index.ts +0 -2702
  137. package/src/intelligent-gateway.ts +0 -339
  138. package/src/mcp-analytics.ts +0 -156
  139. package/src/metered.ts +0 -149
  140. package/src/open-apis-generated.ts +0 -157
  141. package/src/open-apis.ts +0 -558
  142. package/src/postinstall.ts +0 -40
  143. package/src/product-whitelist.ts +0 -246
  144. package/src/proxy.ts +0 -36
  145. package/src/registration-guard.ts +0 -117
  146. package/src/session.ts +0 -129
  147. package/src/stripe.ts +0 -497
  148. package/src/telemetry.ts +0 -71
  149. package/src/test.ts +0 -135
  150. package/src/types/convex-api.d.ts +0 -20
  151. package/src/types/convex-api.ts +0 -21
  152. package/src/types.ts +0 -109
  153. package/src/ui/colors.ts +0 -219
  154. package/src/ui/errors.ts +0 -394
  155. package/src/ui/index.ts +0 -17
  156. package/src/ui/prompts.ts +0 -390
  157. package/src/ui/spinner.ts +0 -325
  158. package/src/utils/backup.ts +0 -224
  159. package/src/utils/config.ts +0 -318
  160. package/src/utils/os.ts +0 -124
  161. package/src/utils/paths.ts +0 -203
  162. package/src/webhook.ts +0 -107
  163. package/test-10-working.cjs +0 -97
  164. package/test-14-final.cjs +0 -96
  165. package/test-actual-handlers.ts +0 -92
  166. package/test-apilayer-all-14.ts +0 -249
  167. package/test-apilayer-fixed.ts +0 -248
  168. package/test-direct-endpoints.ts +0 -174
  169. package/test-exact-endpoints.ts +0 -144
  170. package/test-final.ts +0 -83
  171. package/test-full-routing.ts +0 -100
  172. package/test-handlers-correct.ts +0 -217
  173. package/test-numverify-key.ts +0 -41
  174. package/test-via-handlers.ts +0 -92
  175. package/test-worldnews.mjs +0 -26
  176. package/tsconfig.json +0 -20
@@ -1,753 +0,0 @@
1
- import { v } from "convex/values";
2
- import { mutation, query, internalMutation } from "./_generated/server";
3
-
4
- // ============================================
5
- // EARN REWARDS CONFIGURATION
6
- // ============================================
7
-
8
- const EARN_REWARDS = {
9
- firstDirectCall: 15,
10
- apisUsedComplete: 10, // 3 unique APIs
11
- agentListed: 10,
12
- apiListed: 10,
13
- byokSetup: 5,
14
- githubStarred: 10,
15
- twitterFollowed: 5,
16
- referral: 0, // DISABLED (2026-03-01): Risk of abuse
17
- } as const;
18
-
19
- const APIS_REQUIRED_FOR_COMPLETE = 3;
20
-
21
- // ============================================
22
- // HELPER: Get or Create Progress
23
- // ============================================
24
-
25
- async function getOrCreateProgress(
26
- ctx: any,
27
- workspaceId: any
28
- ): Promise<{ _id: any; [key: string]: any }> {
29
- const existing = await ctx.db
30
- .query("earnProgress")
31
- .withIndex("by_workspaceId", (q: any) => q.eq("workspaceId", workspaceId))
32
- .first();
33
-
34
- if (existing) {
35
- return existing;
36
- }
37
-
38
- const now = Date.now();
39
- const id = await ctx.db.insert("earnProgress", {
40
- workspaceId,
41
- firstDirectCall: false,
42
- apisUsed: [],
43
- apisUsedComplete: false,
44
- agentListed: false,
45
- apiListed: false,
46
- byokSetup: false,
47
- githubStarred: false,
48
- twitterFollowed: false,
49
- referralCount: 0,
50
- totalEarned: 0,
51
- createdAt: now,
52
- updatedAt: now,
53
- });
54
-
55
- return (await ctx.db.get(id))!;
56
- }
57
-
58
- // Calculate total earned based on progress
59
- function calculateTotalEarned(progress: any): number {
60
- let total = 0;
61
-
62
- if (progress.firstDirectCall) total += EARN_REWARDS.firstDirectCall;
63
- if (progress.apisUsedComplete) total += EARN_REWARDS.apisUsedComplete;
64
- if (progress.agentListed) total += EARN_REWARDS.agentListed;
65
- if (progress.apiListed) total += EARN_REWARDS.apiListed;
66
- if (progress.byokSetup) total += EARN_REWARDS.byokSetup;
67
- if (progress.githubStarred) total += EARN_REWARDS.githubStarred;
68
- if (progress.twitterFollowed) total += EARN_REWARDS.twitterFollowed;
69
- total += progress.referralCount * EARN_REWARDS.referral;
70
-
71
- return total;
72
- }
73
-
74
- // ============================================
75
- // QUERIES
76
- // ============================================
77
-
78
- /**
79
- * Get earn progress for a workspace (via session token)
80
- */
81
- export const getEarnProgress = query({
82
- args: {
83
- token: v.string(),
84
- },
85
- handler: async (ctx, args) => {
86
- // Validate session
87
- const session = await ctx.db
88
- .query("agentSessions")
89
- .withIndex("by_sessionToken", (q) => q.eq("sessionToken", args.token))
90
- .first();
91
-
92
- if (!session) {
93
- return null;
94
- }
95
-
96
- const progress = await ctx.db
97
- .query("earnProgress")
98
- .withIndex("by_workspaceId", (q) => q.eq("workspaceId", session.workspaceId))
99
- .first();
100
-
101
- if (!progress) {
102
- // Return default empty progress
103
- return {
104
- firstDirectCall: false,
105
- apisUsed: [],
106
- apisUsedComplete: false,
107
- apisUsedCount: 0,
108
- apisUsedTarget: APIS_REQUIRED_FOR_COMPLETE,
109
- agentListed: false,
110
- apiListed: false,
111
- byokSetup: false,
112
- githubStarred: false,
113
- twitterFollowed: false,
114
- referralCount: 0,
115
- totalEarned: 0,
116
- maxEarnable: calculateMaxEarnable(),
117
- rewards: EARN_REWARDS,
118
- };
119
- }
120
-
121
- return {
122
- firstDirectCall: progress.firstDirectCall,
123
- firstDirectCallAt: progress.firstDirectCallAt,
124
- apisUsed: progress.apisUsed,
125
- apisUsedComplete: progress.apisUsedComplete,
126
- apisUsedCount: progress.apisUsed.length,
127
- apisUsedTarget: APIS_REQUIRED_FOR_COMPLETE,
128
- agentListed: progress.agentListed,
129
- agentListedAt: progress.agentListedAt,
130
- apiListed: progress.apiListed,
131
- apiListedAt: progress.apiListedAt,
132
- byokSetup: progress.byokSetup,
133
- byokSetupAt: progress.byokSetupAt,
134
- githubStarred: progress.githubStarred,
135
- githubStarredAt: progress.githubStarredAt,
136
- twitterFollowed: progress.twitterFollowed,
137
- twitterFollowedAt: progress.twitterFollowedAt,
138
- referralCount: progress.referralCount,
139
- totalEarned: progress.totalEarned,
140
- maxEarnable: calculateMaxEarnable(),
141
- rewards: EARN_REWARDS,
142
- };
143
- },
144
- });
145
-
146
- /**
147
- * Get earn progress by workspace ID (internal use)
148
- */
149
- export const getByWorkspaceId = query({
150
- args: {
151
- workspaceId: v.id("workspaces"),
152
- },
153
- handler: async (ctx, args) => {
154
- const progress = await ctx.db
155
- .query("earnProgress")
156
- .withIndex("by_workspaceId", (q) => q.eq("workspaceId", args.workspaceId))
157
- .first();
158
-
159
- return progress;
160
- },
161
- });
162
-
163
- // ============================================
164
- // REFERRAL SYSTEM
165
- // ============================================
166
-
167
- /**
168
- * Generate a unique referral code (CLAW-XXXXXX format)
169
- */
170
- function generateReferralCode(): string {
171
- const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
172
- let code = "";
173
- for (let i = 0; i < 6; i++) {
174
- code += chars.charAt(Math.floor(Math.random() * chars.length));
175
- }
176
- return `CLAW-${code}`;
177
- }
178
-
179
- /**
180
- * Ensure workspace has a referral code (called on first earn page visit)
181
- */
182
- export const ensureReferralCode = mutation({
183
- args: { token: v.string() },
184
- handler: async (ctx, { token }) => {
185
- const session = await ctx.db
186
- .query("agentSessions")
187
- .withIndex("by_sessionToken", (q) => q.eq("sessionToken", token))
188
- .first();
189
-
190
- if (!session) {
191
- throw new Error("Invalid session");
192
- }
193
-
194
- const workspace = await ctx.db.get(session.workspaceId);
195
- if (!workspace) {
196
- throw new Error("Workspace not found");
197
- }
198
-
199
- // Already has a code
200
- if (workspace.referralCode) {
201
- return { referralCode: workspace.referralCode };
202
- }
203
-
204
- // Generate unique code (check for collisions)
205
- let referralCode: string;
206
- let attempts = 0;
207
- do {
208
- referralCode = generateReferralCode();
209
- const existing = await ctx.db
210
- .query("workspaces")
211
- .withIndex("by_referralCode", (q) => q.eq("referralCode", referralCode))
212
- .first();
213
- if (!existing) break;
214
- attempts++;
215
- } while (attempts < 10);
216
-
217
- // Update workspace with referral code
218
- await ctx.db.patch(workspace._id, {
219
- referralCode,
220
- updatedAt: Date.now(),
221
- });
222
-
223
- return { referralCode };
224
- },
225
- });
226
-
227
- /**
228
- * Get earn progress with referral info (for UI)
229
- */
230
- export const getByToken = query({
231
- args: { token: v.string() },
232
- handler: async (ctx, { token }) => {
233
- const session = await ctx.db
234
- .query("agentSessions")
235
- .withIndex("by_sessionToken", (q) => q.eq("sessionToken", token))
236
- .first();
237
-
238
- if (!session) return null;
239
-
240
- const workspace = await ctx.db.get(session.workspaceId);
241
- if (!workspace) return null;
242
-
243
- const progress = await ctx.db
244
- .query("earnProgress")
245
- .withIndex("by_workspaceId", (q) => q.eq("workspaceId", session.workspaceId))
246
- .first();
247
-
248
- const defaultProgress = {
249
- firstDirectCall: false,
250
- firstDirectCallAt: null,
251
- apisUsed: [] as string[],
252
- apisUsedComplete: false,
253
- agentListed: false,
254
- agentListedAt: null,
255
- apiListed: false,
256
- apiListedAt: null,
257
- byokSetup: false,
258
- byokSetupAt: null,
259
- githubStarred: false,
260
- githubStarredAt: null,
261
- twitterFollowed: false,
262
- twitterFollowedAt: null,
263
- referralCount: 0,
264
- totalEarned: 0,
265
- };
266
-
267
- const earnData = progress || defaultProgress;
268
-
269
- return {
270
- ...earnData,
271
- referralCode: workspace.referralCode || null,
272
- maxEarnable: calculateMaxEarnable(),
273
- rewards: EARN_REWARDS,
274
- };
275
- },
276
- });
277
-
278
- /**
279
- * Get referral stats for a workspace
280
- */
281
- export const getReferralStats = query({
282
- args: { token: v.string() },
283
- handler: async (ctx, { token }) => {
284
- const session = await ctx.db
285
- .query("agentSessions")
286
- .withIndex("by_sessionToken", (q) => q.eq("sessionToken", token))
287
- .first();
288
-
289
- if (!session) return null;
290
-
291
- const workspace = await ctx.db.get(session.workspaceId);
292
- if (!workspace) return null;
293
-
294
- // Count users referred by this workspace
295
- const referredUsers = await ctx.db
296
- .query("workspaces")
297
- .filter((q) => q.eq(q.field("referredBy"), workspace._id))
298
- .collect();
299
-
300
- return {
301
- referralCode: workspace.referralCode,
302
- referralCount: referredUsers.length,
303
- referralUrl: workspace.referralCode
304
- ? `https://apiclaw.com/join?ref=${workspace.referralCode}`
305
- : null,
306
- callsEarned: referredUsers.length * EARN_REWARDS.referral,
307
- };
308
- },
309
- });
310
-
311
- function calculateMaxEarnable(): number {
312
- return (
313
- EARN_REWARDS.firstDirectCall +
314
- EARN_REWARDS.apisUsedComplete +
315
- EARN_REWARDS.agentListed +
316
- EARN_REWARDS.apiListed +
317
- EARN_REWARDS.byokSetup +
318
- EARN_REWARDS.githubStarred +
319
- EARN_REWARDS.twitterFollowed
320
- );
321
- }
322
-
323
- // ============================================
324
- // MUTATIONS - Usage Tasks
325
- // ============================================
326
-
327
- /**
328
- * Mark first direct call as complete
329
- */
330
- export const markFirstDirectCall = mutation({
331
- args: {
332
- workspaceId: v.id("workspaces"),
333
- },
334
- handler: async (ctx, args) => {
335
- const progress = await getOrCreateProgress(ctx, args.workspaceId);
336
-
337
- if (progress.firstDirectCall) {
338
- return { success: true, alreadyClaimed: true, earned: 0 };
339
- }
340
-
341
- const now = Date.now();
342
- const newTotal = calculateTotalEarned({
343
- ...progress,
344
- firstDirectCall: true,
345
- });
346
-
347
- await ctx.db.patch(progress._id, {
348
- firstDirectCall: true,
349
- firstDirectCallAt: now,
350
- totalEarned: newTotal,
351
- updatedAt: now,
352
- });
353
-
354
- // Add earned calls to workspace usage limit
355
- await addEarnedCallsToWorkspace(ctx, args.workspaceId, EARN_REWARDS.firstDirectCall);
356
-
357
- return {
358
- success: true,
359
- alreadyClaimed: false,
360
- earned: EARN_REWARDS.firstDirectCall,
361
- totalEarned: newTotal,
362
- };
363
- },
364
- });
365
-
366
- /**
367
- * Track unique APIs used
368
- */
369
- export const trackApiUsed = mutation({
370
- args: {
371
- workspaceId: v.id("workspaces"),
372
- apiId: v.string(), // Format: "provider:action" or just "provider"
373
- },
374
- handler: async (ctx, args) => {
375
- const progress = await getOrCreateProgress(ctx, args.workspaceId);
376
-
377
- // If already complete, no need to track
378
- if (progress.apisUsedComplete) {
379
- return {
380
- success: true,
381
- alreadyComplete: true,
382
- apisUsedCount: progress.apisUsed.length,
383
- earned: 0,
384
- };
385
- }
386
-
387
- // Check if this API was already tracked
388
- if (progress.apisUsed.includes(args.apiId)) {
389
- return {
390
- success: true,
391
- alreadyTracked: true,
392
- apisUsedCount: progress.apisUsed.length,
393
- earned: 0,
394
- };
395
- }
396
-
397
- const newApisUsed = [...progress.apisUsed, args.apiId];
398
- const isNowComplete = newApisUsed.length >= APIS_REQUIRED_FOR_COMPLETE;
399
- const now = Date.now();
400
-
401
- const newTotal = calculateTotalEarned({
402
- ...progress,
403
- apisUsed: newApisUsed,
404
- apisUsedComplete: isNowComplete,
405
- });
406
-
407
- await ctx.db.patch(progress._id, {
408
- apisUsed: newApisUsed,
409
- apisUsedComplete: isNowComplete,
410
- totalEarned: newTotal,
411
- updatedAt: now,
412
- });
413
-
414
- // If just completed, add earned calls
415
- if (isNowComplete && !progress.apisUsedComplete) {
416
- await addEarnedCallsToWorkspace(ctx, args.workspaceId, EARN_REWARDS.apisUsedComplete);
417
- }
418
-
419
- return {
420
- success: true,
421
- apisUsedCount: newApisUsed.length,
422
- isComplete: isNowComplete,
423
- earned: isNowComplete && !progress.apisUsedComplete ? EARN_REWARDS.apisUsedComplete : 0,
424
- totalEarned: newTotal,
425
- };
426
- },
427
- });
428
-
429
- /**
430
- * Mark agent listed
431
- */
432
- export const markAgentListed = mutation({
433
- args: {
434
- workspaceId: v.id("workspaces"),
435
- },
436
- handler: async (ctx, args) => {
437
- const progress = await getOrCreateProgress(ctx, args.workspaceId);
438
-
439
- if (progress.agentListed) {
440
- return { success: true, alreadyClaimed: true, earned: 0 };
441
- }
442
-
443
- const now = Date.now();
444
- const newTotal = calculateTotalEarned({
445
- ...progress,
446
- agentListed: true,
447
- });
448
-
449
- await ctx.db.patch(progress._id, {
450
- agentListed: true,
451
- agentListedAt: now,
452
- totalEarned: newTotal,
453
- updatedAt: now,
454
- });
455
-
456
- await addEarnedCallsToWorkspace(ctx, args.workspaceId, EARN_REWARDS.agentListed);
457
-
458
- return {
459
- success: true,
460
- alreadyClaimed: false,
461
- earned: EARN_REWARDS.agentListed,
462
- totalEarned: newTotal,
463
- };
464
- },
465
- });
466
-
467
- /**
468
- * Mark API listed
469
- */
470
- export const markApiListed = mutation({
471
- args: {
472
- workspaceId: v.id("workspaces"),
473
- },
474
- handler: async (ctx, args) => {
475
- const progress = await getOrCreateProgress(ctx, args.workspaceId);
476
-
477
- if (progress.apiListed) {
478
- return { success: true, alreadyClaimed: true, earned: 0 };
479
- }
480
-
481
- const now = Date.now();
482
- const newTotal = calculateTotalEarned({
483
- ...progress,
484
- apiListed: true,
485
- });
486
-
487
- await ctx.db.patch(progress._id, {
488
- apiListed: true,
489
- apiListedAt: now,
490
- totalEarned: newTotal,
491
- updatedAt: now,
492
- });
493
-
494
- await addEarnedCallsToWorkspace(ctx, args.workspaceId, EARN_REWARDS.apiListed);
495
-
496
- return {
497
- success: true,
498
- alreadyClaimed: false,
499
- earned: EARN_REWARDS.apiListed,
500
- totalEarned: newTotal,
501
- };
502
- },
503
- });
504
-
505
- /**
506
- * Mark BYOK setup complete
507
- */
508
- export const markByokSetup = mutation({
509
- args: {
510
- workspaceId: v.id("workspaces"),
511
- },
512
- handler: async (ctx, args) => {
513
- const progress = await getOrCreateProgress(ctx, args.workspaceId);
514
-
515
- if (progress.byokSetup) {
516
- return { success: true, alreadyClaimed: true, earned: 0 };
517
- }
518
-
519
- const now = Date.now();
520
- const newTotal = calculateTotalEarned({
521
- ...progress,
522
- byokSetup: true,
523
- });
524
-
525
- await ctx.db.patch(progress._id, {
526
- byokSetup: true,
527
- byokSetupAt: now,
528
- totalEarned: newTotal,
529
- updatedAt: now,
530
- });
531
-
532
- await addEarnedCallsToWorkspace(ctx, args.workspaceId, EARN_REWARDS.byokSetup);
533
-
534
- return {
535
- success: true,
536
- alreadyClaimed: false,
537
- earned: EARN_REWARDS.byokSetup,
538
- totalEarned: newTotal,
539
- };
540
- },
541
- });
542
-
543
- // ============================================
544
- // MUTATIONS - Growth Tasks
545
- // ============================================
546
-
547
- /**
548
- * Claim GitHub star reward
549
- */
550
- export const claimGithub = mutation({
551
- args: {
552
- token: v.string(),
553
- },
554
- handler: async (ctx, args) => {
555
- // Validate session
556
- const session = await ctx.db
557
- .query("agentSessions")
558
- .withIndex("by_sessionToken", (q) => q.eq("sessionToken", args.token))
559
- .first();
560
-
561
- if (!session) {
562
- return { success: false, error: "Invalid session" };
563
- }
564
-
565
- const progress = await getOrCreateProgress(ctx, session.workspaceId);
566
-
567
- if (progress.githubStarred) {
568
- return { success: true, alreadyClaimed: true, earned: 0 };
569
- }
570
-
571
- const now = Date.now();
572
- const newTotal = calculateTotalEarned({
573
- ...progress,
574
- githubStarred: true,
575
- });
576
-
577
- await ctx.db.patch(progress._id, {
578
- githubStarred: true,
579
- githubStarredAt: now,
580
- totalEarned: newTotal,
581
- updatedAt: now,
582
- });
583
-
584
- await addEarnedCallsToWorkspace(ctx, session.workspaceId, EARN_REWARDS.githubStarred);
585
-
586
- return {
587
- success: true,
588
- alreadyClaimed: false,
589
- earned: EARN_REWARDS.githubStarred,
590
- totalEarned: newTotal,
591
- };
592
- },
593
- });
594
-
595
- /**
596
- * Claim Twitter follow reward
597
- */
598
- export const claimTwitter = mutation({
599
- args: {
600
- token: v.string(),
601
- },
602
- handler: async (ctx, args) => {
603
- // Validate session
604
- const session = await ctx.db
605
- .query("agentSessions")
606
- .withIndex("by_sessionToken", (q) => q.eq("sessionToken", args.token))
607
- .first();
608
-
609
- if (!session) {
610
- return { success: false, error: "Invalid session" };
611
- }
612
-
613
- const progress = await getOrCreateProgress(ctx, session.workspaceId);
614
-
615
- if (progress.twitterFollowed) {
616
- return { success: true, alreadyClaimed: true, earned: 0 };
617
- }
618
-
619
- const now = Date.now();
620
- const newTotal = calculateTotalEarned({
621
- ...progress,
622
- twitterFollowed: true,
623
- });
624
-
625
- await ctx.db.patch(progress._id, {
626
- twitterFollowed: true,
627
- twitterFollowedAt: now,
628
- totalEarned: newTotal,
629
- updatedAt: now,
630
- });
631
-
632
- await addEarnedCallsToWorkspace(ctx, session.workspaceId, EARN_REWARDS.twitterFollowed);
633
-
634
- return {
635
- success: true,
636
- alreadyClaimed: false,
637
- earned: EARN_REWARDS.twitterFollowed,
638
- totalEarned: newTotal,
639
- };
640
- },
641
- });
642
-
643
- /**
644
- * Increment referral count (called when someone signs up with referral code)
645
- */
646
- export const incrementReferral = mutation({
647
- args: {
648
- workspaceId: v.id("workspaces"),
649
- },
650
- handler: async (ctx, args) => {
651
- const progress = await getOrCreateProgress(ctx, args.workspaceId);
652
-
653
- const newReferralCount = progress.referralCount + 1;
654
- const now = Date.now();
655
-
656
- const newTotal = calculateTotalEarned({
657
- ...progress,
658
- referralCount: newReferralCount,
659
- });
660
-
661
- await ctx.db.patch(progress._id, {
662
- referralCount: newReferralCount,
663
- totalEarned: newTotal,
664
- updatedAt: now,
665
- });
666
-
667
- await addEarnedCallsToWorkspace(ctx, args.workspaceId, EARN_REWARDS.referral);
668
-
669
- return {
670
- success: true,
671
- referralCount: newReferralCount,
672
- earned: EARN_REWARDS.referral,
673
- totalEarned: newTotal,
674
- };
675
- },
676
- });
677
-
678
- // ============================================
679
- // INTERNAL HELPER
680
- // ============================================
681
-
682
- /**
683
- * Add earned API calls to workspace usage limit
684
- */
685
- async function addEarnedCallsToWorkspace(
686
- ctx: any,
687
- workspaceId: any,
688
- calls: number
689
- ): Promise<void> {
690
- const workspace = await ctx.db.get(workspaceId);
691
- if (!workspace) return;
692
-
693
- // Add to usage limit (not usage count - they earn MORE calls)
694
- await ctx.db.patch(workspaceId, {
695
- usageLimit: workspace.usageLimit + calls,
696
- updatedAt: Date.now(),
697
- });
698
- }
699
-
700
- // ============================================
701
- // INTERNAL MUTATIONS (for integration)
702
- // ============================================
703
-
704
- /**
705
- * Internal: Track API call for earn progress
706
- * Called from usage tracking after successful API call
707
- */
708
- export const trackApiCallInternal = internalMutation({
709
- args: {
710
- workspaceId: v.id("workspaces"),
711
- provider: v.string(),
712
- action: v.string(),
713
- },
714
- handler: async (ctx, args) => {
715
- const progress = await getOrCreateProgress(ctx, args.workspaceId);
716
- const apiId = `${args.provider}:${args.action}`;
717
- const now = Date.now();
718
-
719
- const updates: Record<string, any> = { updatedAt: now };
720
- let earnedCalls = 0;
721
-
722
- // Check first direct call
723
- if (!progress.firstDirectCall) {
724
- updates.firstDirectCall = true;
725
- updates.firstDirectCallAt = now;
726
- earnedCalls += EARN_REWARDS.firstDirectCall;
727
- }
728
-
729
- // Track unique APIs
730
- if (!progress.apisUsedComplete && !progress.apisUsed.includes(apiId)) {
731
- const newApisUsed = [...progress.apisUsed, apiId];
732
- updates.apisUsed = newApisUsed;
733
-
734
- if (newApisUsed.length >= APIS_REQUIRED_FOR_COMPLETE) {
735
- updates.apisUsedComplete = true;
736
- earnedCalls += EARN_REWARDS.apisUsedComplete;
737
- }
738
- }
739
-
740
- // Only update if there are changes
741
- if (Object.keys(updates).length > 1) {
742
- updates.totalEarned = calculateTotalEarned({ ...progress, ...updates });
743
- await ctx.db.patch(progress._id, updates);
744
-
745
- // Add earned calls to workspace
746
- if (earnedCalls > 0) {
747
- await addEarnedCallsToWorkspace(ctx, args.workspaceId, earnedCalls);
748
- }
749
- }
750
-
751
- return { earnedCalls };
752
- },
753
- });