@nordsym/apiclaw 1.7.2 → 1.7.4
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/convex/_generated/api.d.ts +115 -0
- package/convex/_generated/api.js +23 -0
- package/convex/_generated/dataModel.d.ts +60 -0
- package/convex/_generated/server.d.ts +143 -0
- package/convex/_generated/server.js +93 -0
- package/convex/adminActivate.d.ts +3 -0
- package/convex/adminActivate.d.ts.map +1 -0
- package/convex/adminActivate.js +47 -0
- package/convex/adminActivate.js.map +1 -0
- package/convex/adminActivate.ts +54 -0
- package/convex/adminStats.d.ts +3 -0
- package/convex/adminStats.d.ts.map +1 -0
- package/convex/adminStats.js +42 -0
- package/convex/adminStats.js.map +1 -0
- package/convex/adminStats.ts +44 -0
- package/convex/agents.d.ts +76 -0
- package/convex/agents.d.ts.map +1 -0
- package/convex/agents.js +699 -0
- package/convex/agents.js.map +1 -0
- package/convex/agents.ts +814 -0
- package/convex/analytics.d.ts +5 -0
- package/convex/analytics.d.ts.map +1 -0
- package/convex/analytics.js +166 -0
- package/convex/analytics.js.map +1 -0
- package/convex/analytics.ts +186 -0
- package/convex/billing.d.ts +88 -0
- package/convex/billing.d.ts.map +1 -0
- package/convex/billing.js +655 -0
- package/convex/billing.js.map +1 -0
- package/convex/billing.ts +791 -0
- package/convex/capabilities.d.ts +9 -0
- package/convex/capabilities.d.ts.map +1 -0
- package/convex/capabilities.js +145 -0
- package/convex/capabilities.js.map +1 -0
- package/convex/capabilities.ts +157 -0
- package/convex/chains.d.ts +68 -0
- package/convex/chains.d.ts.map +1 -0
- package/convex/chains.js +1105 -0
- package/convex/chains.js.map +1 -0
- package/convex/chains.ts +1318 -0
- package/convex/credits.d.ts +25 -0
- package/convex/credits.d.ts.map +1 -0
- package/convex/credits.js +186 -0
- package/convex/credits.js.map +1 -0
- package/convex/credits.ts +211 -0
- package/convex/crons.d.ts +3 -0
- package/convex/crons.d.ts.map +1 -0
- package/convex/crons.js +17 -0
- package/convex/crons.js.map +1 -0
- package/convex/crons.ts +28 -0
- package/convex/directCall.d.ts +72 -0
- package/convex/directCall.d.ts.map +1 -0
- package/convex/directCall.js +627 -0
- package/convex/directCall.js.map +1 -0
- package/convex/directCall.ts +678 -0
- package/convex/earnProgress.d.ts +58 -0
- package/convex/earnProgress.d.ts.map +1 -0
- package/convex/earnProgress.js +649 -0
- package/convex/earnProgress.js.map +1 -0
- package/convex/earnProgress.ts +753 -0
- package/convex/email.d.ts +14 -0
- package/convex/email.d.ts.map +1 -0
- package/convex/email.js +300 -0
- package/convex/email.js.map +1 -0
- package/convex/email.ts +329 -0
- package/convex/feedback.d.ts +7 -0
- package/convex/feedback.d.ts.map +1 -0
- package/convex/feedback.js +227 -0
- package/convex/feedback.js.map +1 -0
- package/convex/feedback.ts +265 -0
- package/convex/http.d.ts +3 -0
- package/convex/http.d.ts.map +1 -0
- package/convex/http.js +1405 -0
- package/convex/http.js.map +1 -0
- package/convex/http.ts +1577 -0
- package/convex/inbound.d.ts +2 -0
- package/convex/inbound.d.ts.map +1 -0
- package/convex/inbound.js +32 -0
- package/convex/inbound.js.map +1 -0
- package/convex/inbound.ts +32 -0
- package/convex/logs.d.ts +38 -0
- package/convex/logs.d.ts.map +1 -0
- package/convex/logs.js +487 -0
- package/convex/logs.js.map +1 -0
- package/convex/logs.ts +550 -0
- package/convex/mou.d.ts +6 -0
- package/convex/mou.d.ts.map +1 -0
- package/convex/mou.js +82 -0
- package/convex/mou.js.map +1 -0
- package/convex/mou.ts +91 -0
- package/convex/providerKeys.d.ts +31 -0
- package/convex/providerKeys.d.ts.map +1 -0
- package/convex/providerKeys.js +257 -0
- package/convex/providerKeys.js.map +1 -0
- package/convex/providerKeys.ts +289 -0
- package/convex/providers.d.ts +32 -0
- package/convex/providers.d.ts.map +1 -0
- package/convex/providers.js +814 -0
- package/convex/providers.js.map +1 -0
- package/convex/providers.ts +909 -0
- package/convex/purchases.d.ts +7 -0
- package/convex/purchases.d.ts.map +1 -0
- package/convex/purchases.js +157 -0
- package/convex/purchases.js.map +1 -0
- package/convex/purchases.ts +183 -0
- package/convex/ratelimit.d.ts +4 -0
- package/convex/ratelimit.d.ts.map +1 -0
- package/convex/ratelimit.js +91 -0
- package/convex/ratelimit.js.map +1 -0
- package/convex/ratelimit.ts +104 -0
- package/convex/schema.ts +802 -0
- package/convex/searchLogs.d.ts +4 -0
- package/convex/searchLogs.d.ts.map +1 -0
- package/convex/searchLogs.js +129 -0
- package/convex/searchLogs.js.map +1 -0
- package/convex/searchLogs.ts +146 -0
- package/convex/seedAPILayerAPIs.d.ts +7 -0
- package/convex/seedAPILayerAPIs.d.ts.map +1 -0
- package/convex/seedAPILayerAPIs.js +177 -0
- package/convex/seedAPILayerAPIs.js.map +1 -0
- package/convex/seedAPILayerAPIs.ts +191 -0
- package/convex/seedDirectCallConfigs.d.ts +2 -0
- package/convex/seedDirectCallConfigs.d.ts.map +1 -0
- package/convex/seedDirectCallConfigs.js +324 -0
- package/convex/seedDirectCallConfigs.js.map +1 -0
- package/convex/seedDirectCallConfigs.ts +336 -0
- package/convex/seedPratham.d.ts +6 -0
- package/convex/seedPratham.d.ts.map +1 -0
- package/convex/seedPratham.js +150 -0
- package/convex/seedPratham.js.map +1 -0
- package/convex/seedPratham.ts +161 -0
- package/convex/spendAlerts.d.ts +36 -0
- package/convex/spendAlerts.d.ts.map +1 -0
- package/convex/spendAlerts.js +380 -0
- package/convex/spendAlerts.js.map +1 -0
- package/convex/spendAlerts.ts +442 -0
- package/convex/stripeActions.d.ts +19 -0
- package/convex/stripeActions.d.ts.map +1 -0
- package/convex/stripeActions.js +411 -0
- package/convex/stripeActions.js.map +1 -0
- package/convex/stripeActions.ts +512 -0
- package/convex/teams.d.ts +21 -0
- package/convex/teams.d.ts.map +1 -0
- package/convex/teams.js +215 -0
- package/convex/teams.js.map +1 -0
- package/convex/teams.ts +243 -0
- package/convex/telemetry.d.ts +4 -0
- package/convex/telemetry.d.ts.map +1 -0
- package/convex/telemetry.js +74 -0
- package/convex/telemetry.js.map +1 -0
- package/convex/telemetry.ts +81 -0
- package/convex/tsconfig.json +25 -0
- package/convex/updateAPIStatus.d.ts +6 -0
- package/convex/updateAPIStatus.d.ts.map +1 -0
- package/convex/updateAPIStatus.js +40 -0
- package/convex/updateAPIStatus.js.map +1 -0
- package/convex/updateAPIStatus.ts +45 -0
- package/convex/usage.d.ts +27 -0
- package/convex/usage.d.ts.map +1 -0
- package/convex/usage.js +229 -0
- package/convex/usage.js.map +1 -0
- package/convex/usage.ts +260 -0
- package/convex/waitlist.d.ts +4 -0
- package/convex/waitlist.d.ts.map +1 -0
- package/convex/waitlist.js +49 -0
- package/convex/waitlist.js.map +1 -0
- package/convex/waitlist.ts +55 -0
- package/convex/webhooks.d.ts +12 -0
- package/convex/webhooks.d.ts.map +1 -0
- package/convex/webhooks.js +410 -0
- package/convex/webhooks.js.map +1 -0
- package/convex/webhooks.ts +494 -0
- package/convex/workspaces.d.ts +31 -0
- package/convex/workspaces.d.ts.map +1 -0
- package/convex/workspaces.js +975 -0
- package/convex/workspaces.js.map +1 -0
- package/convex/workspaces.ts +1130 -0
- package/dist/bin.js +0 -0
- package/dist/capability-router.js +1 -1
- package/dist/capability-router.js.map +1 -1
- package/dist/execute.d.ts +2 -0
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +18 -4
- package/dist/execute.js.map +1 -1
- package/dist/http-api.js +1 -1
- package/dist/http-api.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-analytics.d.ts +32 -0
- package/dist/mcp-analytics.d.ts.map +1 -0
- package/dist/mcp-analytics.js +130 -0
- package/dist/mcp-analytics.js.map +1 -0
- package/package.json +1 -1
- package/dist/chain-types.d.ts +0 -187
- package/dist/chain-types.d.ts.map +0 -1
- package/dist/chain-types.js +0 -33
- package/dist/chain-types.js.map +0 -1
- package/dist/registry/apis.json.bak +0 -248811
- package/dist/src/bin.js +0 -17
- package/dist/src/capability-router.js +0 -240
- package/dist/src/chainExecutor.js +0 -451
- package/dist/src/chainResolver.js +0 -518
- package/dist/src/cli/commands/doctor.js +0 -324
- package/dist/src/cli/commands/mcp-install.js +0 -255
- package/dist/src/cli/commands/restore.js +0 -259
- package/dist/src/cli/commands/setup.js +0 -205
- package/dist/src/cli/commands/uninstall.js +0 -188
- package/dist/src/cli/index.js +0 -111
- package/dist/src/cli.js +0 -302
- package/dist/src/confirmation.js +0 -240
- package/dist/src/credentials.js +0 -357
- package/dist/src/credits.js +0 -260
- package/dist/src/crypto.js +0 -66
- package/dist/src/discovery.js +0 -504
- package/dist/src/enterprise/env.js +0 -123
- package/dist/src/enterprise/script-generator.js +0 -460
- package/dist/src/execute-dynamic.js +0 -473
- package/dist/src/execute.js +0 -1727
- package/dist/src/index.js +0 -2062
- package/dist/src/metered.js +0 -80
- package/dist/src/open-apis.js +0 -276
- package/dist/src/proxy.js +0 -28
- package/dist/src/session.js +0 -86
- package/dist/src/stripe.js +0 -407
- package/dist/src/telemetry.js +0 -49
- package/dist/src/types.js +0 -2
- package/dist/src/utils/backup.js +0 -181
- package/dist/src/utils/config.js +0 -220
- package/dist/src/utils/os.js +0 -105
- package/dist/src/utils/paths.js +0 -159
|
@@ -0,0 +1,753 @@
|
|
|
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
|
+
});
|