@nordsym/apiclaw 1.8.6 → 1.8.7
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 +4 -4
- package/apiclaw-README.md +12 -12
- package/convex/_generated/api.d.ts +4 -0
- package/convex/apiKeys.ts +220 -0
- package/convex/directCall.ts +49 -14
- package/convex/email.ts +5 -5
- package/convex/http.ts +598 -40
- package/convex/logs.ts +26 -22
- package/convex/migrateProviderWorkspaces.ts +154 -35
- package/convex/providers.ts +313 -203
- package/convex/schema.ts +50 -1
- package/convex/searchLogs.ts +2 -6
- package/convex/seedPratham.ts +1 -1
- package/convex/spendAlerts.ts +2 -2
- package/convex/stripeActions.ts +1 -1
- package/convex/updateAPIStatus.ts +2 -3
- package/convex/workspaceSettings.ts +136 -0
- package/dist/cli/commands/demo.js +2 -2
- package/dist/cli/commands/doctor.js +1 -1
- package/dist/cli/commands/login.js +1 -1
- package/dist/cli/commands/setup.js +2 -2
- package/dist/discovery.js +1 -1
- package/dist/execute.js +3 -3
- package/dist/index.js +13 -13
- package/dist/ui/errors.js +16 -16
- package/dist/ui/prompts.js +1 -1
- package/email-templates/filestack-provider-outreach.html +1 -1
- package/email-templates/partnership-template.html +1 -1
- package/email-templates/pratham-partnership-draft.html +2 -2
- package/package.json +2 -2
- package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
- package/reports/session-report-2026-04-05.html +433 -0
- package/src/cli/commands/demo.ts +2 -2
- package/src/cli/commands/doctor.ts +1 -1
- package/src/cli/commands/login.ts +1 -1
- package/src/cli/commands/setup.ts +2 -2
- package/src/discovery.ts +1 -1
- package/src/execute.ts +3 -3
- package/src/index.ts +14 -14
- package/src/ui/errors.ts +16 -16
- package/src/ui/prompts.ts +1 -1
- package/convex/adminActivate.d.ts +0 -3
- package/convex/adminActivate.js +0 -47
- package/convex/adminStats.d.ts +0 -9
- package/convex/adminStats.js +0 -280
- package/convex/agents.d.ts +0 -84
- package/convex/agents.js +0 -809
- package/convex/analytics.d.ts +0 -5
- package/convex/analytics.js +0 -167
- package/convex/backfillAnalytics.d.ts +0 -2
- package/convex/backfillAnalytics.js +0 -20
- package/convex/backfillSearchLogs.d.ts +0 -2
- package/convex/backfillSearchLogs.js +0 -29
- package/convex/billing.d.ts +0 -88
- package/convex/billing.js +0 -655
- package/convex/capabilities.d.ts +0 -9
- package/convex/capabilities.js +0 -145
- package/convex/chains.d.ts +0 -68
- package/convex/chains.js +0 -1105
- package/convex/credits.d.ts +0 -25
- package/convex/credits.js +0 -186
- package/convex/crons.d.ts +0 -3
- package/convex/crons.js +0 -17
- package/convex/debugFilestackLogs.d.ts +0 -2
- package/convex/debugFilestackLogs.js +0 -17
- package/convex/debugGetToken.d.ts +0 -2
- package/convex/debugGetToken.js +0 -18
- package/convex/directCall.d.ts +0 -72
- package/convex/directCall.js +0 -627
- package/convex/earnProgress.d.ts +0 -58
- package/convex/earnProgress.js +0 -649
- package/convex/email.d.ts +0 -14
- package/convex/email.js +0 -300
- package/convex/feedback.d.ts +0 -7
- package/convex/feedback.js +0 -227
- package/convex/http.d.ts +0 -3
- package/convex/http.js +0 -1408
- package/convex/inbound.d.ts +0 -2
- package/convex/inbound.js +0 -32
- package/convex/logs.d.ts +0 -48
- package/convex/logs.js +0 -621
- package/convex/migrateFilestack.d.ts +0 -2
- package/convex/migrateFilestack.js +0 -74
- package/convex/migratePartnersProd.d.ts +0 -8
- package/convex/migratePartnersProd.js +0 -165
- package/convex/migratePratham.d.ts +0 -2
- package/convex/migratePratham.js +0 -121
- package/convex/migrateProviderWorkspaces.d.ts +0 -2
- package/convex/migrateProviderWorkspaces.js +0 -55
- package/convex/mou.d.ts +0 -6
- package/convex/mou.js +0 -82
- package/convex/providerKeys.d.ts +0 -31
- package/convex/providerKeys.js +0 -257
- package/convex/providers.d.ts +0 -35
- package/convex/providers.js +0 -922
- package/convex/purchases.d.ts +0 -7
- package/convex/purchases.js +0 -157
- package/convex/ratelimit.d.ts +0 -4
- package/convex/ratelimit.js +0 -91
- package/convex/searchLogs.d.ts +0 -13
- package/convex/searchLogs.js +0 -246
- package/convex/seedAPILayerAPIs.d.ts +0 -7
- package/convex/seedAPILayerAPIs.js +0 -177
- package/convex/seedDirectCallConfigs.d.ts +0 -2
- package/convex/seedDirectCallConfigs.js +0 -324
- package/convex/seedPratham.d.ts +0 -6
- package/convex/seedPratham.js +0 -150
- package/convex/spendAlerts.d.ts +0 -36
- package/convex/spendAlerts.js +0 -380
- package/convex/stripeActions.d.ts +0 -19
- package/convex/stripeActions.js +0 -411
- package/convex/teams.d.ts +0 -21
- package/convex/teams.js +0 -215
- package/convex/telemetry.d.ts +0 -4
- package/convex/telemetry.js +0 -74
- package/convex/updateAPIStatus.d.ts +0 -6
- package/convex/updateAPIStatus.js +0 -40
- package/convex/usage.d.ts +0 -27
- package/convex/usage.js +0 -229
- package/convex/waitlist.d.ts +0 -4
- package/convex/waitlist.js +0 -49
- package/convex/webhooks.d.ts +0 -12
- package/convex/webhooks.js +0 -410
- package/convex/workspaces.d.ts +0 -33
- package/convex/workspaces.js +0 -991
package/convex/logs.ts
CHANGED
|
@@ -91,19 +91,16 @@ export const logProviderCall = mutation({
|
|
|
91
91
|
errorMessage: v.optional(v.string()),
|
|
92
92
|
},
|
|
93
93
|
handler: async (ctx, args) => {
|
|
94
|
-
//
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
workspace = await ctx.db
|
|
104
|
-
.query("workspaces")
|
|
105
|
-
.withIndex("by_email", (q) => q.eq("email", providerEmail))
|
|
106
|
-
.first();
|
|
94
|
+
// Resolve provider → workspace dynamically (no hardcoded email maps)
|
|
95
|
+
const providerNameLower = args.provider.toLowerCase();
|
|
96
|
+
const allProviders = await ctx.db.query("providers").collect();
|
|
97
|
+
const providerRecord = allProviders.find(
|
|
98
|
+
(p) => p.name.toLowerCase() === providerNameLower
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
let workspace: any = null;
|
|
102
|
+
if (providerRecord && (providerRecord as any).workspaceId) {
|
|
103
|
+
workspace = await ctx.db.get((providerRecord as any).workspaceId);
|
|
107
104
|
}
|
|
108
105
|
|
|
109
106
|
// Always log to global provider analytics (even without workspace)
|
|
@@ -148,8 +145,9 @@ export const getProviderAnalytics = query({
|
|
|
148
145
|
args: {
|
|
149
146
|
token: v.string(),
|
|
150
147
|
hoursBack: v.optional(v.number()),
|
|
148
|
+
direction: v.optional(v.string()), // "outbound" = my usage, "inbound" = traffic to my APIs, omit = all
|
|
151
149
|
},
|
|
152
|
-
handler: async (ctx, { token, hoursBack = 168 }) => {
|
|
150
|
+
handler: async (ctx, { token, hoursBack = 168, direction }) => {
|
|
153
151
|
const session = await ctx.db
|
|
154
152
|
.query("agentSessions")
|
|
155
153
|
.withIndex("by_sessionToken", (q) => q.eq("sessionToken", token))
|
|
@@ -164,11 +162,18 @@ export const getProviderAnalytics = query({
|
|
|
164
162
|
.withIndex("by_workspaceId", (q) => q.eq("workspaceId", session.workspaceId))
|
|
165
163
|
.collect();
|
|
166
164
|
|
|
167
|
-
|
|
165
|
+
let periodLogs = allLogs.filter((l) => l.createdAt >= since);
|
|
166
|
+
|
|
167
|
+
// Filter by direction if specified
|
|
168
|
+
if (direction === "outbound") {
|
|
169
|
+
periodLogs = periodLogs.filter((l) => l.direction !== "inbound");
|
|
170
|
+
} else if (direction === "inbound") {
|
|
171
|
+
periodLogs = periodLogs.filter((l) => l.direction === "inbound");
|
|
172
|
+
}
|
|
173
|
+
|
|
168
174
|
const inboundLogs = periodLogs.filter((l) => l.direction === "inbound");
|
|
169
|
-
const outboundLogs = periodLogs.filter((l) => l.direction !== "inbound");
|
|
170
175
|
|
|
171
|
-
// Daily buckets
|
|
176
|
+
// Daily buckets
|
|
172
177
|
const byDay: Record<string, { calls: number; searches: number }> = {};
|
|
173
178
|
periodLogs.forEach((l) => {
|
|
174
179
|
const day = new Date(l.createdAt).toISOString().split("T")[0];
|
|
@@ -180,11 +185,10 @@ export const getProviderAnalytics = query({
|
|
|
180
185
|
}
|
|
181
186
|
});
|
|
182
187
|
|
|
183
|
-
// By action
|
|
188
|
+
// By action
|
|
184
189
|
const byAction: Record<string, { calls: number; success: number; type: string }> = {};
|
|
185
190
|
periodLogs.forEach((l) => {
|
|
186
191
|
const isDiscovery = l.action.startsWith("discovery:");
|
|
187
|
-
const key = isDiscovery ? l.action : `${l.provider}:${l.action}`;
|
|
188
192
|
const displayName = isDiscovery ? l.action.replace("discovery:", "Search: ") : l.action;
|
|
189
193
|
if (!byAction[displayName]) byAction[displayName] = { calls: 0, success: 0, type: isDiscovery ? "discovery" : "call" };
|
|
190
194
|
byAction[displayName].calls++;
|
|
@@ -194,7 +198,6 @@ export const getProviderAnalytics = query({
|
|
|
194
198
|
// Unique callers (for inbound)
|
|
195
199
|
const uniqueCallers = new Set(inboundLogs.map((l) => l.callerWorkspaceId).filter(Boolean)).size;
|
|
196
200
|
|
|
197
|
-
// Counts
|
|
198
201
|
const callLogs = periodLogs.filter((l) => !l.action.startsWith("discovery:"));
|
|
199
202
|
const discoveryLogs = periodLogs.filter((l) => l.action.startsWith("discovery:"));
|
|
200
203
|
|
|
@@ -202,7 +205,7 @@ export const getProviderAnalytics = query({
|
|
|
202
205
|
totalCalls: callLogs.length,
|
|
203
206
|
totalDiscoveries: discoveryLogs.length,
|
|
204
207
|
inboundCalls: inboundLogs.filter((l) => !l.action.startsWith("discovery:")).length,
|
|
205
|
-
|
|
208
|
+
uniqueCallers,
|
|
206
209
|
byDay: Object.entries(byDay)
|
|
207
210
|
.map(([date, data]) => ({ date, calls: data.calls, searches: data.searches }))
|
|
208
211
|
.sort((a, b) => a.date.localeCompare(b.date)),
|
|
@@ -668,7 +671,8 @@ export const createProxyLog = mutation({
|
|
|
668
671
|
subagentId: subagentId || "unknown",
|
|
669
672
|
sessionToken: sessionToken || "proxy",
|
|
670
673
|
status: "success",
|
|
671
|
-
latencyMs: 0,
|
|
674
|
+
latencyMs: 0,
|
|
675
|
+
direction: "outbound",
|
|
672
676
|
createdAt: now,
|
|
673
677
|
});
|
|
674
678
|
|
|
@@ -1,56 +1,175 @@
|
|
|
1
1
|
import { mutation } from "./_generated/server";
|
|
2
|
-
import { v } from "convex/values";
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Workspace Unification Migration (idempotent)
|
|
5
|
+
*
|
|
6
|
+
* For every providers record, find the matching workspace by email
|
|
7
|
+
* and backfill workspaceId on the provider + all related tables.
|
|
8
|
+
*
|
|
9
|
+
* Does NOT create new workspaces. If no workspace is found for a
|
|
10
|
+
* provider email, it is logged in the report for manual handling.
|
|
11
|
+
*
|
|
12
|
+
* Safe to run multiple times — skips already-linked records.
|
|
13
|
+
*/
|
|
5
14
|
export const run = mutation({
|
|
6
15
|
args: {},
|
|
7
16
|
handler: async (ctx) => {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
const report: {
|
|
18
|
+
providers: Array<{
|
|
19
|
+
email: string;
|
|
20
|
+
name: string;
|
|
21
|
+
providerId: string;
|
|
22
|
+
workspaceId: string | null;
|
|
23
|
+
status: "linked" | "already_linked" | "workspace_not_found";
|
|
24
|
+
backfilled: {
|
|
25
|
+
providerAPIs: number;
|
|
26
|
+
apis: number;
|
|
27
|
+
apiCalls: number;
|
|
28
|
+
payouts: number;
|
|
29
|
+
providerDirectCall: number;
|
|
30
|
+
usageLog: number;
|
|
31
|
+
};
|
|
32
|
+
}>;
|
|
33
|
+
warnings: string[];
|
|
34
|
+
} = { providers: [], warnings: [] };
|
|
35
|
+
|
|
36
|
+
// 1. Get ALL provider records (no hardcoded list)
|
|
37
|
+
const allProviders = await ctx.db.query("providers").collect();
|
|
38
|
+
|
|
39
|
+
for (const provider of allProviders) {
|
|
40
|
+
const entry: (typeof report.providers)[number] = {
|
|
41
|
+
email: provider.email,
|
|
42
|
+
name: provider.name,
|
|
43
|
+
providerId: provider._id as string,
|
|
44
|
+
workspaceId: null,
|
|
45
|
+
status: "workspace_not_found",
|
|
46
|
+
backfilled: {
|
|
47
|
+
providerAPIs: 0,
|
|
48
|
+
apis: 0,
|
|
49
|
+
apiCalls: 0,
|
|
50
|
+
payouts: 0,
|
|
51
|
+
providerDirectCall: 0,
|
|
52
|
+
usageLog: 0,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
30
55
|
|
|
31
|
-
//
|
|
56
|
+
// Already linked?
|
|
57
|
+
if (provider.workspaceId) {
|
|
58
|
+
entry.workspaceId = provider.workspaceId as string;
|
|
59
|
+
entry.status = "already_linked";
|
|
60
|
+
// Still backfill child tables in case they were missed
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 2. Find workspace by email (case-insensitive)
|
|
64
|
+
const emailLower = provider.email.toLowerCase();
|
|
32
65
|
const workspace = await ctx.db
|
|
33
66
|
.query("workspaces")
|
|
34
|
-
.withIndex("by_email", (q) => q.eq("email",
|
|
67
|
+
.withIndex("by_email", (q) => q.eq("email", emailLower))
|
|
35
68
|
.first();
|
|
36
|
-
if (!workspace) { results[pair.label] = -2; continue; }
|
|
37
69
|
|
|
38
|
-
|
|
39
|
-
|
|
70
|
+
if (!workspace) {
|
|
71
|
+
// Try exact match as fallback (in case stored with different casing)
|
|
72
|
+
const workspaceExact = await ctx.db
|
|
73
|
+
.query("workspaces")
|
|
74
|
+
.withIndex("by_email", (q) => q.eq("email", provider.email))
|
|
75
|
+
.first();
|
|
76
|
+
|
|
77
|
+
if (!workspaceExact) {
|
|
78
|
+
report.warnings.push(
|
|
79
|
+
`No workspace found for provider "${provider.name}" (${provider.email})`
|
|
80
|
+
);
|
|
81
|
+
report.providers.push(entry);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
// Use exact match
|
|
85
|
+
entry.workspaceId = workspaceExact._id as string;
|
|
86
|
+
} else {
|
|
87
|
+
entry.workspaceId = workspace._id as string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const wsId = entry.workspaceId! as any;
|
|
91
|
+
|
|
92
|
+
// 3. Link provider → workspace (if not already)
|
|
93
|
+
if (!provider.workspaceId) {
|
|
94
|
+
await ctx.db.patch(provider._id, { workspaceId: wsId });
|
|
95
|
+
entry.status = "linked";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 4. Backfill providerAPIs (skip if workspaceId already set)
|
|
99
|
+
const providerAPIs = await ctx.db
|
|
40
100
|
.query("providerAPIs")
|
|
41
101
|
.withIndex("by_providerId", (q) => q.eq("providerId", provider._id))
|
|
42
102
|
.collect();
|
|
103
|
+
for (const api of providerAPIs) {
|
|
104
|
+
if (!api.workspaceId) {
|
|
105
|
+
await ctx.db.patch(api._id, { workspaceId: wsId });
|
|
106
|
+
entry.backfilled.providerAPIs++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
43
109
|
|
|
44
|
-
|
|
110
|
+
// 5. Backfill apis
|
|
111
|
+
const apis = await ctx.db
|
|
112
|
+
.query("apis")
|
|
113
|
+
.withIndex("by_providerId", (q) => q.eq("providerId", provider._id))
|
|
114
|
+
.collect();
|
|
45
115
|
for (const api of apis) {
|
|
46
|
-
if (!api.workspaceId) {
|
|
47
|
-
await ctx.db.patch(api._id, { workspaceId:
|
|
48
|
-
|
|
116
|
+
if (!(api as any).workspaceId) {
|
|
117
|
+
await ctx.db.patch(api._id, { workspaceId: wsId } as any);
|
|
118
|
+
entry.backfilled.apis++;
|
|
49
119
|
}
|
|
50
120
|
}
|
|
51
|
-
|
|
121
|
+
|
|
122
|
+
// 6. Backfill apiCalls
|
|
123
|
+
const apiCalls = await ctx.db
|
|
124
|
+
.query("apiCalls")
|
|
125
|
+
.withIndex("by_providerId", (q) => q.eq("providerId", provider._id))
|
|
126
|
+
.collect();
|
|
127
|
+
for (const call of apiCalls) {
|
|
128
|
+
if (!(call as any).workspaceId) {
|
|
129
|
+
await ctx.db.patch(call._id, { workspaceId: wsId } as any);
|
|
130
|
+
entry.backfilled.apiCalls++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 7. Backfill payouts
|
|
135
|
+
const payouts = await ctx.db
|
|
136
|
+
.query("payouts")
|
|
137
|
+
.withIndex("by_providerId", (q) => q.eq("providerId", provider._id))
|
|
138
|
+
.collect();
|
|
139
|
+
for (const payout of payouts) {
|
|
140
|
+
if (!(payout as any).workspaceId) {
|
|
141
|
+
await ctx.db.patch(payout._id, { workspaceId: wsId } as any);
|
|
142
|
+
entry.backfilled.payouts++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 8. Backfill providerDirectCall
|
|
147
|
+
const directCalls = await ctx.db
|
|
148
|
+
.query("providerDirectCall")
|
|
149
|
+
.withIndex("by_providerId", (q) => q.eq("providerId", provider._id))
|
|
150
|
+
.collect();
|
|
151
|
+
for (const dc of directCalls) {
|
|
152
|
+
if (!(dc as any).workspaceId) {
|
|
153
|
+
await ctx.db.patch(dc._id, { workspaceId: wsId } as any);
|
|
154
|
+
entry.backfilled.providerDirectCall++;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 9. Backfill usageLog
|
|
159
|
+
const usageLogs = await ctx.db
|
|
160
|
+
.query("usageLog")
|
|
161
|
+
.withIndex("by_providerId", (q) => q.eq("providerId", provider._id))
|
|
162
|
+
.collect();
|
|
163
|
+
for (const log of usageLogs) {
|
|
164
|
+
if (!(log as any).workspaceId) {
|
|
165
|
+
await ctx.db.patch(log._id, { workspaceId: wsId } as any);
|
|
166
|
+
entry.backfilled.usageLog++;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
report.providers.push(entry);
|
|
52
171
|
}
|
|
53
172
|
|
|
54
|
-
return
|
|
173
|
+
return report;
|
|
55
174
|
},
|
|
56
175
|
});
|