@nordsym/apiclaw 2.1.0 → 2.2.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 (185) 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/funnel-client.d.ts +24 -0
  5. package/dist/funnel-client.d.ts.map +1 -0
  6. package/dist/funnel-client.js +131 -0
  7. package/dist/funnel-client.js.map +1 -0
  8. package/dist/funnel.test.d.ts +2 -0
  9. package/dist/funnel.test.d.ts.map +1 -0
  10. package/dist/funnel.test.js +145 -0
  11. package/dist/funnel.test.js.map +1 -0
  12. package/dist/gateway-client.d.ts.map +1 -1
  13. package/dist/gateway-client.js +24 -2
  14. package/dist/gateway-client.js.map +1 -1
  15. package/dist/index.bundled.js +61263 -0
  16. package/dist/index.js +161 -74
  17. package/dist/index.js.map +1 -1
  18. package/dist/postinstall.d.ts +0 -5
  19. package/dist/postinstall.d.ts.map +1 -1
  20. package/dist/postinstall.js +24 -3
  21. package/dist/postinstall.js.map +1 -1
  22. package/dist/registration-guard.d.ts +29 -0
  23. package/dist/registration-guard.d.ts.map +1 -0
  24. package/dist/registration-guard.js +87 -0
  25. package/dist/registration-guard.js.map +1 -0
  26. package/package.json +7 -2
  27. package/.claude/settings.local.json +0 -9
  28. package/.env.prod +0 -1
  29. package/apiclaw-README.md +0 -494
  30. package/convex/_generated/api.d.ts +0 -137
  31. package/convex/_generated/api.js +0 -23
  32. package/convex/_generated/dataModel.d.ts +0 -60
  33. package/convex/_generated/server.d.ts +0 -143
  34. package/convex/_generated/server.js +0 -93
  35. package/convex/adminActivate.ts +0 -53
  36. package/convex/adminStats.ts +0 -306
  37. package/convex/agents.ts +0 -939
  38. package/convex/analytics.ts +0 -187
  39. package/convex/apiKeys.ts +0 -220
  40. package/convex/backfillAnalytics.ts +0 -272
  41. package/convex/backfillSearchLogs.ts +0 -35
  42. package/convex/billing.ts +0 -834
  43. package/convex/capabilities.ts +0 -157
  44. package/convex/chains.ts +0 -1318
  45. package/convex/credits.ts +0 -211
  46. package/convex/crons.ts +0 -50
  47. package/convex/debugFilestackLogs.ts +0 -16
  48. package/convex/debugGetToken.ts +0 -18
  49. package/convex/directCall.ts +0 -713
  50. package/convex/earnProgress.ts +0 -753
  51. package/convex/email.ts +0 -329
  52. package/convex/feedback.ts +0 -265
  53. package/convex/http.ts +0 -3430
  54. package/convex/inbound.ts +0 -32
  55. package/convex/logs.ts +0 -701
  56. package/convex/migrateFilestack.ts +0 -81
  57. package/convex/migratePartnersProd.ts +0 -174
  58. package/convex/migratePratham.ts +0 -126
  59. package/convex/migrateProviderWorkspaces.ts +0 -175
  60. package/convex/mou.ts +0 -91
  61. package/convex/providerKeys.ts +0 -289
  62. package/convex/providers.ts +0 -1135
  63. package/convex/purchases.ts +0 -183
  64. package/convex/ratelimit.ts +0 -104
  65. package/convex/schema.ts +0 -869
  66. package/convex/searchLogs.ts +0 -265
  67. package/convex/seedAPILayerAPIs.ts +0 -191
  68. package/convex/seedDirectCallConfigs.ts +0 -336
  69. package/convex/seedPratham.ts +0 -149
  70. package/convex/spendAlerts.ts +0 -442
  71. package/convex/stripeActions.ts +0 -607
  72. package/convex/teams.ts +0 -243
  73. package/convex/telemetry.ts +0 -81
  74. package/convex/tsconfig.json +0 -25
  75. package/convex/updateAPIStatus.ts +0 -44
  76. package/convex/usage.ts +0 -260
  77. package/convex/usageReports.ts +0 -357
  78. package/convex/waitlist.ts +0 -55
  79. package/convex/webhooks.ts +0 -494
  80. package/convex/workspaceSettings.ts +0 -143
  81. package/convex/workspaces.ts +0 -1331
  82. package/convex.json +0 -3
  83. package/direct-test.mjs +0 -51
  84. package/email-templates/filestack-provider-outreach.html +0 -162
  85. package/email-templates/partnership-template.html +0 -116
  86. package/email-templates/pratham-draft-preview.txt +0 -57
  87. package/email-templates/pratham-partnership-draft.html +0 -141
  88. package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
  89. package/reports/pipeline/PIPELINE-REPORT.json +0 -153
  90. package/reports/pipeline/acquire_apisguru.json +0 -17
  91. package/reports/pipeline/capabilities.json +0 -38
  92. package/reports/pipeline/discover_azure_recursive.json +0 -1551
  93. package/reports/pipeline/discover_github.json +0 -25
  94. package/reports/pipeline/discover_github_repos.json +0 -49
  95. package/reports/pipeline/discover_swaggerhub.json +0 -24
  96. package/reports/pipeline/discover_well_known.json +0 -23
  97. package/reports/pipeline/fetch_specs.json +0 -19
  98. package/reports/pipeline/generate_providers.json +0 -14
  99. package/reports/pipeline/match_registry.json +0 -11
  100. package/reports/pipeline/parse_specs.json +0 -17
  101. package/reports/pipeline/promote_candidates.json +0 -34
  102. package/reports/pipeline/validate.json +0 -30
  103. package/reports/pipeline/validate_smoke_details.json +0 -3835
  104. package/reports/session-report-2026-04-05.html +0 -433
  105. package/seed-apis-direct.mjs +0 -106
  106. package/src/access-control.ts +0 -174
  107. package/src/adapters/base.ts +0 -364
  108. package/src/adapters/claude-desktop.ts +0 -41
  109. package/src/adapters/cline.ts +0 -88
  110. package/src/adapters/continue.ts +0 -91
  111. package/src/adapters/cursor.ts +0 -43
  112. package/src/adapters/custom.ts +0 -188
  113. package/src/adapters/detect.ts +0 -202
  114. package/src/adapters/index.ts +0 -47
  115. package/src/adapters/windsurf.ts +0 -44
  116. package/src/bin-http.ts +0 -45
  117. package/src/bin.ts +0 -34
  118. package/src/capability-router.ts +0 -331
  119. package/src/chainExecutor.ts +0 -730
  120. package/src/chainResolver.test.ts +0 -246
  121. package/src/chainResolver.ts +0 -658
  122. package/src/cli/commands/demo.ts +0 -109
  123. package/src/cli/commands/doctor.ts +0 -435
  124. package/src/cli/commands/index.ts +0 -9
  125. package/src/cli/commands/login.ts +0 -203
  126. package/src/cli/commands/mcp-install.ts +0 -373
  127. package/src/cli/commands/restore.ts +0 -333
  128. package/src/cli/commands/setup.ts +0 -297
  129. package/src/cli/commands/uninstall.ts +0 -240
  130. package/src/cli/index.ts +0 -148
  131. package/src/cli.ts +0 -370
  132. package/src/confirmation.ts +0 -296
  133. package/src/credentials.ts +0 -455
  134. package/src/credits.ts +0 -329
  135. package/src/crypto.ts +0 -75
  136. package/src/discovery.ts +0 -568
  137. package/src/enterprise/env.ts +0 -156
  138. package/src/enterprise/index.ts +0 -7
  139. package/src/enterprise/script-generator.ts +0 -481
  140. package/src/execute-dynamic.ts +0 -617
  141. package/src/execute.ts +0 -2386
  142. package/src/gateway-client.ts +0 -192
  143. package/src/hivr-whitelist.ts +0 -110
  144. package/src/http-api.ts +0 -286
  145. package/src/http-server-minimal.ts +0 -154
  146. package/src/index.ts +0 -2611
  147. package/src/intelligent-gateway.ts +0 -339
  148. package/src/mcp-analytics.ts +0 -156
  149. package/src/metered.ts +0 -149
  150. package/src/open-apis-generated.ts +0 -157
  151. package/src/open-apis.ts +0 -558
  152. package/src/postinstall.ts +0 -18
  153. package/src/product-whitelist.ts +0 -246
  154. package/src/proxy.ts +0 -36
  155. package/src/session.ts +0 -129
  156. package/src/stripe.ts +0 -497
  157. package/src/telemetry.ts +0 -71
  158. package/src/test.ts +0 -135
  159. package/src/types/convex-api.d.ts +0 -20
  160. package/src/types/convex-api.ts +0 -21
  161. package/src/types.ts +0 -109
  162. package/src/ui/colors.ts +0 -219
  163. package/src/ui/errors.ts +0 -394
  164. package/src/ui/index.ts +0 -17
  165. package/src/ui/prompts.ts +0 -390
  166. package/src/ui/spinner.ts +0 -325
  167. package/src/utils/backup.ts +0 -224
  168. package/src/utils/config.ts +0 -318
  169. package/src/utils/os.ts +0 -124
  170. package/src/utils/paths.ts +0 -203
  171. package/src/webhook.ts +0 -107
  172. package/test-10-working.cjs +0 -97
  173. package/test-14-final.cjs +0 -96
  174. package/test-actual-handlers.ts +0 -92
  175. package/test-apilayer-all-14.ts +0 -249
  176. package/test-apilayer-fixed.ts +0 -248
  177. package/test-direct-endpoints.ts +0 -174
  178. package/test-exact-endpoints.ts +0 -144
  179. package/test-final.ts +0 -83
  180. package/test-full-routing.ts +0 -100
  181. package/test-handlers-correct.ts +0 -217
  182. package/test-numverify-key.ts +0 -41
  183. package/test-via-handlers.ts +0 -92
  184. package/test-worldnews.mjs +0 -26
  185. 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
- });