@nordsym/apiclaw 1.5.17 → 1.5.19

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