@nordsym/apiclaw 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.md +15 -2
  2. package/dist/bin-http.js +0 -0
  3. package/dist/bin.bundled.js +79288 -0
  4. package/dist/funnel-client.d.ts +24 -0
  5. package/dist/funnel-client.d.ts.map +1 -0
  6. package/dist/funnel-client.js +131 -0
  7. package/dist/funnel-client.js.map +1 -0
  8. package/dist/funnel.test.d.ts +2 -0
  9. package/dist/funnel.test.d.ts.map +1 -0
  10. package/dist/funnel.test.js +145 -0
  11. package/dist/funnel.test.js.map +1 -0
  12. package/dist/gateway-client.d.ts.map +1 -1
  13. package/dist/gateway-client.js +24 -2
  14. package/dist/gateway-client.js.map +1 -1
  15. package/dist/index.bundled.js +61263 -0
  16. package/dist/index.js +161 -74
  17. package/dist/index.js.map +1 -1
  18. package/dist/postinstall.d.ts +0 -5
  19. package/dist/postinstall.d.ts.map +1 -1
  20. package/dist/postinstall.js +24 -3
  21. package/dist/postinstall.js.map +1 -1
  22. package/dist/registration-guard.d.ts +29 -0
  23. package/dist/registration-guard.d.ts.map +1 -0
  24. package/dist/registration-guard.js +87 -0
  25. package/dist/registration-guard.js.map +1 -0
  26. package/package.json +7 -2
  27. package/.claude/settings.local.json +0 -9
  28. package/.env.prod +0 -1
  29. package/apiclaw-README.md +0 -494
  30. package/convex/_generated/api.d.ts +0 -137
  31. package/convex/_generated/api.js +0 -23
  32. package/convex/_generated/dataModel.d.ts +0 -60
  33. package/convex/_generated/server.d.ts +0 -143
  34. package/convex/_generated/server.js +0 -93
  35. package/convex/adminActivate.ts +0 -53
  36. package/convex/adminStats.ts +0 -306
  37. package/convex/agents.ts +0 -939
  38. package/convex/analytics.ts +0 -187
  39. package/convex/apiKeys.ts +0 -220
  40. package/convex/backfillAnalytics.ts +0 -272
  41. package/convex/backfillSearchLogs.ts +0 -35
  42. package/convex/billing.ts +0 -834
  43. package/convex/capabilities.ts +0 -157
  44. package/convex/chains.ts +0 -1318
  45. package/convex/credits.ts +0 -211
  46. package/convex/crons.ts +0 -50
  47. package/convex/debugFilestackLogs.ts +0 -16
  48. package/convex/debugGetToken.ts +0 -18
  49. package/convex/directCall.ts +0 -713
  50. package/convex/earnProgress.ts +0 -753
  51. package/convex/email.ts +0 -329
  52. package/convex/feedback.ts +0 -265
  53. package/convex/http.ts +0 -3430
  54. package/convex/inbound.ts +0 -32
  55. package/convex/logs.ts +0 -701
  56. package/convex/migrateFilestack.ts +0 -81
  57. package/convex/migratePartnersProd.ts +0 -174
  58. package/convex/migratePratham.ts +0 -126
  59. package/convex/migrateProviderWorkspaces.ts +0 -175
  60. package/convex/mou.ts +0 -91
  61. package/convex/providerKeys.ts +0 -289
  62. package/convex/providers.ts +0 -1135
  63. package/convex/purchases.ts +0 -183
  64. package/convex/ratelimit.ts +0 -104
  65. package/convex/schema.ts +0 -869
  66. package/convex/searchLogs.ts +0 -265
  67. package/convex/seedAPILayerAPIs.ts +0 -191
  68. package/convex/seedDirectCallConfigs.ts +0 -336
  69. package/convex/seedPratham.ts +0 -149
  70. package/convex/spendAlerts.ts +0 -442
  71. package/convex/stripeActions.ts +0 -607
  72. package/convex/teams.ts +0 -243
  73. package/convex/telemetry.ts +0 -81
  74. package/convex/tsconfig.json +0 -25
  75. package/convex/updateAPIStatus.ts +0 -44
  76. package/convex/usage.ts +0 -260
  77. package/convex/usageReports.ts +0 -357
  78. package/convex/waitlist.ts +0 -55
  79. package/convex/webhooks.ts +0 -494
  80. package/convex/workspaceSettings.ts +0 -143
  81. package/convex/workspaces.ts +0 -1331
  82. package/convex.json +0 -3
  83. package/direct-test.mjs +0 -51
  84. package/email-templates/filestack-provider-outreach.html +0 -162
  85. package/email-templates/partnership-template.html +0 -116
  86. package/email-templates/pratham-draft-preview.txt +0 -57
  87. package/email-templates/pratham-partnership-draft.html +0 -141
  88. package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
  89. package/reports/pipeline/PIPELINE-REPORT.json +0 -153
  90. package/reports/pipeline/acquire_apisguru.json +0 -17
  91. package/reports/pipeline/capabilities.json +0 -38
  92. package/reports/pipeline/discover_azure_recursive.json +0 -1551
  93. package/reports/pipeline/discover_github.json +0 -25
  94. package/reports/pipeline/discover_github_repos.json +0 -49
  95. package/reports/pipeline/discover_swaggerhub.json +0 -24
  96. package/reports/pipeline/discover_well_known.json +0 -23
  97. package/reports/pipeline/fetch_specs.json +0 -19
  98. package/reports/pipeline/generate_providers.json +0 -14
  99. package/reports/pipeline/match_registry.json +0 -11
  100. package/reports/pipeline/parse_specs.json +0 -17
  101. package/reports/pipeline/promote_candidates.json +0 -34
  102. package/reports/pipeline/validate.json +0 -30
  103. package/reports/pipeline/validate_smoke_details.json +0 -3835
  104. package/reports/session-report-2026-04-05.html +0 -433
  105. package/seed-apis-direct.mjs +0 -106
  106. package/src/access-control.ts +0 -174
  107. package/src/adapters/base.ts +0 -364
  108. package/src/adapters/claude-desktop.ts +0 -41
  109. package/src/adapters/cline.ts +0 -88
  110. package/src/adapters/continue.ts +0 -91
  111. package/src/adapters/cursor.ts +0 -43
  112. package/src/adapters/custom.ts +0 -188
  113. package/src/adapters/detect.ts +0 -202
  114. package/src/adapters/index.ts +0 -47
  115. package/src/adapters/windsurf.ts +0 -44
  116. package/src/bin-http.ts +0 -45
  117. package/src/bin.ts +0 -34
  118. package/src/capability-router.ts +0 -331
  119. package/src/chainExecutor.ts +0 -730
  120. package/src/chainResolver.test.ts +0 -246
  121. package/src/chainResolver.ts +0 -658
  122. package/src/cli/commands/demo.ts +0 -109
  123. package/src/cli/commands/doctor.ts +0 -435
  124. package/src/cli/commands/index.ts +0 -9
  125. package/src/cli/commands/login.ts +0 -203
  126. package/src/cli/commands/mcp-install.ts +0 -373
  127. package/src/cli/commands/restore.ts +0 -333
  128. package/src/cli/commands/setup.ts +0 -297
  129. package/src/cli/commands/uninstall.ts +0 -240
  130. package/src/cli/index.ts +0 -148
  131. package/src/cli.ts +0 -370
  132. package/src/confirmation.ts +0 -296
  133. package/src/credentials.ts +0 -455
  134. package/src/credits.ts +0 -329
  135. package/src/crypto.ts +0 -75
  136. package/src/discovery.ts +0 -568
  137. package/src/enterprise/env.ts +0 -156
  138. package/src/enterprise/index.ts +0 -7
  139. package/src/enterprise/script-generator.ts +0 -481
  140. package/src/execute-dynamic.ts +0 -617
  141. package/src/execute.ts +0 -2386
  142. package/src/gateway-client.ts +0 -192
  143. package/src/hivr-whitelist.ts +0 -110
  144. package/src/http-api.ts +0 -286
  145. package/src/http-server-minimal.ts +0 -154
  146. package/src/index.ts +0 -2611
  147. package/src/intelligent-gateway.ts +0 -339
  148. package/src/mcp-analytics.ts +0 -156
  149. package/src/metered.ts +0 -149
  150. package/src/open-apis-generated.ts +0 -157
  151. package/src/open-apis.ts +0 -558
  152. package/src/postinstall.ts +0 -18
  153. package/src/product-whitelist.ts +0 -246
  154. package/src/proxy.ts +0 -36
  155. package/src/session.ts +0 -129
  156. package/src/stripe.ts +0 -497
  157. package/src/telemetry.ts +0 -71
  158. package/src/test.ts +0 -135
  159. package/src/types/convex-api.d.ts +0 -20
  160. package/src/types/convex-api.ts +0 -21
  161. package/src/types.ts +0 -109
  162. package/src/ui/colors.ts +0 -219
  163. package/src/ui/errors.ts +0 -394
  164. package/src/ui/index.ts +0 -17
  165. package/src/ui/prompts.ts +0 -390
  166. package/src/ui/spinner.ts +0 -325
  167. package/src/utils/backup.ts +0 -224
  168. package/src/utils/config.ts +0 -318
  169. package/src/utils/os.ts +0 -124
  170. package/src/utils/paths.ts +0 -203
  171. package/src/webhook.ts +0 -107
  172. package/test-10-working.cjs +0 -97
  173. package/test-14-final.cjs +0 -96
  174. package/test-actual-handlers.ts +0 -92
  175. package/test-apilayer-all-14.ts +0 -249
  176. package/test-apilayer-fixed.ts +0 -248
  177. package/test-direct-endpoints.ts +0 -174
  178. package/test-exact-endpoints.ts +0 -144
  179. package/test-final.ts +0 -83
  180. package/test-full-routing.ts +0 -100
  181. package/test-handlers-correct.ts +0 -217
  182. package/test-numverify-key.ts +0 -41
  183. package/test-via-handlers.ts +0 -92
  184. package/test-worldnews.mjs +0 -26
  185. package/tsconfig.json +0 -20
