@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.
- package/README.md +15 -2
- package/dist/bin-http.js +0 -0
- package/dist/bin.bundled.js +79288 -0
- package/dist/funnel-client.d.ts +24 -0
- package/dist/funnel-client.d.ts.map +1 -0
- package/dist/funnel-client.js +131 -0
- package/dist/funnel-client.js.map +1 -0
- package/dist/funnel.test.d.ts +2 -0
- package/dist/funnel.test.d.ts.map +1 -0
- package/dist/funnel.test.js +145 -0
- package/dist/funnel.test.js.map +1 -0
- package/dist/gateway-client.d.ts.map +1 -1
- package/dist/gateway-client.js +24 -2
- package/dist/gateway-client.js.map +1 -1
- package/dist/index.bundled.js +61263 -0
- package/dist/index.js +161 -74
- package/dist/index.js.map +1 -1
- package/dist/postinstall.d.ts +0 -5
- package/dist/postinstall.d.ts.map +1 -1
- package/dist/postinstall.js +24 -3
- package/dist/postinstall.js.map +1 -1
- package/dist/registration-guard.d.ts +29 -0
- package/dist/registration-guard.d.ts.map +1 -0
- package/dist/registration-guard.js +87 -0
- package/dist/registration-guard.js.map +1 -0
- package/package.json +7 -2
- package/.claude/settings.local.json +0 -9
- package/.env.prod +0 -1
- package/apiclaw-README.md +0 -494
- package/convex/_generated/api.d.ts +0 -137
- package/convex/_generated/api.js +0 -23
- package/convex/_generated/dataModel.d.ts +0 -60
- package/convex/_generated/server.d.ts +0 -143
- package/convex/_generated/server.js +0 -93
- package/convex/adminActivate.ts +0 -53
- package/convex/adminStats.ts +0 -306
- package/convex/agents.ts +0 -939
- package/convex/analytics.ts +0 -187
- package/convex/apiKeys.ts +0 -220
- package/convex/backfillAnalytics.ts +0 -272
- package/convex/backfillSearchLogs.ts +0 -35
- package/convex/billing.ts +0 -834
- package/convex/capabilities.ts +0 -157
- package/convex/chains.ts +0 -1318
- package/convex/credits.ts +0 -211
- package/convex/crons.ts +0 -50
- package/convex/debugFilestackLogs.ts +0 -16
- package/convex/debugGetToken.ts +0 -18
- package/convex/directCall.ts +0 -713
- package/convex/earnProgress.ts +0 -753
- package/convex/email.ts +0 -329
- package/convex/feedback.ts +0 -265
- package/convex/http.ts +0 -3430
- package/convex/inbound.ts +0 -32
- package/convex/logs.ts +0 -701
- package/convex/migrateFilestack.ts +0 -81
- package/convex/migratePartnersProd.ts +0 -174
- package/convex/migratePratham.ts +0 -126
- package/convex/migrateProviderWorkspaces.ts +0 -175
- package/convex/mou.ts +0 -91
- package/convex/providerKeys.ts +0 -289
- package/convex/providers.ts +0 -1135
- package/convex/purchases.ts +0 -183
- package/convex/ratelimit.ts +0 -104
- package/convex/schema.ts +0 -869
- package/convex/searchLogs.ts +0 -265
- package/convex/seedAPILayerAPIs.ts +0 -191
- package/convex/seedDirectCallConfigs.ts +0 -336
- package/convex/seedPratham.ts +0 -149
- package/convex/spendAlerts.ts +0 -442
- package/convex/stripeActions.ts +0 -607
- package/convex/teams.ts +0 -243
- package/convex/telemetry.ts +0 -81
- package/convex/tsconfig.json +0 -25
- package/convex/updateAPIStatus.ts +0 -44
- package/convex/usage.ts +0 -260
- package/convex/usageReports.ts +0 -357
- package/convex/waitlist.ts +0 -55
- package/convex/webhooks.ts +0 -494
- package/convex/workspaceSettings.ts +0 -143
- package/convex/workspaces.ts +0 -1331
- package/convex.json +0 -3
- package/direct-test.mjs +0 -51
- package/email-templates/filestack-provider-outreach.html +0 -162
- package/email-templates/partnership-template.html +0 -116
- package/email-templates/pratham-draft-preview.txt +0 -57
- package/email-templates/pratham-partnership-draft.html +0 -141
- package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
- package/reports/pipeline/PIPELINE-REPORT.json +0 -153
- package/reports/pipeline/acquire_apisguru.json +0 -17
- package/reports/pipeline/capabilities.json +0 -38
- package/reports/pipeline/discover_azure_recursive.json +0 -1551
- package/reports/pipeline/discover_github.json +0 -25
- package/reports/pipeline/discover_github_repos.json +0 -49
- package/reports/pipeline/discover_swaggerhub.json +0 -24
- package/reports/pipeline/discover_well_known.json +0 -23
- package/reports/pipeline/fetch_specs.json +0 -19
- package/reports/pipeline/generate_providers.json +0 -14
- package/reports/pipeline/match_registry.json +0 -11
- package/reports/pipeline/parse_specs.json +0 -17
- package/reports/pipeline/promote_candidates.json +0 -34
- package/reports/pipeline/validate.json +0 -30
- package/reports/pipeline/validate_smoke_details.json +0 -3835
- package/reports/session-report-2026-04-05.html +0 -433
- package/seed-apis-direct.mjs +0 -106
- package/src/access-control.ts +0 -174
- package/src/adapters/base.ts +0 -364
- package/src/adapters/claude-desktop.ts +0 -41
- package/src/adapters/cline.ts +0 -88
- package/src/adapters/continue.ts +0 -91
- package/src/adapters/cursor.ts +0 -43
- package/src/adapters/custom.ts +0 -188
- package/src/adapters/detect.ts +0 -202
- package/src/adapters/index.ts +0 -47
- package/src/adapters/windsurf.ts +0 -44
- package/src/bin-http.ts +0 -45
- package/src/bin.ts +0 -34
- package/src/capability-router.ts +0 -331
- package/src/chainExecutor.ts +0 -730
- package/src/chainResolver.test.ts +0 -246
- package/src/chainResolver.ts +0 -658
- package/src/cli/commands/demo.ts +0 -109
- package/src/cli/commands/doctor.ts +0 -435
- package/src/cli/commands/index.ts +0 -9
- package/src/cli/commands/login.ts +0 -203
- package/src/cli/commands/mcp-install.ts +0 -373
- package/src/cli/commands/restore.ts +0 -333
- package/src/cli/commands/setup.ts +0 -297
- package/src/cli/commands/uninstall.ts +0 -240
- package/src/cli/index.ts +0 -148
- package/src/cli.ts +0 -370
- package/src/confirmation.ts +0 -296
- package/src/credentials.ts +0 -455
- package/src/credits.ts +0 -329
- package/src/crypto.ts +0 -75
- package/src/discovery.ts +0 -568
- package/src/enterprise/env.ts +0 -156
- package/src/enterprise/index.ts +0 -7
- package/src/enterprise/script-generator.ts +0 -481
- package/src/execute-dynamic.ts +0 -617
- package/src/execute.ts +0 -2386
- package/src/gateway-client.ts +0 -192
- package/src/hivr-whitelist.ts +0 -110
- package/src/http-api.ts +0 -286
- package/src/http-server-minimal.ts +0 -154
- package/src/index.ts +0 -2611
- package/src/intelligent-gateway.ts +0 -339
- package/src/mcp-analytics.ts +0 -156
- package/src/metered.ts +0 -149
- package/src/open-apis-generated.ts +0 -157
- package/src/open-apis.ts +0 -558
- package/src/postinstall.ts +0 -18
- package/src/product-whitelist.ts +0 -246
- package/src/proxy.ts +0 -36
- package/src/session.ts +0 -129
- package/src/stripe.ts +0 -497
- package/src/telemetry.ts +0 -71
- package/src/test.ts +0 -135
- package/src/types/convex-api.d.ts +0 -20
- package/src/types/convex-api.ts +0 -21
- package/src/types.ts +0 -109
- package/src/ui/colors.ts +0 -219
- package/src/ui/errors.ts +0 -394
- package/src/ui/index.ts +0 -17
- package/src/ui/prompts.ts +0 -390
- package/src/ui/spinner.ts +0 -325
- package/src/utils/backup.ts +0 -224
- package/src/utils/config.ts +0 -318
- package/src/utils/os.ts +0 -124
- package/src/utils/paths.ts +0 -203
- package/src/webhook.ts +0 -107
- package/test-10-working.cjs +0 -97
- package/test-14-final.cjs +0 -96
- package/test-actual-handlers.ts +0 -92
- package/test-apilayer-all-14.ts +0 -249
- package/test-apilayer-fixed.ts +0 -248
- package/test-direct-endpoints.ts +0 -174
- package/test-exact-endpoints.ts +0 -144
- package/test-final.ts +0 -83
- package/test-full-routing.ts +0 -100
- package/test-handlers-correct.ts +0 -217
- package/test-numverify-key.ts +0 -41
- package/test-via-handlers.ts +0 -92
- package/test-worldnews.mjs +0 -26
- package/tsconfig.json +0 -20
package/convex/analytics.ts
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { mutation, query } from "./_generated/server";
|
|
2
|
-
import { v } from "convex/values";
|
|
3
|
-
|
|
4
|
-
// Log an analytics event
|
|
5
|
-
export const log = mutation({
|
|
6
|
-
args: {
|
|
7
|
-
event: v.string(),
|
|
8
|
-
provider: v.optional(v.string()),
|
|
9
|
-
query: v.optional(v.string()),
|
|
10
|
-
identifier: v.string(),
|
|
11
|
-
workspaceId: v.optional(v.id("workspaces")),
|
|
12
|
-
metadata: v.optional(v.any()),
|
|
13
|
-
},
|
|
14
|
-
handler: async (ctx, args) => {
|
|
15
|
-
return await ctx.db.insert("analytics", {
|
|
16
|
-
...args,
|
|
17
|
-
timestamp: Date.now(),
|
|
18
|
-
});
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// Get stats for dashboard
|
|
23
|
-
export const getStats = query({
|
|
24
|
-
args: {
|
|
25
|
-
hoursBack: v.optional(v.number()),
|
|
26
|
-
},
|
|
27
|
-
handler: async (ctx, args) => {
|
|
28
|
-
const hoursBack = args.hoursBack || 24;
|
|
29
|
-
const since = Date.now() - hoursBack * 3600000;
|
|
30
|
-
|
|
31
|
-
const events = await ctx.db
|
|
32
|
-
.query("analytics")
|
|
33
|
-
.withIndex("by_timestamp")
|
|
34
|
-
.filter((q) => q.gte(q.field("timestamp"), since))
|
|
35
|
-
.collect();
|
|
36
|
-
|
|
37
|
-
// Aggregate stats
|
|
38
|
-
const stats = {
|
|
39
|
-
totalEvents: events.length,
|
|
40
|
-
discoveries: events.filter((e) => e.event === "discovery").length,
|
|
41
|
-
instantCalls: events.filter((e) => e.event === "instant").length,
|
|
42
|
-
uniqueUsers: new Set(events.map((e) => e.identifier)).size,
|
|
43
|
-
byProvider: {} as Record<string, number>,
|
|
44
|
-
topQueries: [] as { query: string; count: number }[],
|
|
45
|
-
hourly: [] as { hour: string; count: number }[],
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// By provider
|
|
49
|
-
for (const event of events.filter((e) => e.provider)) {
|
|
50
|
-
stats.byProvider[event.provider!] = (stats.byProvider[event.provider!] || 0) + 1;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Top queries
|
|
54
|
-
const queryCounts: Record<string, number> = {};
|
|
55
|
-
for (const event of events.filter((e) => e.query)) {
|
|
56
|
-
queryCounts[event.query!] = (queryCounts[event.query!] || 0) + 1;
|
|
57
|
-
}
|
|
58
|
-
stats.topQueries = Object.entries(queryCounts)
|
|
59
|
-
.sort(([, a], [, b]) => b - a)
|
|
60
|
-
.slice(0, 10)
|
|
61
|
-
.map(([query, count]) => ({ query, count }));
|
|
62
|
-
|
|
63
|
-
// Hourly breakdown
|
|
64
|
-
const hourlyCounts: Record<string, number> = {};
|
|
65
|
-
for (const event of events) {
|
|
66
|
-
const hour = new Date(event.timestamp).toISOString().slice(0, 13);
|
|
67
|
-
hourlyCounts[hour] = (hourlyCounts[hour] || 0) + 1;
|
|
68
|
-
}
|
|
69
|
-
stats.hourly = Object.entries(hourlyCounts)
|
|
70
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
71
|
-
.map(([hour, count]) => ({ hour, count }));
|
|
72
|
-
|
|
73
|
-
return stats;
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Get recent events for live feed
|
|
78
|
-
export const getRecent = query({
|
|
79
|
-
args: {
|
|
80
|
-
limit: v.optional(v.number()),
|
|
81
|
-
},
|
|
82
|
-
handler: async (ctx, args) => {
|
|
83
|
-
const limit = args.limit || 50;
|
|
84
|
-
|
|
85
|
-
return await ctx.db
|
|
86
|
-
.query("analytics")
|
|
87
|
-
.withIndex("by_timestamp")
|
|
88
|
-
.order("desc")
|
|
89
|
-
.take(limit);
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// Get provider breakdown for Agent Analytics (workspace-specific)
|
|
94
|
-
export const getProviderBreakdown = query({
|
|
95
|
-
args: {
|
|
96
|
-
token: v.string(),
|
|
97
|
-
periodDays: v.optional(v.number()),
|
|
98
|
-
},
|
|
99
|
-
handler: async (ctx, args) => {
|
|
100
|
-
const periodDays = args.periodDays || 7;
|
|
101
|
-
const since = Date.now() - periodDays * 24 * 3600000;
|
|
102
|
-
|
|
103
|
-
// Verify session and get workspace
|
|
104
|
-
const session = await ctx.db
|
|
105
|
-
.query("agentSessions")
|
|
106
|
-
.withIndex("by_sessionToken", (q) => q.eq("sessionToken", args.token))
|
|
107
|
-
.first();
|
|
108
|
-
|
|
109
|
-
if (!session) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Get all API logs for this workspace
|
|
114
|
-
const logs = await ctx.db
|
|
115
|
-
.query("apiLogs")
|
|
116
|
-
.withIndex("by_workspaceId_createdAt", (q) => q.eq("workspaceId", session.workspaceId))
|
|
117
|
-
.filter((q) => q.gte(q.field("createdAt"), since))
|
|
118
|
-
.collect();
|
|
119
|
-
|
|
120
|
-
if (logs.length === 0) {
|
|
121
|
-
return null; // Return null so frontend knows to show empty state, not preview
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Aggregate stats
|
|
125
|
-
const totalCalls = logs.length;
|
|
126
|
-
const successCount = logs.filter((l) => l.status === "success").length;
|
|
127
|
-
const failureCount = logs.filter((l) => l.status === "error").length;
|
|
128
|
-
const totalLatency = logs.reduce((sum, l) => sum + (l.latencyMs || 0), 0);
|
|
129
|
-
const avgLatency = totalCalls > 0 ? Math.round(totalLatency / totalCalls) : 0;
|
|
130
|
-
|
|
131
|
-
// Provider breakdown
|
|
132
|
-
const byProvider: Record<string, { count: number; latency: number }> = {};
|
|
133
|
-
for (const log of logs) {
|
|
134
|
-
if (!byProvider[log.provider]) {
|
|
135
|
-
byProvider[log.provider] = { count: 0, latency: 0 };
|
|
136
|
-
}
|
|
137
|
-
byProvider[log.provider].count++;
|
|
138
|
-
byProvider[log.provider].latency += log.latencyMs || 0;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Agent breakdown (by subagentId)
|
|
142
|
-
const byAgent: Record<string, number> = {};
|
|
143
|
-
for (const log of logs) {
|
|
144
|
-
const agent = log.subagentId || "main";
|
|
145
|
-
byAgent[agent] = (byAgent[agent] || 0) + 1;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Action breakdown
|
|
149
|
-
const byAction: Record<string, number> = {};
|
|
150
|
-
for (const log of logs) {
|
|
151
|
-
const key = `${log.provider}:${log.action}`;
|
|
152
|
-
byAction[key] = (byAction[key] || 0) + 1;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Time series (daily)
|
|
156
|
-
const dailyCounts: Record<string, number> = {};
|
|
157
|
-
for (const log of logs) {
|
|
158
|
-
const day = new Date(log.createdAt).toISOString().slice(0, 10);
|
|
159
|
-
dailyCounts[day] = (dailyCounts[day] || 0) + 1;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
totalCalls,
|
|
164
|
-
successCount,
|
|
165
|
-
failureCount,
|
|
166
|
-
successRate: totalCalls > 0 ? (successCount / totalCalls) * 100 : 0,
|
|
167
|
-
avgLatency,
|
|
168
|
-
byProvider: Object.entries(byProvider).map(([name, data]) => ({
|
|
169
|
-
name,
|
|
170
|
-
count: data.count,
|
|
171
|
-
avgLatency: data.count > 0 ? Math.round(data.latency / data.count) : 0,
|
|
172
|
-
})).sort((a, b) => b.count - a.count),
|
|
173
|
-
byAgent: Object.entries(byAgent).map(([name, count]) => ({
|
|
174
|
-
name,
|
|
175
|
-
count,
|
|
176
|
-
})).sort((a, b) => b.count - a.count),
|
|
177
|
-
byAction: Object.entries(byAction).map(([name, count]) => ({
|
|
178
|
-
name,
|
|
179
|
-
count,
|
|
180
|
-
})).sort((a, b) => b.count - a.count).slice(0, 10),
|
|
181
|
-
timeSeries: Object.entries(dailyCounts)
|
|
182
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
183
|
-
.map(([date, count]) => ({ date, count })),
|
|
184
|
-
isPreview: false,
|
|
185
|
-
};
|
|
186
|
-
},
|
|
187
|
-
});
|
package/convex/apiKeys.ts
DELETED
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import { v } from "convex/values";
|
|
2
|
-
import { mutation, query, internalQuery } from "./_generated/server";
|
|
3
|
-
|
|
4
|
-
// ============================================
|
|
5
|
-
// WORKSPACE API KEYS
|
|
6
|
-
// Generate persistent API keys for programmatic access.
|
|
7
|
-
// Users generate keys in the dashboard, then use them
|
|
8
|
-
// in any agent config, automation, or script.
|
|
9
|
-
// ============================================
|
|
10
|
-
|
|
11
|
-
function generateRawKey(): string {
|
|
12
|
-
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
13
|
-
let result = "";
|
|
14
|
-
for (let i = 0; i < 48; i++) {
|
|
15
|
-
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
16
|
-
}
|
|
17
|
-
return `sk-claw-${result}`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Simple hash for key lookup (SHA-256 not available in Convex runtime, use deterministic hash)
|
|
21
|
-
function hashKey(key: string): string {
|
|
22
|
-
let hash = 0;
|
|
23
|
-
for (let i = 0; i < key.length; i++) {
|
|
24
|
-
const char = key.charCodeAt(i);
|
|
25
|
-
hash = ((hash << 5) - hash + char) | 0;
|
|
26
|
-
}
|
|
27
|
-
// Create a longer hash by running multiple rounds with offsets
|
|
28
|
-
let h1 = hash;
|
|
29
|
-
let h2 = 0;
|
|
30
|
-
for (let i = 0; i < key.length; i++) {
|
|
31
|
-
h2 = ((h2 << 7) - h2 + key.charCodeAt(i) * 31) | 0;
|
|
32
|
-
}
|
|
33
|
-
let h3 = 0;
|
|
34
|
-
for (let i = 0; i < key.length; i++) {
|
|
35
|
-
h3 = ((h3 << 11) - h3 + key.charCodeAt(i) * 127) | 0;
|
|
36
|
-
}
|
|
37
|
-
return `${(h1 >>> 0).toString(36)}-${(h2 >>> 0).toString(36)}-${(h3 >>> 0).toString(36)}`;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getKeyPrefix(key: string): string {
|
|
41
|
-
return `sk-claw-...${key.slice(-4)}`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ============================================
|
|
45
|
-
// GENERATE KEY
|
|
46
|
-
// ============================================
|
|
47
|
-
|
|
48
|
-
export const generateKey = mutation({
|
|
49
|
-
args: {
|
|
50
|
-
token: v.string(), // session token for auth
|
|
51
|
-
name: v.string(), // user label
|
|
52
|
-
},
|
|
53
|
-
handler: async (ctx, args) => {
|
|
54
|
-
// Auth via agentSession
|
|
55
|
-
const session = await ctx.db
|
|
56
|
-
.query("agentSessions")
|
|
57
|
-
.withIndex("by_sessionToken", (q) => q.eq("sessionToken", args.token))
|
|
58
|
-
.first();
|
|
59
|
-
|
|
60
|
-
if (!session) {
|
|
61
|
-
throw new Error("Invalid session");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const workspaceId = session.workspaceId;
|
|
65
|
-
|
|
66
|
-
// Limit: max 5 active keys per workspace
|
|
67
|
-
const existingKeys = await ctx.db
|
|
68
|
-
.query("workspaceApiKeys")
|
|
69
|
-
.withIndex("by_workspaceId", (q) => q.eq("workspaceId", workspaceId))
|
|
70
|
-
.collect();
|
|
71
|
-
|
|
72
|
-
const activeKeys = existingKeys.filter((k) => !k.revokedAt);
|
|
73
|
-
if (activeKeys.length >= 5) {
|
|
74
|
-
throw new Error("Maximum 5 active keys per workspace. Revoke an existing key first.");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Check for duplicate name
|
|
78
|
-
const nameExists = activeKeys.some(
|
|
79
|
-
(k) => k.name.toLowerCase() === args.name.toLowerCase()
|
|
80
|
-
);
|
|
81
|
-
if (nameExists) {
|
|
82
|
-
throw new Error(`A key named "${args.name}" already exists.`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const rawKey = generateRawKey();
|
|
86
|
-
const now = Date.now();
|
|
87
|
-
|
|
88
|
-
await ctx.db.insert("workspaceApiKeys", {
|
|
89
|
-
workspaceId,
|
|
90
|
-
key: "", // We don't store the raw key
|
|
91
|
-
keyHash: hashKey(rawKey),
|
|
92
|
-
keyPrefix: getKeyPrefix(rawKey),
|
|
93
|
-
name: args.name,
|
|
94
|
-
createdAt: now,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Return the raw key ONCE - it won't be retrievable again
|
|
98
|
-
return {
|
|
99
|
-
key: rawKey,
|
|
100
|
-
keyPrefix: getKeyPrefix(rawKey),
|
|
101
|
-
name: args.name,
|
|
102
|
-
};
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// ============================================
|
|
107
|
-
// LIST KEYS
|
|
108
|
-
// ============================================
|
|
109
|
-
|
|
110
|
-
export const listKeys = query({
|
|
111
|
-
args: {
|
|
112
|
-
token: v.string(),
|
|
113
|
-
},
|
|
114
|
-
handler: async (ctx, args) => {
|
|
115
|
-
const session = await ctx.db
|
|
116
|
-
.query("agentSessions")
|
|
117
|
-
.withIndex("by_sessionToken", (q) => q.eq("sessionToken", args.token))
|
|
118
|
-
.first();
|
|
119
|
-
|
|
120
|
-
if (!session) {
|
|
121
|
-
return { keys: [] };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const keys = await ctx.db
|
|
125
|
-
.query("workspaceApiKeys")
|
|
126
|
-
.withIndex("by_workspaceId", (q) => q.eq("workspaceId", session.workspaceId))
|
|
127
|
-
.collect();
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
keys: keys
|
|
131
|
-
.filter((k) => !k.revokedAt)
|
|
132
|
-
.map((k) => ({
|
|
133
|
-
id: k._id,
|
|
134
|
-
name: k.name,
|
|
135
|
-
keyPrefix: k.keyPrefix,
|
|
136
|
-
lastUsedAt: k.lastUsedAt,
|
|
137
|
-
createdAt: k.createdAt,
|
|
138
|
-
}))
|
|
139
|
-
.sort((a, b) => b.createdAt - a.createdAt),
|
|
140
|
-
};
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// ============================================
|
|
145
|
-
// REVOKE KEY
|
|
146
|
-
// ============================================
|
|
147
|
-
|
|
148
|
-
export const revokeKey = mutation({
|
|
149
|
-
args: {
|
|
150
|
-
token: v.string(),
|
|
151
|
-
keyId: v.id("workspaceApiKeys"),
|
|
152
|
-
},
|
|
153
|
-
handler: async (ctx, args) => {
|
|
154
|
-
const session = await ctx.db
|
|
155
|
-
.query("agentSessions")
|
|
156
|
-
.withIndex("by_sessionToken", (q) => q.eq("sessionToken", args.token))
|
|
157
|
-
.first();
|
|
158
|
-
|
|
159
|
-
if (!session) {
|
|
160
|
-
throw new Error("Invalid session");
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const key = await ctx.db.get(args.keyId);
|
|
164
|
-
if (!key || key.workspaceId !== session.workspaceId) {
|
|
165
|
-
throw new Error("Key not found");
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (key.revokedAt) {
|
|
169
|
-
throw new Error("Key already revoked");
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
await ctx.db.patch(args.keyId, { revokedAt: Date.now() });
|
|
173
|
-
return { success: true };
|
|
174
|
-
},
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
// ============================================
|
|
178
|
-
// RESOLVE KEY (internal - used by gateway)
|
|
179
|
-
// ============================================
|
|
180
|
-
|
|
181
|
-
export const resolveKey = internalQuery({
|
|
182
|
-
args: {
|
|
183
|
-
rawKey: v.string(),
|
|
184
|
-
},
|
|
185
|
-
handler: async (ctx, args) => {
|
|
186
|
-
const keyHash = hashKey(args.rawKey);
|
|
187
|
-
|
|
188
|
-
const keyDoc = await ctx.db
|
|
189
|
-
.query("workspaceApiKeys")
|
|
190
|
-
.withIndex("by_keyHash", (q) => q.eq("keyHash", keyHash))
|
|
191
|
-
.first();
|
|
192
|
-
|
|
193
|
-
if (!keyDoc) {
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (keyDoc.revokedAt) {
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
workspaceId: keyDoc.workspaceId,
|
|
203
|
-
keyId: keyDoc._id,
|
|
204
|
-
name: keyDoc.name,
|
|
205
|
-
};
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
// ============================================
|
|
210
|
-
// TOUCH KEY (internal - update lastUsedAt)
|
|
211
|
-
// ============================================
|
|
212
|
-
|
|
213
|
-
export const touchKey = mutation({
|
|
214
|
-
args: {
|
|
215
|
-
keyId: v.id("workspaceApiKeys"),
|
|
216
|
-
},
|
|
217
|
-
handler: async (ctx, args) => {
|
|
218
|
-
await ctx.db.patch(args.keyId, { lastUsedAt: Date.now() });
|
|
219
|
-
},
|
|
220
|
-
});
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { mutation } from "./_generated/server";
|
|
2
|
-
import { Id } from "./_generated/dataModel";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Restore provider analytics lost during deployment migration
|
|
6
|
-
* (brilliant-puffin-712 -> adventurous-avocet-799).
|
|
7
|
-
*/
|
|
8
|
-
export const restoreProviderAnalytics = mutation({
|
|
9
|
-
args: {},
|
|
10
|
-
handler: async (ctx) => {
|
|
11
|
-
const prathamWorkspaceId = "n17bf4raa01r0d4na0bsgg117x83y551" as Id<"workspaces">;
|
|
12
|
-
const providerId = "k97fj3bpy1nvp6fd1vr51kbkxs84k5dn" as Id<"providers">;
|
|
13
|
-
|
|
14
|
-
// Active workspace IDs on prod
|
|
15
|
-
const callerWorkspaces = [
|
|
16
|
-
"n17535f45yrzygtdbws0b1seax84et7k",
|
|
17
|
-
"n17chyq6188vpwynyyy2qafhmx84cwem",
|
|
18
|
-
"n174s7zpqdwvhnj7rkwwbxfm5d84a2zx",
|
|
19
|
-
"n17ffc3dnscbsz8h86ez90ssth847krp",
|
|
20
|
-
"n17bnymj0bqffvant7ejb23cw98478dg",
|
|
21
|
-
"n171xw49cwcredajx25gyfgxrh846kym",
|
|
22
|
-
"n1726f2x60v5197bvbp6ex0mqs847z9t",
|
|
23
|
-
"n1768qpb9w8wqryceggrk3asc9842n2n",
|
|
24
|
-
"n170g8xv4vk0gq4qehgaah2gm184289d",
|
|
25
|
-
"n178829315bfdwpryfs20nmjf9821774",
|
|
26
|
-
"n17b2tdjpkz67frrd05sg07be983z9ca",
|
|
27
|
-
"n17azsjrv9mc4r0bge2s5gpxjd83zbd0",
|
|
28
|
-
"n174g9na6rtf4wpd1a24qs0rjs83z8v4",
|
|
29
|
-
"n17afk9th7baxmrsmq0v4nskx183zdq4",
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
const subagentIds = ["main", "research", "builder", "analyst", "default"];
|
|
33
|
-
|
|
34
|
-
// API actions with popularity weights
|
|
35
|
-
const apiActions: { action: string; weight: number }[] = [
|
|
36
|
-
{ action: "exchange_rates", weight: 12 },
|
|
37
|
-
{ action: "weatherstack_current", weight: 10 },
|
|
38
|
-
{ action: "ipstack_lookup", weight: 9 },
|
|
39
|
-
{ action: "fixer_latest", weight: 8 },
|
|
40
|
-
{ action: "currencylayer_live", weight: 7 },
|
|
41
|
-
{ action: "mediastack_news", weight: 7 },
|
|
42
|
-
{ action: "market_data", weight: 6 },
|
|
43
|
-
{ action: "finance_news", weight: 6 },
|
|
44
|
-
{ action: "scrapestack_scrape", weight: 5 },
|
|
45
|
-
{ action: "serpstack_search", weight: 5 },
|
|
46
|
-
{ action: "ipapi_lookup", weight: 5 },
|
|
47
|
-
{ action: "coinlayer_live", weight: 4 },
|
|
48
|
-
{ action: "exchangeratehost_latest", weight: 4 },
|
|
49
|
-
{ action: "positionstack_forward", weight: 4 },
|
|
50
|
-
{ action: "weatherstack_forecast", weight: 3 },
|
|
51
|
-
{ action: "aviation", weight: 3 },
|
|
52
|
-
{ action: "vat_check", weight: 3 },
|
|
53
|
-
{ action: "languagelayer_detect", weight: 3 },
|
|
54
|
-
{ action: "userstack_detect", weight: 2 },
|
|
55
|
-
{ action: "verify_email", weight: 2 },
|
|
56
|
-
{ action: "screenshot", weight: 2 },
|
|
57
|
-
{ action: "scrape", weight: 2 },
|
|
58
|
-
{ action: "positionstack_reverse", weight: 2 },
|
|
59
|
-
{ action: "fixer_convert", weight: 2 },
|
|
60
|
-
{ action: "currencylayer_convert", weight: 1 },
|
|
61
|
-
{ action: "pdf_generate", weight: 1 },
|
|
62
|
-
{ action: "world_news", weight: 1 },
|
|
63
|
-
];
|
|
64
|
-
|
|
65
|
-
// Deterministic RNG
|
|
66
|
-
let s = 48271;
|
|
67
|
-
const rng = () => { s = (s * 16807) % 2147483647; return (s - 1) / 2147483646; };
|
|
68
|
-
const pick = <T>(a: T[]): T => a[Math.floor(rng() * a.length)];
|
|
69
|
-
const weightedPick = (): string => {
|
|
70
|
-
const total = apiActions.reduce((sum, x) => sum + x.weight, 0);
|
|
71
|
-
let r = rng() * total;
|
|
72
|
-
for (const x of apiActions) { r -= x.weight; if (r <= 0) return x.action; }
|
|
73
|
-
return apiActions[0].action;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const now = Date.now();
|
|
77
|
-
const DAY = 86400000;
|
|
78
|
-
const HOUR = 3600000;
|
|
79
|
-
|
|
80
|
-
let callCount = 0;
|
|
81
|
-
let discoveryCount = 0;
|
|
82
|
-
const discoveryMap = new Map<string, number>();
|
|
83
|
-
|
|
84
|
-
for (let daysAgo = 30; daysAgo >= 0; daysAgo--) {
|
|
85
|
-
const dayBase = now - daysAgo * DAY;
|
|
86
|
-
const date = new Date(dayBase);
|
|
87
|
-
const dow = date.getUTCDay();
|
|
88
|
-
const isWeekend = dow === 0 || dow === 6;
|
|
89
|
-
const ramp = 0.4 + 0.6 * ((30 - daysAgo) / 30);
|
|
90
|
-
|
|
91
|
-
const dayDisc = Math.round((isWeekend ? 4 : 9) * ramp * (0.7 + rng() * 0.6));
|
|
92
|
-
const dayCalls = Math.round((isWeekend ? 2 : 5) * ramp * (0.6 + rng() * 0.8));
|
|
93
|
-
|
|
94
|
-
// Discovery events
|
|
95
|
-
for (let i = 0; i < dayDisc; i++) {
|
|
96
|
-
const action = weightedPick();
|
|
97
|
-
const ts = dayBase + Math.floor(rng() * 16 + 6) * HOUR + Math.floor(rng() * 60) * 60000;
|
|
98
|
-
|
|
99
|
-
discoveryMap.set(action, (discoveryMap.get(action) || 0) + 1);
|
|
100
|
-
discoveryCount++;
|
|
101
|
-
|
|
102
|
-
await ctx.db.insert("apiLogs", {
|
|
103
|
-
workspaceId: prathamWorkspaceId,
|
|
104
|
-
sessionToken: "",
|
|
105
|
-
provider: "apilayer",
|
|
106
|
-
action: `discover:${action}`,
|
|
107
|
-
status: "success",
|
|
108
|
-
latencyMs: Math.floor(40 + rng() * 180),
|
|
109
|
-
direction: "inbound",
|
|
110
|
-
callerWorkspaceId: pick(callerWorkspaces),
|
|
111
|
-
subagentId: pick(subagentIds),
|
|
112
|
-
createdAt: ts,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Call events
|
|
117
|
-
for (let i = 0; i < dayCalls; i++) {
|
|
118
|
-
const action = weightedPick();
|
|
119
|
-
const ts = dayBase + Math.floor(rng() * 14 + 8) * HOUR + Math.floor(rng() * 60) * 60000;
|
|
120
|
-
const ok = rng() > 0.04;
|
|
121
|
-
|
|
122
|
-
await ctx.db.insert("apiLogs", {
|
|
123
|
-
workspaceId: prathamWorkspaceId,
|
|
124
|
-
sessionToken: "",
|
|
125
|
-
provider: "apilayer",
|
|
126
|
-
action,
|
|
127
|
-
status: ok ? "success" : "error",
|
|
128
|
-
latencyMs: Math.floor(120 + rng() * 800 + (rng() > 0.9 ? rng() * 2000 : 0)),
|
|
129
|
-
direction: "inbound",
|
|
130
|
-
callerWorkspaceId: pick(callerWorkspaces),
|
|
131
|
-
subagentId: pick(subagentIds),
|
|
132
|
-
errorMessage: ok ? undefined : pick(["rate_limit_exceeded", "timeout", "provider_error"]),
|
|
133
|
-
createdAt: ts,
|
|
134
|
-
});
|
|
135
|
-
callCount++;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Update discoveryCount on providerAPIs
|
|
140
|
-
const actionToApi: Record<string, string> = {
|
|
141
|
-
exchange_rates: "ExchangeRate API", market_data: "Marketstack", aviation: "AviationStack",
|
|
142
|
-
pdf_generate: "PDF Layer", screenshot: "Screenshot Layer", verify_email: "Email Verification API",
|
|
143
|
-
verify_number: "Number Verification API", vat_check: "VAT Layer", world_news: "World News API",
|
|
144
|
-
finance_news: "Finance News API", scrape: "Advanced Scraper API", image_crop: "Image Crop API",
|
|
145
|
-
skills: "Skills API", form_submit: "Form API", fixer_convert: "Fixer API", fixer_latest: "Fixer API",
|
|
146
|
-
currencylayer_live: "Currencylayer", currencylayer_convert: "Currencylayer",
|
|
147
|
-
coinlayer_live: "Coinlayer", exchangeratehost_latest: "Exchangerate.host",
|
|
148
|
-
weatherstack_current: "Weatherstack", weatherstack_forecast: "Weatherstack",
|
|
149
|
-
ipstack_lookup: "IPstack", ipapi_lookup: "IPapi",
|
|
150
|
-
positionstack_forward: "Positionstack", positionstack_reverse: "Positionstack",
|
|
151
|
-
languagelayer_detect: "Languagelayer", scrapestack_scrape: "Scrapestack",
|
|
152
|
-
serpstack_search: "Serpstack", mediastack_news: "Mediastack", userstack_detect: "Userstack",
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const apiTotals = new Map<string, number>();
|
|
156
|
-
for (const [action, count] of discoveryMap) {
|
|
157
|
-
const name = actionToApi[action] || action;
|
|
158
|
-
apiTotals.set(name, (apiTotals.get(name) || 0) + count);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const allApis = await ctx.db
|
|
162
|
-
.query("providerAPIs")
|
|
163
|
-
.withIndex("by_providerId", (q) => q.eq("providerId", providerId))
|
|
164
|
-
.collect();
|
|
165
|
-
|
|
166
|
-
let updated = 0;
|
|
167
|
-
for (const api of allApis) {
|
|
168
|
-
const total = apiTotals.get(api.name) || 0;
|
|
169
|
-
if (total > 0) {
|
|
170
|
-
await ctx.db.patch(api._id, {
|
|
171
|
-
discoveryCount: total,
|
|
172
|
-
lastDiscoveredAt: now - Math.floor(rng() * 2 * DAY),
|
|
173
|
-
});
|
|
174
|
-
updated++;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return { calls: callCount, discoveries: discoveryCount, apisUpdated: updated };
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Restore Filestack discovery analytics (discovery-only, not callable).
|
|
184
|
-
*/
|
|
185
|
-
export const restoreFilestackAnalytics = mutation({
|
|
186
|
-
args: {},
|
|
187
|
-
handler: async (ctx) => {
|
|
188
|
-
const filestackWorkspaceId = "n175gprvbbvygmhtg8cfk8jzbd83m0p8" as Id<"workspaces">;
|
|
189
|
-
const filestackApiId = "k57cafdwd4zt66v9y5t23bqzwn8467wj" as Id<"providerAPIs">;
|
|
190
|
-
|
|
191
|
-
const callerWorkspaces = [
|
|
192
|
-
"n17535f45yrzygtdbws0b1seax84et7k",
|
|
193
|
-
"n17chyq6188vpwynyyy2qafhmx84cwem",
|
|
194
|
-
"n174s7zpqdwvhnj7rkwwbxfm5d84a2zx",
|
|
195
|
-
"n17ffc3dnscbsz8h86ez90ssth847krp",
|
|
196
|
-
"n17bnymj0bqffvant7ejb23cw98478dg",
|
|
197
|
-
"n171xw49cwcredajx25gyfgxrh846kym",
|
|
198
|
-
"n1726f2x60v5197bvbp6ex0mqs847z9t",
|
|
199
|
-
"n178829315bfdwpryfs20nmjf9821774",
|
|
200
|
-
"n17b2tdjpkz67frrd05sg07be983z9ca",
|
|
201
|
-
"n17azsjrv9mc4r0bge2s5gpxjd83zbd0",
|
|
202
|
-
];
|
|
203
|
-
|
|
204
|
-
const subagentIds = ["main", "research", "builder", "default"];
|
|
205
|
-
|
|
206
|
-
// Filestack discovery actions (file-related searches that surface Filestack)
|
|
207
|
-
const discoveryQueries = [
|
|
208
|
-
"discover:file_upload",
|
|
209
|
-
"discover:file_transform",
|
|
210
|
-
"discover:file_storage",
|
|
211
|
-
"discover:image_processing",
|
|
212
|
-
"discover:file_conversion",
|
|
213
|
-
"discover:cdn_delivery",
|
|
214
|
-
];
|
|
215
|
-
const weights = [10, 6, 5, 8, 4, 3];
|
|
216
|
-
|
|
217
|
-
let s = 73019;
|
|
218
|
-
const rng = () => { s = (s * 16807) % 2147483647; return (s - 1) / 2147483646; };
|
|
219
|
-
const pick = <T>(a: T[]): T => a[Math.floor(rng() * a.length)];
|
|
220
|
-
const weightedQuery = (): string => {
|
|
221
|
-
const total = weights.reduce((a, b) => a + b, 0);
|
|
222
|
-
let r = rng() * total;
|
|
223
|
-
for (let i = 0; i < weights.length; i++) {
|
|
224
|
-
r -= weights[i];
|
|
225
|
-
if (r <= 0) return discoveryQueries[i];
|
|
226
|
-
}
|
|
227
|
-
return discoveryQueries[0];
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const now = Date.now();
|
|
231
|
-
const DAY = 86400000;
|
|
232
|
-
const HOUR = 3600000;
|
|
233
|
-
|
|
234
|
-
let count = 0;
|
|
235
|
-
|
|
236
|
-
for (let daysAgo = 30; daysAgo >= 0; daysAgo--) {
|
|
237
|
-
const dayBase = now - daysAgo * DAY;
|
|
238
|
-
const dow = new Date(dayBase).getUTCDay();
|
|
239
|
-
const isWeekend = dow === 0 || dow === 6;
|
|
240
|
-
const ramp = 0.5 + 0.5 * ((30 - daysAgo) / 30);
|
|
241
|
-
|
|
242
|
-
// Filestack is niche -- fewer hits than APILayer
|
|
243
|
-
const dayDisc = Math.round((isWeekend ? 1.5 : 3.5) * ramp * (0.6 + rng() * 0.8));
|
|
244
|
-
|
|
245
|
-
for (let i = 0; i < dayDisc; i++) {
|
|
246
|
-
const ts = dayBase + Math.floor(rng() * 15 + 7) * HOUR + Math.floor(rng() * 60) * 60000;
|
|
247
|
-
|
|
248
|
-
await ctx.db.insert("apiLogs", {
|
|
249
|
-
workspaceId: filestackWorkspaceId,
|
|
250
|
-
sessionToken: "",
|
|
251
|
-
provider: "filestack",
|
|
252
|
-
action: weightedQuery(),
|
|
253
|
-
status: "success",
|
|
254
|
-
latencyMs: Math.floor(30 + rng() * 150),
|
|
255
|
-
direction: "inbound",
|
|
256
|
-
callerWorkspaceId: pick(callerWorkspaces),
|
|
257
|
-
subagentId: pick(subagentIds),
|
|
258
|
-
createdAt: ts,
|
|
259
|
-
});
|
|
260
|
-
count++;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Update discoveryCount on the single Filestack API to match
|
|
265
|
-
await ctx.db.patch(filestackApiId, {
|
|
266
|
-
discoveryCount: count,
|
|
267
|
-
lastDiscoveredAt: now - Math.floor(rng() * DAY),
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
return { discoveries: count };
|
|
271
|
-
},
|
|
272
|
-
});
|