@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,756 +0,0 @@
1
- import { mutation, query } from "./_generated/server";
2
- import { v } from "convex/values";
3
- // Register a new provider and their first API
4
- export const registerProvider = mutation({
5
- args: {
6
- provider: v.object({
7
- name: v.string(),
8
- email: v.string(),
9
- website: v.optional(v.string()),
10
- }),
11
- api: v.object({
12
- name: v.string(),
13
- description: v.string(),
14
- category: v.string(),
15
- openApiUrl: v.optional(v.string()),
16
- docsUrl: v.optional(v.string()),
17
- pricingModel: v.string(),
18
- pricingNotes: v.optional(v.string()),
19
- }),
20
- },
21
- handler: async (ctx, args) => {
22
- const now = Date.now();
23
- // Check if provider already exists by email
24
- const existing = await ctx.db
25
- .query("providers")
26
- .withIndex("by_email", (q) => q.eq("email", args.provider.email))
27
- .first();
28
- let providerId;
29
- if (existing) {
30
- // Use existing provider
31
- providerId = existing._id;
32
- }
33
- else {
34
- // Create new provider - auto-approve for now
35
- providerId = await ctx.db.insert("providers", {
36
- name: args.provider.name,
37
- email: args.provider.email,
38
- website: args.provider.website,
39
- status: "approved", // Auto-approve for MVP
40
- createdAt: now,
41
- updatedAt: now,
42
- approvedAt: now,
43
- });
44
- }
45
- // Create the API listing - auto-approve for now
46
- const apiId = await ctx.db.insert("providerAPIs", {
47
- providerId,
48
- name: args.api.name,
49
- description: args.api.description,
50
- category: args.api.category,
51
- openApiUrl: args.api.openApiUrl,
52
- docsUrl: args.api.docsUrl,
53
- pricingModel: args.api.pricingModel,
54
- pricingNotes: args.api.pricingNotes,
55
- status: "approved", // Auto-approve for MVP
56
- createdAt: now,
57
- approvedAt: now,
58
- discoveryCount: 0,
59
- });
60
- // Create session for auto-login after registration
61
- const sessionToken = generateToken();
62
- const sessionExpiresAt = now + 30 * 24 * 60 * 60 * 1000; // 30 days
63
- await ctx.db.insert("sessions", {
64
- providerId,
65
- token: sessionToken,
66
- expiresAt: sessionExpiresAt,
67
- createdAt: now,
68
- });
69
- return { providerId, apiId, sessionToken };
70
- },
71
- });
72
- // Get provider by email
73
- export const getProviderByEmail = query({
74
- args: { email: v.string() },
75
- handler: async (ctx, args) => {
76
- return await ctx.db
77
- .query("providers")
78
- .withIndex("by_email", (q) => q.eq("email", args.email))
79
- .first();
80
- },
81
- });
82
- // Get all APIs for a provider
83
- export const getProviderAPIs = query({
84
- args: { providerId: v.id("providers") },
85
- handler: async (ctx, args) => {
86
- return await ctx.db
87
- .query("providerAPIs")
88
- .withIndex("by_providerId", (q) => q.eq("providerId", args.providerId))
89
- .collect();
90
- },
91
- });
92
- // Get all approved APIs (for the registry)
93
- export const getApprovedAPIs = query({
94
- args: {
95
- category: v.optional(v.string()),
96
- limit: v.optional(v.number()),
97
- },
98
- handler: async (ctx, args) => {
99
- const query = ctx.db
100
- .query("providerAPIs")
101
- .withIndex("by_status", (q) => q.eq("status", "approved"));
102
- const apis = await query.collect();
103
- // Filter by category if provided
104
- let filtered = args.category
105
- ? apis.filter((api) => api.category === args.category)
106
- : apis;
107
- // Apply limit
108
- if (args.limit) {
109
- filtered = filtered.slice(0, args.limit);
110
- }
111
- return filtered;
112
- },
113
- });
114
- // Get API categories with counts
115
- export const getCategories = query({
116
- handler: async (ctx) => {
117
- const apis = await ctx.db
118
- .query("providerAPIs")
119
- .withIndex("by_status", (q) => q.eq("status", "approved"))
120
- .collect();
121
- const categories = {};
122
- for (const api of apis) {
123
- categories[api.category] = (categories[api.category] || 0) + 1;
124
- }
125
- return Object.entries(categories)
126
- .map(([name, count]) => ({ name, count }))
127
- .sort((a, b) => b.count - a.count);
128
- },
129
- });
130
- // Increment discovery count when an agent finds an API
131
- export const trackDiscovery = mutation({
132
- args: { apiId: v.id("providerAPIs") },
133
- handler: async (ctx, args) => {
134
- const api = await ctx.db.get(args.apiId);
135
- if (!api)
136
- return;
137
- await ctx.db.patch(args.apiId, {
138
- discoveryCount: (api.discoveryCount || 0) + 1,
139
- lastDiscoveredAt: Date.now(),
140
- });
141
- },
142
- });
143
- // Admin: List pending providers
144
- export const getPendingProviders = query({
145
- handler: async (ctx) => {
146
- return await ctx.db
147
- .query("providers")
148
- .withIndex("by_status", (q) => q.eq("status", "pending"))
149
- .collect();
150
- },
151
- });
152
- // Admin: Approve provider
153
- export const approveProvider = mutation({
154
- args: { providerId: v.id("providers") },
155
- handler: async (ctx, args) => {
156
- await ctx.db.patch(args.providerId, {
157
- status: "approved",
158
- approvedAt: Date.now(),
159
- updatedAt: Date.now(),
160
- });
161
- },
162
- });
163
- // Admin: Reject provider
164
- export const rejectProvider = mutation({
165
- args: { providerId: v.id("providers") },
166
- handler: async (ctx, args) => {
167
- await ctx.db.patch(args.providerId, {
168
- status: "rejected",
169
- updatedAt: Date.now(),
170
- });
171
- },
172
- });
173
- // Get provider stats
174
- export const getProviderStats = query({
175
- handler: async (ctx) => {
176
- const providers = await ctx.db.query("providers").collect();
177
- const apis = await ctx.db.query("providerAPIs").collect();
178
- return {
179
- totalProviders: providers.length,
180
- approvedProviders: providers.filter((p) => p.status === "approved").length,
181
- pendingProviders: providers.filter((p) => p.status === "pending").length,
182
- totalAPIs: apis.length,
183
- approvedAPIs: apis.filter((a) => a.status === "approved").length,
184
- pendingAPIs: apis.filter((a) => a.status === "pending").length,
185
- totalDiscoveries: apis.reduce((sum, a) => sum + (a.discoveryCount || 0), 0),
186
- };
187
- },
188
- });
189
- // ============================================
190
- // DASHBOARD AUTH & SESSION FUNCTIONS
191
- // ============================================
192
- // Create magic link for email auth
193
- export const createMagicLink = mutation({
194
- args: { email: v.string() },
195
- handler: async (ctx, { email }) => {
196
- const token = generateToken();
197
- const expiresAt = Date.now() + 15 * 60 * 1000; // 15 minutes
198
- await ctx.db.insert("magicLinks", {
199
- email: email.toLowerCase(),
200
- token,
201
- expiresAt,
202
- createdAt: Date.now(),
203
- });
204
- return { token, expiresAt };
205
- },
206
- });
207
- // Verify magic link and create session
208
- export const verifyMagicLink = mutation({
209
- args: { token: v.string() },
210
- handler: async (ctx, { token }) => {
211
- const magicLink = await ctx.db
212
- .query("magicLinks")
213
- .withIndex("by_token", (q) => q.eq("token", token))
214
- .first();
215
- if (!magicLink) {
216
- return { success: false, error: "Invalid token" };
217
- }
218
- if (magicLink.expiresAt < Date.now()) {
219
- return { success: false, error: "Token expired" };
220
- }
221
- if (magicLink.usedAt) {
222
- return { success: false, error: "Token already used" };
223
- }
224
- // Mark as used
225
- await ctx.db.patch(magicLink._id, { usedAt: Date.now() });
226
- // Find or create provider
227
- let provider = await ctx.db
228
- .query("providers")
229
- .withIndex("by_email", (q) => q.eq("email", magicLink.email))
230
- .first();
231
- if (!provider) {
232
- const providerId = await ctx.db.insert("providers", {
233
- email: magicLink.email,
234
- name: magicLink.email.split("@")[0],
235
- status: "approved",
236
- createdAt: Date.now(),
237
- updatedAt: Date.now(),
238
- });
239
- provider = await ctx.db.get(providerId);
240
- }
241
- // Create session
242
- const sessionToken = generateToken();
243
- const sessionExpiresAt = Date.now() + 30 * 24 * 60 * 60 * 1000; // 30 days
244
- await ctx.db.insert("sessions", {
245
- providerId: provider._id,
246
- token: sessionToken,
247
- expiresAt: sessionExpiresAt,
248
- createdAt: Date.now(),
249
- });
250
- return {
251
- success: true,
252
- sessionToken,
253
- provider: {
254
- id: provider._id,
255
- email: provider.email,
256
- name: provider.name,
257
- },
258
- };
259
- },
260
- });
261
- // Get current session
262
- export const getSession = query({
263
- args: { token: v.string() },
264
- handler: async (ctx, { token }) => {
265
- const session = await ctx.db
266
- .query("sessions")
267
- .withIndex("by_token", (q) => q.eq("token", token))
268
- .first();
269
- if (!session || session.expiresAt < Date.now()) {
270
- return null;
271
- }
272
- const provider = await ctx.db.get(session.providerId);
273
- if (!provider)
274
- return null;
275
- return {
276
- providerId: provider._id,
277
- email: provider.email,
278
- name: provider.name,
279
- stripeOnboardingComplete: provider.stripeOnboardingComplete,
280
- };
281
- },
282
- });
283
- // ============================================
284
- // DASHBOARD ANALYTICS
285
- // ============================================
286
- // Get single API by ID
287
- export const getApiById = query({
288
- args: { apiId: v.string() },
289
- handler: async (ctx, args) => {
290
- // Try to get by document ID
291
- try {
292
- const api = await ctx.db.get(args.apiId);
293
- if (api) {
294
- // Check if it has Direct Call configured
295
- const directCall = await ctx.db
296
- .query("providerDirectCall")
297
- .filter((q) => q.eq(q.field("apiId"), args.apiId))
298
- .first();
299
- return { ...api, hasDirectCall: !!directCall, directCallStatus: directCall?.status };
300
- }
301
- }
302
- catch {
303
- // Not a valid ID format
304
- }
305
- return null;
306
- },
307
- });
308
- // Get provider APIs with Direct Call status
309
- export const getProviderAPIsWithStatus = query({
310
- args: { providerId: v.string() },
311
- handler: async (ctx, args) => {
312
- const apis = await ctx.db
313
- .query("providerAPIs")
314
- .filter((q) => q.eq(q.field("providerId"), args.providerId))
315
- .collect();
316
- // Add Direct Call status to each API
317
- const apisWithStatus = await Promise.all(apis.map(async (api) => {
318
- const directCall = await ctx.db
319
- .query("providerDirectCall")
320
- .filter((q) => q.eq(q.field("apiId"), api._id))
321
- .first();
322
- return {
323
- ...api,
324
- hasDirectCall: !!directCall,
325
- directCallStatus: directCall?.status,
326
- };
327
- }));
328
- return apisWithStatus;
329
- },
330
- });
331
- // DEBUG: Delete API
332
- export const debugDeleteAPI = mutation({
333
- args: { apiId: v.string() },
334
- handler: async (ctx, args) => {
335
- await ctx.db.delete(args.apiId);
336
- return { deleted: true };
337
- },
338
- });
339
- // Add API for logged-in provider (used by register page)
340
- export const addAPI = mutation({
341
- args: {
342
- token: v.string(),
343
- api: v.object({
344
- name: v.string(),
345
- description: v.string(),
346
- category: v.string(),
347
- openApiUrl: v.optional(v.string()),
348
- docsUrl: v.optional(v.string()),
349
- pricingModel: v.string(),
350
- pricingNotes: v.optional(v.string()),
351
- }),
352
- },
353
- handler: async (ctx, args) => {
354
- // Verify session
355
- const session = await ctx.db
356
- .query("sessions")
357
- .withIndex("by_token", (q) => q.eq("token", args.token))
358
- .first();
359
- if (!session || session.expiresAt < Date.now()) {
360
- throw new Error("Invalid or expired session");
361
- }
362
- const now = Date.now();
363
- const apiId = await ctx.db.insert("providerAPIs", {
364
- providerId: session.providerId,
365
- name: args.api.name,
366
- description: args.api.description,
367
- category: args.api.category,
368
- openApiUrl: args.api.openApiUrl,
369
- docsUrl: args.api.docsUrl,
370
- pricingModel: args.api.pricingModel,
371
- pricingNotes: args.api.pricingNotes,
372
- status: "approved",
373
- createdAt: now,
374
- approvedAt: now,
375
- discoveryCount: 0,
376
- });
377
- return { apiId, success: true };
378
- },
379
- });
380
- // Delete API for logged-in provider
381
- export const deleteAPI = mutation({
382
- args: {
383
- token: v.string(),
384
- apiId: v.string(),
385
- },
386
- handler: async (ctx, args) => {
387
- // Verify session
388
- const session = await ctx.db
389
- .query("sessions")
390
- .withIndex("by_token", (q) => q.eq("token", args.token))
391
- .first();
392
- if (!session || session.expiresAt < Date.now()) {
393
- throw new Error("Invalid or expired session");
394
- }
395
- // Get the API and verify ownership
396
- const api = await ctx.db.get(args.apiId);
397
- if (!api || api.providerId !== session.providerId) {
398
- throw new Error("API not found or unauthorized");
399
- }
400
- // Delete the API
401
- await ctx.db.delete(args.apiId);
402
- // Also delete any Direct Call config
403
- const directCallConfig = await ctx.db
404
- .query("providerDirectCall")
405
- .filter((q) => q.eq(q.field("apiId"), args.apiId))
406
- .first();
407
- if (directCallConfig) {
408
- await ctx.db.delete(directCallConfig._id);
409
- }
410
- return { deleted: true };
411
- },
412
- });
413
- // DEBUG: Update provider name
414
- export const debugUpdateProvider = mutation({
415
- args: {
416
- providerId: v.string(),
417
- name: v.optional(v.string()),
418
- },
419
- handler: async (ctx, args) => {
420
- const updates = {};
421
- if (args.name)
422
- updates.name = args.name;
423
- await ctx.db.patch(args.providerId, updates);
424
- return { updated: true };
425
- },
426
- });
427
- // DEBUG: Add API for provider (seeding)
428
- export const debugAddAPI = mutation({
429
- args: {
430
- providerId: v.string(),
431
- name: v.string(),
432
- description: v.string(),
433
- category: v.string(),
434
- docsUrl: v.optional(v.string()),
435
- pricingModel: v.string(),
436
- pricingNotes: v.optional(v.string()),
437
- },
438
- handler: async (ctx, args) => {
439
- const now = Date.now();
440
- return await ctx.db.insert("providerAPIs", {
441
- providerId: args.providerId,
442
- name: args.name,
443
- description: args.description,
444
- category: args.category,
445
- docsUrl: args.docsUrl,
446
- pricingModel: args.pricingModel,
447
- pricingNotes: args.pricingNotes,
448
- status: "approved",
449
- createdAt: now,
450
- approvedAt: now,
451
- discoveryCount: 0,
452
- });
453
- },
454
- });
455
- // DEBUG: Delete provider and all related data
456
- export const debugDeleteProvider = mutation({
457
- args: { providerId: v.string() },
458
- handler: async (ctx, args) => {
459
- const providerId = args.providerId;
460
- // Delete sessions
461
- const sessions = await ctx.db.query("sessions").filter(q => q.eq(q.field("providerId"), providerId)).collect();
462
- for (const s of sessions)
463
- await ctx.db.delete(s._id);
464
- // Delete APIs
465
- const apis = await ctx.db.query("providerAPIs").filter(q => q.eq(q.field("providerId"), providerId)).collect();
466
- for (const a of apis)
467
- await ctx.db.delete(a._id);
468
- // Delete direct call configs
469
- const configs = await ctx.db.query("providerDirectCall").filter(q => q.eq(q.field("providerId"), providerId)).collect();
470
- for (const c of configs) {
471
- // Delete actions for this config
472
- const actions = await ctx.db.query("providerActions").filter(q => q.eq(q.field("directCallId"), c._id)).collect();
473
- for (const act of actions)
474
- await ctx.db.delete(act._id);
475
- await ctx.db.delete(c._id);
476
- }
477
- // Delete provider
478
- await ctx.db.delete(providerId);
479
- return { deleted: true };
480
- },
481
- });
482
- // DEBUG: List all sessions
483
- export const debugListSessions = query({
484
- args: {},
485
- handler: async (ctx) => {
486
- return await ctx.db.query("sessions").collect();
487
- },
488
- });
489
- // DEBUG: List all providers
490
- export const debugListProviders = query({
491
- args: {},
492
- handler: async (ctx) => {
493
- return await ctx.db.query("providers").collect();
494
- },
495
- });
496
- export const getAnalytics = query({
497
- args: {
498
- token: v.string(),
499
- period: v.optional(v.string()), // "week", "month", "all"
500
- },
501
- handler: async (ctx, { token, period = "month" }) => {
502
- const session = await ctx.db
503
- .query("sessions")
504
- .withIndex("by_token", (q) => q.eq("token", token))
505
- .first();
506
- if (!session || session.expiresAt < Date.now()) {
507
- return null;
508
- }
509
- const now = Date.now();
510
- const periodMs = {
511
- week: 7 * 24 * 60 * 60 * 1000,
512
- month: 30 * 24 * 60 * 60 * 1000,
513
- all: now,
514
- }[period] || 30 * 24 * 60 * 60 * 1000;
515
- const startTime = now - periodMs;
516
- // Get usage logs for this provider (from Direct Call usageLog)
517
- const usageLogs = await ctx.db
518
- .query("usageLog")
519
- .withIndex("by_providerId", (q) => q.eq("providerId", session.providerId))
520
- .collect();
521
- const periodCalls = usageLogs.filter((c) => c.timestamp >= startTime);
522
- // Calculate metrics
523
- const totalCalls = periodCalls.length;
524
- const uniqueAgents = new Set(periodCalls.map((c) => c.userId)).size;
525
- const totalRevenue = periodCalls.reduce((sum, c) => sum + (c.creditsUsed / 100), 0); // cents to dollars
526
- const successCount = periodCalls.filter((c) => c.success).length;
527
- const successRate = totalCalls > 0 ? (successCount / totalCalls) * 100 : 100;
528
- const avgLatency = totalCalls > 0
529
- ? periodCalls.reduce((sum, c) => sum + c.latencyMs, 0) / totalCalls
530
- : 0;
531
- // Calls over time (daily buckets)
532
- const callsByDay = {};
533
- periodCalls.forEach((call) => {
534
- const day = new Date(call.timestamp).toISOString().split("T")[0];
535
- if (!callsByDay[day]) {
536
- callsByDay[day] = { calls: 0, revenue: 0, success: 0 };
537
- }
538
- callsByDay[day].calls += 1;
539
- callsByDay[day].revenue += call.creditsUsed / 100;
540
- if (call.success)
541
- callsByDay[day].success += 1;
542
- });
543
- // Top agents (users)
544
- const agentCallCounts = {};
545
- periodCalls.forEach((call) => {
546
- agentCallCounts[call.userId] = (agentCallCounts[call.userId] || 0) + 1;
547
- });
548
- const topAgents = Object.entries(agentCallCounts)
549
- .sort((a, b) => b[1] - a[1])
550
- .slice(0, 10)
551
- .map(([agentId, calls]) => ({ agentId, calls }));
552
- // Top actions
553
- const actionCallCounts = {};
554
- periodCalls.forEach((call) => {
555
- actionCallCounts[call.actionName] = (actionCallCounts[call.actionName] || 0) + 1;
556
- });
557
- const topActions = Object.entries(actionCallCounts)
558
- .sort((a, b) => b[1] - a[1])
559
- .slice(0, 10)
560
- .map(([actionName, calls]) => ({ actionName, calls }));
561
- // Get provider's APIs
562
- const apis = await ctx.db
563
- .query("providerAPIs")
564
- .withIndex("by_providerId", (q) => q.eq("providerId", session.providerId))
565
- .collect();
566
- // Get Direct Call configs to map directCallId to apiId
567
- const directCallConfigs = await ctx.db
568
- .query("providerDirectCall")
569
- .withIndex("by_providerId", (q) => q.eq("providerId", session.providerId))
570
- .collect();
571
- // Calls per API (via directCallId → apiId mapping)
572
- const callsByDirectCallId = {};
573
- periodCalls.forEach((call) => {
574
- const dcId = call.directCallId;
575
- callsByDirectCallId[dcId] = (callsByDirectCallId[dcId] || 0) + 1;
576
- });
577
- // Map to apiId
578
- const callsByApiId = {};
579
- directCallConfigs.forEach((dc) => {
580
- if (dc.apiId) {
581
- callsByApiId[dc.apiId] = callsByDirectCallId[dc._id] || 0;
582
- }
583
- });
584
- // Preview data for providers with no usage yet
585
- const isPreview = totalCalls === 0;
586
- if (isPreview) {
587
- // Generate preview data so providers can see what the dashboard looks like
588
- const previewDays = [];
589
- for (let i = 13; i >= 0; i--) {
590
- const date = new Date(now - i * 24 * 60 * 60 * 1000).toISOString().split("T")[0];
591
- previewDays.push({
592
- date,
593
- calls: Math.floor(Math.random() * 50) + 10,
594
- revenue: Math.random() * 5,
595
- });
596
- }
597
- return {
598
- totalCalls: 847,
599
- uniqueAgents: 23,
600
- totalRevenue: 42.35,
601
- successRate: 98.2,
602
- avgLatency: 145,
603
- callsByDay: previewDays,
604
- topAgents: [
605
- { agentId: "agent_demo_1", calls: 234 },
606
- { agentId: "agent_demo_2", calls: 189 },
607
- { agentId: "agent_demo_3", calls: 156 },
608
- { agentId: "agent_demo_4", calls: 98 },
609
- { agentId: "agent_demo_5", calls: 67 },
610
- ],
611
- topActions: [
612
- { actionName: "send_message", calls: 412 },
613
- { actionName: "get_status", calls: 289 },
614
- { actionName: "create_invoice", calls: 146 },
615
- ],
616
- apis: apis.map((api) => ({
617
- id: api._id,
618
- name: api.name,
619
- calls: Math.floor(Math.random() * 200) + 50,
620
- status: api.status,
621
- })),
622
- isPreview: true,
623
- };
624
- }
625
- return {
626
- totalCalls,
627
- uniqueAgents,
628
- totalRevenue,
629
- successRate,
630
- avgLatency,
631
- callsByDay: Object.entries(callsByDay)
632
- .map(([date, data]) => ({
633
- date,
634
- calls: data.calls,
635
- revenue: data.revenue,
636
- }))
637
- .sort((a, b) => a.date.localeCompare(b.date)),
638
- topAgents,
639
- topActions,
640
- apis: apis.map((api) => ({
641
- id: api._id,
642
- name: api.name,
643
- calls: callsByApiId[api._id] || 0,
644
- status: api.status,
645
- })),
646
- isPreview: false,
647
- };
648
- },
649
- });
650
- // ============================================
651
- // DASHBOARD EARNINGS
652
- // ============================================
653
- export const getEarnings = query({
654
- args: { token: v.string() },
655
- handler: async (ctx, { token }) => {
656
- const session = await ctx.db
657
- .query("sessions")
658
- .withIndex("by_token", (q) => q.eq("token", token))
659
- .first();
660
- if (!session || session.expiresAt < Date.now()) {
661
- return null;
662
- }
663
- // Get all payouts
664
- const payouts = await ctx.db
665
- .query("payouts")
666
- .withIndex("by_providerId", (q) => q.eq("providerId", session.providerId))
667
- .collect();
668
- // Get all API calls to calculate pending
669
- const allCalls = await ctx.db
670
- .query("apiCalls")
671
- .withIndex("by_providerId", (q) => q.eq("providerId", session.providerId))
672
- .collect();
673
- // Find last completed payout
674
- const completedPayouts = payouts
675
- .filter((p) => p.status === "completed")
676
- .sort((a, b) => b.periodEnd - a.periodEnd);
677
- const lastPayoutEnd = completedPayouts[0]?.periodEnd || 0;
678
- // Pending = all revenue since last payout
679
- const pendingCalls = allCalls.filter((c) => c.timestamp > lastPayoutEnd);
680
- const pendingAmount = pendingCalls.reduce((sum, c) => sum + c.costUsd, 0);
681
- // Total earned all time
682
- const totalEarned = allCalls.reduce((sum, c) => sum + c.costUsd, 0);
683
- // Get provider for Stripe status
684
- const provider = await ctx.db.get(session.providerId);
685
- return {
686
- pendingAmount,
687
- totalEarned,
688
- totalPaidOut: completedPayouts.reduce((sum, p) => sum + p.amountUsd, 0),
689
- stripeConnected: !!provider?.stripeConnectId,
690
- stripeOnboardingComplete: provider?.stripeOnboardingComplete || false,
691
- payouts: payouts
692
- .sort((a, b) => b.createdAt - a.createdAt)
693
- .slice(0, 20)
694
- .map((p) => ({
695
- id: p._id,
696
- amount: p.amountUsd,
697
- status: p.status,
698
- periodStart: p.periodStart,
699
- periodEnd: p.periodEnd,
700
- createdAt: p.createdAt,
701
- completedAt: p.completedAt,
702
- })),
703
- };
704
- },
705
- });
706
- // ============================================
707
- // ADMIN QUERIES
708
- // ============================================
709
- // Get all providers (admin only)
710
- export const getAllProviders = query({
711
- handler: async (ctx) => {
712
- return await ctx.db
713
- .query("providers")
714
- .order("desc")
715
- .collect();
716
- },
717
- });
718
- // Get all APIs (admin only)
719
- export const getAllAPIs = query({
720
- handler: async (ctx) => {
721
- return await ctx.db
722
- .query("providerAPIs")
723
- .order("desc")
724
- .collect();
725
- },
726
- });
727
- // Helper function
728
- function generateToken() {
729
- const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
730
- let result = "";
731
- for (let i = 0; i < 48; i++) {
732
- result += chars.charAt(Math.floor(Math.random() * chars.length));
733
- }
734
- return result;
735
- }
736
- // Debug: Update API name/description
737
- export const debugUpdateAPI = mutation({
738
- args: {
739
- apiId: v.string(),
740
- name: v.optional(v.string()),
741
- description: v.optional(v.string()),
742
- category: v.optional(v.string()),
743
- },
744
- handler: async (ctx, args) => {
745
- const updates = {};
746
- if (args.name)
747
- updates.name = args.name;
748
- if (args.description)
749
- updates.description = args.description;
750
- if (args.category)
751
- updates.category = args.category;
752
- await ctx.db.patch(args.apiId, updates);
753
- return { updated: true };
754
- },
755
- });
756
- //# sourceMappingURL=providers.js.map