@@ -1,713 +0,0 @@
1
- import { v } from "convex/values";
2
- import { mutation, query } from "./_generated/server";
3
-
4
- // ============================================
5
- // MUTATIONS
6
- // ============================================
7
-
8
- /**
9
- * Save/update provider's Direct Call configuration
10
- */
11
- export const saveDirectCallConfig = mutation({
12
- args: {
13
- id: v.optional(v.id("providerDirectCall")),
14
- providerId: v.id("providers"),
15
- apiId: v.optional(v.id("providerAPIs")),
16
- baseUrl: v.string(),
17
- authType: v.string(),
18
- authHeader: v.string(),
19
- authPrefix: v.string(),
20
- encryptedMasterKey: v.string(),
21
- rateLimitPerUser: v.number(),
22
- rateLimitPerDay: v.number(),
23
- pricePerRequest: v.number(),
24
- },
25
- handler: async (ctx, args) => {
26
- const now = Date.now();
27
-
28
- if (args.id) {
29
- // Update existing
30
- await ctx.db.patch(args.id, {
31
- baseUrl: args.baseUrl,
32
- authType: args.authType,
33
- authHeader: args.authHeader,
34
- authPrefix: args.authPrefix,
35
- encryptedMasterKey: args.encryptedMasterKey,
36
- rateLimitPerUser: args.rateLimitPerUser,
37
- rateLimitPerDay: args.rateLimitPerDay,
38
- pricePerRequest: args.pricePerRequest,
39
- updatedAt: now,
40
- });
41
- return args.id;
42
- }
43
-
44
- // Create new
45
- return await ctx.db.insert("providerDirectCall", {
46
- providerId: args.providerId,
47
- apiId: args.apiId,
48
- baseUrl: args.baseUrl,
49
- authType: args.authType,
50
- authHeader: args.authHeader,
51
- authPrefix: args.authPrefix,
52
- encryptedMasterKey: args.encryptedMasterKey,
53
- rateLimitPerUser: args.rateLimitPerUser,
54
- rateLimitPerDay: args.rateLimitPerDay,
55
- pricePerRequest: args.pricePerRequest,
56
- status: "draft",
57
- createdAt: now,
58
- updatedAt: now,
59
- });
60
- },
61
- });
62
-
63
- /**
64
- * Save Direct Call config with token auth (used by frontend)
65
- */
66
- export const saveConfig = mutation({
67
- args: {
68
- token: v.string(),
69
- config: v.object({
70
- apiId: v.string(),
71
- baseUrl: v.string(),
72
- authType: v.string(),
73
- authHeader: v.string(),
74
- authPrefix: v.string(),
75
- masterApiKey: v.optional(v.string()),
76
- rateLimitPerUser: v.number(),
77
- rateLimitPerDay: v.number(),
78
- pricePerRequest: v.number(),
79
- status: v.string(),
80
- allowCustomerKeys: v.optional(v.boolean()),
81
- requireCustomerKeys: v.optional(v.boolean()),
82
- }),
83
- },
84
- handler: async (ctx, args) => {
85
- // Verify session (unified: agentSessions first, fallback to legacy sessions)
86
- let providerId: any = null;
87
-
88
- const agentSession = await ctx.db
89
- .query("agentSessions")
90
- .withIndex("by_sessionToken", (q) => q.eq("sessionToken", args.token))
91
- .first();
92
-
93
- if (agentSession) {
94
- const provider = await ctx.db
95
- .query("providers")
96
- .withIndex("by_workspaceId", (q) => q.eq("workspaceId", agentSession.workspaceId))
97
- .first();
98
- if (!provider) throw new Error("No provider linked to workspace");
99
- providerId = provider._id;
100
- } else {
101
- // Fallback: legacy sessions
102
- const session = await ctx.db
103
- .query("sessions")
104
- .withIndex("by_token", (q) => q.eq("token", args.token))
105
- .first();
106
- if (!session || session.expiresAt < Date.now()) {
107
- throw new Error("Invalid or expired session");
108
- }
109
- providerId = session.providerId;
110
- }
111
-
112
- const now = Date.now();
113
- const { config } = args;
114
-
115
- // Check if config already exists for this API
116
- const existing = await ctx.db
117
- .query("providerDirectCall")
118
- .filter((q) => q.eq(q.field("apiId"), config.apiId as any))
119
- .first();
120
-
121
- if (existing) {
122
- // Update existing
123
- const updateData: Record<string, unknown> = {
124
- baseUrl: config.baseUrl,
125
- authType: config.authType,
126
- authHeader: config.authHeader,
127
- authPrefix: config.authPrefix,
128
- rateLimitPerUser: config.rateLimitPerUser,
129
- rateLimitPerDay: config.rateLimitPerDay,
130
- pricePerRequest: config.pricePerRequest,
131
- status: config.status,
132
- allowCustomerKeys: config.allowCustomerKeys ?? true,
133
- requireCustomerKeys: config.requireCustomerKeys ?? false,
134
- updatedAt: now,
135
- };
136
-
137
- // Only update master key if provided (not empty)
138
- if (config.masterApiKey) {
139
- updateData.encryptedMasterKey = config.masterApiKey; // In prod: encrypt this
140
- }
141
-
142
- await ctx.db.patch(existing._id, updateData);
143
- return existing._id;
144
- }
145
-
146
- // Create new config
147
- return await ctx.db.insert("providerDirectCall", {
148
- providerId,
149
- apiId: config.apiId as any,
150
- baseUrl: config.baseUrl,
151
- authType: config.authType,
152
- authHeader: config.authHeader,
153
- authPrefix: config.authPrefix,
154
- encryptedMasterKey: config.masterApiKey || "",
155
- rateLimitPerUser: config.rateLimitPerUser,
156
- rateLimitPerDay: config.rateLimitPerDay,
157
- pricePerRequest: config.pricePerRequest,
158
- status: config.status as "draft" | "testing" | "live",
159
- allowCustomerKeys: config.allowCustomerKeys ?? true,
160
- requireCustomerKeys: config.requireCustomerKeys ?? false,
161
- createdAt: now,
162
- updatedAt: now,
163
- });
164
- },
165
- });
166
-
167
- /**
168
- * Create/update an action for a Direct Call config
169
- */
170
- export const saveAction = mutation({
171
- args: {
172
- id: v.optional(v.id("providerActions")),
173
- directCallId: v.id("providerDirectCall"),
174
- name: v.string(),
175
- displayName: v.string(),
176
- description: v.string(),
177
- method: v.string(),
178
- path: v.string(),
179
- params: v.array(v.object({
180
- name: v.string(),
181
- type: v.string(),
182
- required: v.boolean(),
183
- description: v.string(),
184
- default: v.optional(v.any()),
185
- in: v.string(),
186
- })),
187
- responseMapping: v.array(v.object({
188
- name: v.string(),
189
- path: v.string(),
190
- })),
191
- enabled: v.boolean(),
192
- },
193
- handler: async (ctx, args) => {
194
- const now = Date.now();
195
-
196
- if (args.id) {
197
- // Update existing
198
- await ctx.db.patch(args.id, {
199
- name: args.name,
200
- displayName: args.displayName,
201
- description: args.description,
202
- method: args.method,
203
- path: args.path,
204
- params: args.params,
205
- responseMapping: args.responseMapping,
206
- enabled: args.enabled,
207
- updatedAt: now,
208
- });
209
- return args.id;
210
- }
211
-
212
- // Create new
213
- return await ctx.db.insert("providerActions", {
214
- directCallId: args.directCallId,
215
- name: args.name,
216
- displayName: args.displayName,
217
- description: args.description,
218
- method: args.method,
219
- path: args.path,
220
- params: args.params,
221
- responseMapping: args.responseMapping,
222
- enabled: args.enabled,
223
- createdAt: now,
224
- updatedAt: now,
225
- });
226
- },
227
- });
228
-
229
- /**
230
- * Delete an action
231
- */
232
- export const deleteAction = mutation({
233
- args: {
234
- id: v.id("providerActions"),
235
- },
236
- handler: async (ctx, args) => {
237
- await ctx.db.delete(args.id);
238
- return { success: true };
239
- },
240
- });
241
-
242
- /**
243
- * Publish Direct Call - set status to live
244
- * Also marks apiListed earn progress for the provider's workspace
245
- */
246
- export const publishDirectCall = mutation({
247
- args: {
248
- id: v.id("providerDirectCall"),
249
- },
250
- handler: async (ctx, args) => {
251
- const now = Date.now();
252
-
253
- // Get the direct call config to find the provider
254
- const config = await ctx.db.get(args.id);
255
- if (!config) {
256
- throw new Error("Direct Call config not found");
257
- }
258
-
259
- await ctx.db.patch(args.id, {
260
- status: "live",
261
- publishedAt: now,
262
- updatedAt: now,
263
- });
264
-
265
- // Try to mark apiListed for earn progress
266
- // First, get the provider to find their session/workspace
267
- const provider = await ctx.db.get(config.providerId);
268
- if (provider) {
269
- // Find workspace by provider email
270
- const workspace = await ctx.db
271
- .query("workspaces")
272
- .withIndex("by_email", (q) => q.eq("email", provider.email))
273
- .first();
274
-
275
- if (workspace) {
276
- // Update earn progress
277
- const earnProgress = await ctx.db
278
- .query("earnProgress")
279
- .withIndex("by_workspaceId", (q) => q.eq("workspaceId", workspace._id))
280
- .first();
281
-
282
- if (earnProgress && !earnProgress.apiListed) {
283
- const newTotal = calculateEarnTotal({ ...earnProgress, apiListed: true });
284
- await ctx.db.patch(earnProgress._id, {
285
- apiListed: true,
286
- apiListedAt: now,
287
- totalEarned: newTotal,
288
- updatedAt: now,
289
- });
290
- // Add 10 calls to workspace limit
291
- await ctx.db.patch(workspace._id, {
292
- usageLimit: workspace.usageLimit + 10,
293
- updatedAt: now,
294
- });
295
- } else if (!earnProgress) {
296
- // Create earn progress with apiListed
297
- await ctx.db.insert("earnProgress", {
298
- workspaceId: workspace._id,
299
- firstDirectCall: false,
300
- apisUsed: [],
301
- apisUsedComplete: false,
302
- agentListed: false,
303
- apiListed: true,
304
- apiListedAt: now,
305
- byokSetup: false,
306
- githubStarred: false,
307
- twitterFollowed: false,
308
- referralCount: 0,
309
- totalEarned: 10,
310
- createdAt: now,
311
- updatedAt: now,
312
- });
313
- // Add 10 calls to workspace limit
314
- await ctx.db.patch(workspace._id, {
315
- usageLimit: workspace.usageLimit + 10,
316
- updatedAt: now,
317
- });
318
- }
319
- }
320
- }
321
-
322
- return { success: true, publishedAt: now };
323
- },
324
- });
325
-
326
- // Helper to calculate earn total (duplicated to avoid circular import)
327
- function calculateEarnTotal(progress: any): number {
328
- let total = 0;
329
- if (progress.firstDirectCall) total += 15;
330
- if (progress.apisUsedComplete) total += 10;
331
- if (progress.agentListed) total += 10;
332
- if (progress.apiListed) total += 10;
333
- if (progress.byokSetup) total += 5;
334
- if (progress.githubStarred) total += 10;
335
- if (progress.twitterFollowed) total += 5;
336
- total += (progress.referralCount || 0) * 10;
337
- return total;
338
- }
339
-
340
- /**
341
- * Set Direct Call status (draft, testing, live)
342
- */
343
- export const setStatus = mutation({
344
- args: {
345
- id: v.id("providerDirectCall"),
346
- status: v.string(),
347
- },
348
- handler: async (ctx, args) => {
349
- const now = Date.now();
350
- const update: { status: string; updatedAt: number; publishedAt?: number } = {
351
- status: args.status,
352
- updatedAt: now,
353
- };
354
- if (args.status === "live") {
355
- update.publishedAt = now;
356
- }
357
- await ctx.db.patch(args.id, update);
358
- return { success: true };
359
- },
360
- });
361
-
362
- // ============================================
363
- // QUERIES
364
- // ============================================
365
-
366
- /**
367
- * Get Direct Call config by providerId
368
- */
369
- export const getDirectCallConfig = query({
370
- args: {
371
- providerId: v.id("providers"),
372
- },
373
- handler: async (ctx, args) => {
374
- return await ctx.db
375
- .query("providerDirectCall")
376
- .withIndex("by_providerId", (q) => q.eq("providerId", args.providerId))
377
- .first();
378
- },
379
- });
380
-
381
- /**
382
- * Get Direct Call config by ID
383
- */
384
- export const getDirectCallConfigById = query({
385
- args: {
386
- id: v.id("providerDirectCall"),
387
- },
388
- handler: async (ctx, args) => {
389
- return await ctx.db.get(args.id);
390
- },
391
- });
392
-
393
- /**
394
- * Get Direct Call config by API ID
395
- */
396
- export const getDirectCallConfigByApiId = query({
397
- args: {
398
- apiId: v.string(), // Accept string since that's what frontend passes
399
- },
400
- handler: async (ctx, args) => {
401
- return await ctx.db
402
- .query("providerDirectCall")
403
- .filter((q) => q.eq(q.field("apiId"), args.apiId as any))
404
- .first();
405
- },
406
- });
407
-
408
- /**
409
- * Get all actions for a Direct Call config
410
- */
411
- export const getActions = query({
412
- args: {
413
- directCallId: v.id("providerDirectCall"),
414
- },
415
- handler: async (ctx, args) => {
416
- return await ctx.db
417
- .query("providerActions")
418
- .withIndex("by_directCallId", (q) => q.eq("directCallId", args.directCallId))
419
- .collect();
420
- },
421
- });
422
-
423
- /**
424
- * Get single action by directCallId + name
425
- */
426
- export const getActionByName = query({
427
- args: {
428
- directCallId: v.id("providerDirectCall"),
429
- name: v.string(),
430
- },
431
- handler: async (ctx, args) => {
432
- return await ctx.db
433
- .query("providerActions")
434
- .withIndex("by_directCallId_name", (q) =>
435
- q.eq("directCallId", args.directCallId).eq("name", args.name)
436
- )
437
- .first();
438
- },
439
- });
440
-
441
- /**
442
- * Get action by ID
443
- */
444
- export const getActionById = query({
445
- args: {
446
- id: v.id("providerActions"),
447
- },
448
- handler: async (ctx, args) => {
449
- return await ctx.db.get(args.id);
450
- },
451
- });
452
-
453
- /**
454
- * DEBUG: Get all Direct Call configs
455
- */
456
- export const getAllConfigs = query({
457
- args: {},
458
- handler: async (ctx) => {
459
- return await ctx.db.query("providerDirectCall").collect();
460
- },
461
- });
462
-
463
- /**
464
- * Get all live Direct Call configs (for public API discovery)
465
- */
466
- export const getLiveConfigs = query({
467
- args: {},
468
- handler: async (ctx) => {
469
- return await ctx.db
470
- .query("providerDirectCall")
471
- .withIndex("by_status", (q) => q.eq("status", "live"))
472
- .collect();
473
- },
474
- });
475
-
476
- /**
477
- * Get Direct Call config by API ID (for test console)
478
- */
479
- export const getConfig = query({
480
- args: {
481
- apiId: v.string(),
482
- },
483
- handler: async (ctx, args) => {
484
- // Try as providerAPIs ID first
485
- const config = await ctx.db
486
- .query("providerDirectCall")
487
- .withIndex("by_apiId")
488
- .filter((q) => q.eq(q.field("apiId"), args.apiId as any))
489
- .first();
490
-
491
- return config;
492
- },
493
- });
494
-
495
- /**
496
- * Get Direct Call config by API slug (for MCP/agent execution)
497
- * Looks up API by name, then gets the Direct Call config
498
- */
499
- export const getByApiSlug = query({
500
- args: {
501
- slug: v.string(),
502
- },
503
- handler: async (ctx, args) => {
504
- // Normalize slug (lowercase, replace spaces/dashes)
505
- const normalizedSlug = args.slug.toLowerCase().replace(/[\s-]/g, '_');
506
-
507
- // Find API by name (case-insensitive match)
508
- const apis = await ctx.db.query("providerAPIs").collect();
509
- const api = apis.find(a =>
510
- a.name.toLowerCase().replace(/[\s-]/g, '_') === normalizedSlug ||
511
- a.name.toLowerCase() === args.slug.toLowerCase()
512
- );
513
-
514
- if (!api) {
515
- return null;
516
- }
517
-
518
- // Get Direct Call config for this API
519
- const config = await ctx.db
520
- .query("providerDirectCall")
521
- .withIndex("by_apiId")
522
- .filter((q) => q.eq(q.field("apiId"), api._id))
523
- .first();
524
-
525
- if (!config || config.status !== 'live') {
526
- return null;
527
- }
528
-
529
- return {
530
- ...config,
531
- apiName: api.name,
532
- apiSlug: normalizedSlug,
533
- };
534
- },
535
- });
536
-
537
- // ============================================
538
- // TEST ACTION
539
- // ============================================
540
-
541
- /**
542
- * Test an action by calling the actual provider API
543
- * For V1: Provider passes their own test key
544
- */
545
- export const testAction = mutation({
546
- args: {
547
- token: v.string(),
548
- directCallId: v.id("providerDirectCall"),
549
- actionId: v.id("providerActions"),
550
- params: v.record(v.string(), v.any()),
551
- testKey: v.optional(v.string()),
552
- },
553
- handler: async (ctx, args) => {
554
- const startTime = Date.now();
555
-
556
- // 1. Verify provider session (unified: agentSessions first, fallback to legacy)
557
- let sessionProviderId: any = null;
558
-
559
- const agentSession = await ctx.db
560
- .query("agentSessions")
561
- .withIndex("by_sessionToken", (q) => q.eq("sessionToken", args.token))
562
- .first();
563
-
564
- if (agentSession) {
565
- const provider = await ctx.db
566
- .query("providers")
567
- .withIndex("by_workspaceId", (q) => q.eq("workspaceId", agentSession.workspaceId))
568
- .first();
569
- if (provider) sessionProviderId = provider._id;
570
- } else {
571
- const session = await ctx.db
572
- .query("sessions")
573
- .withIndex("by_token", (q) => q.eq("token", args.token))
574
- .first();
575
- if (session && session.expiresAt >= Date.now()) {
576
- sessionProviderId = session.providerId;
577
- }
578
- }
579
-
580
- if (!sessionProviderId) {
581
- return {
582
- success: false,
583
- error: "Unauthorized - invalid or expired session",
584
- latencyMs: Date.now() - startTime,
585
- };
586
- }
587
-
588
- // 2. Get directCallConfig
589
- const config = await ctx.db.get(args.directCallId);
590
- if (!config) {
591
- return {
592
- success: false,
593
- error: "Direct Call config not found",
594
- latencyMs: Date.now() - startTime,
595
- };
596
- }
597
-
598
- // Verify ownership
599
- if (config.providerId !== sessionProviderId) {
600
- return {
601
- success: false,
602
- error: "Unauthorized - you don't own this config",
603
- latencyMs: Date.now() - startTime,
604
- };
605
- }
606
-
607
- // 3. Get action
608
- const action = await ctx.db.get(args.actionId);
609
- if (!action) {
610
- return {
611
- success: false,
612
- error: "Action not found",
613
- latencyMs: Date.now() - startTime,
614
- };
615
- }
616
-
617
- // 4. Get API key (use testKey if provided, else use stored key)
618
- // Note: For production, encryptedMasterKey would need server-side decryption
619
- // For V1 test console, we use testKey directly
620
- const apiKey = args.testKey || config.encryptedMasterKey;
621
- if (!apiKey) {
622
- return {
623
- success: false,
624
- error: "No API key provided. Add a test key or configure master key.",
625
- latencyMs: Date.now() - startTime,
626
- };
627
- }
628
-
629
- // 5. Build URL with path params
630
- let path = action.path;
631
- const queryParams: Record<string, string> = {};
632
- const bodyParams: Record<string, unknown> = {};
633
-
634
- for (const paramDef of action.params) {
635
- const value = args.params[paramDef.name];
636
- if (value === undefined || value === "") continue;
637
-
638
- if (paramDef.in === "path") {
639
- // Replace {paramName} in path
640
- path = path.replace(`{${paramDef.name}}`, String(value));
641
- } else if (paramDef.in === "query") {
642
- queryParams[paramDef.name] = String(value);
643
- } else if (paramDef.in === "body") {
644
- bodyParams[paramDef.name] = value;
645
- }
646
- }
647
-
648
- // Build full URL
649
- let url = config.baseUrl.replace(/\/$/, "") + path;
650
- const queryString = new URLSearchParams(queryParams).toString();
651
- if (queryString) {
652
- url += (url.includes("?") ? "&" : "?") + queryString;
653
- }
654
-
655
- // 6. Build headers with auth
656
- const headers: Record<string, string> = {
657
- "Content-Type": "application/json",
658
- "Accept": "application/json",
659
- };
660
-
661
- // Add auth header
662
- if (config.authType !== "none" && apiKey) {
663
- const authValue = config.authPrefix
664
- ? `${config.authPrefix}${apiKey}`
665
- : apiKey;
666
- headers[config.authHeader] = authValue;
667
- }
668
-
669
- // 7. Build fetch options
670
- const fetchOptions: RequestInit = {
671
- method: action.method,
672
- headers,
673
- };
674
-
675
- // Add body for non-GET requests
676
- if (action.method !== "GET" && Object.keys(bodyParams).length > 0) {
677
- fetchOptions.body = JSON.stringify(bodyParams);
678
- }
679
-
680
- // 8. Execute request
681
- try {
682
- const response = await fetch(url, fetchOptions);
683
- const latencyMs = Date.now() - startTime;
684
-
685
- // Try to parse response as JSON, fallback to text
686
- let data: unknown;
687
- const contentType = response.headers.get("content-type") || "";
688
-
689
- if (contentType.includes("application/json")) {
690
- try {
691
- data = await response.json();
692
- } catch {
693
- data = await response.text();
694
- }
695
- } else {
696
- data = await response.text();
697
- }
698
-
699
- return {
700
- success: response.ok,
701
- status: response.status,
702
- data,
703
- latencyMs,
704
- };
705
- } catch (error) {
706
- return {
707
- success: false,
708
- error: error instanceof Error ? error.message : "Request failed",
709
- latencyMs: Date.now() - startTime,
710
- };
711
- }
712
- },
713
- });