@nordsym/apiclaw 1.5.13 → 1.5.14
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/dist/bin.js +1 -1
- package/dist/cli/index.js +7 -0
- package/dist/convex/adminActivate.js +46 -0
- package/dist/convex/adminStats.js +41 -0
- package/dist/convex/agents.js +498 -0
- package/dist/convex/analytics.js +165 -0
- package/dist/convex/billing.js +654 -0
- package/dist/convex/capabilities.js +144 -0
- package/dist/convex/chains.js +1041 -0
- package/dist/convex/credits.js +185 -0
- package/dist/convex/crons.js +16 -0
- package/dist/convex/directCall.js +626 -0
- package/dist/convex/earnProgress.js +648 -0
- package/dist/convex/email.js +299 -0
- package/dist/convex/feedback.js +226 -0
- package/dist/convex/http.js +909 -0
- package/dist/convex/logs.js +486 -0
- package/dist/convex/mou.js +81 -0
- package/dist/convex/providerKeys.js +256 -0
- package/dist/convex/providers.js +755 -0
- package/dist/convex/purchases.js +156 -0
- package/dist/convex/ratelimit.js +90 -0
- package/dist/convex/schema.js +709 -0
- package/dist/convex/searchLogs.js +128 -0
- package/dist/convex/spendAlerts.js +379 -0
- package/dist/convex/stripeActions.js +410 -0
- package/dist/convex/teams.js +214 -0
- package/dist/convex/telemetry.js +73 -0
- package/dist/convex/usage.js +228 -0
- package/dist/convex/waitlist.js +48 -0
- package/dist/convex/webhooks.js +409 -0
- package/dist/convex/workspaces.js +879 -0
- package/dist/src/analytics.js +129 -0
- package/dist/src/bin.js +17 -0
- package/dist/src/capability-router.js +240 -0
- package/dist/src/chainExecutor.js +451 -0
- package/dist/src/chainResolver.js +518 -0
- package/dist/src/cli/commands/doctor.js +324 -0
- package/dist/src/cli/commands/mcp-install.js +255 -0
- package/dist/src/cli/commands/restore.js +259 -0
- package/dist/src/cli/commands/setup.js +205 -0
- package/dist/src/cli/commands/uninstall.js +188 -0
- package/dist/src/cli/index.js +111 -0
- package/dist/src/cli.js +302 -0
- package/dist/src/confirmation.js +240 -0
- package/dist/src/credentials.js +357 -0
- package/dist/src/credits.js +260 -0
- package/dist/src/crypto.js +66 -0
- package/dist/src/discovery.js +504 -0
- package/dist/src/enterprise/env.js +123 -0
- package/dist/src/enterprise/script-generator.js +460 -0
- package/dist/src/execute-dynamic.js +473 -0
- package/dist/src/execute.js +1727 -0
- package/dist/src/index.js +2062 -0
- package/dist/src/metered.js +80 -0
- package/dist/src/open-apis.js +276 -0
- package/dist/src/proxy.js +28 -0
- package/dist/src/session.js +86 -0
- package/dist/src/stripe.js +407 -0
- package/dist/src/telemetry.js +49 -0
- package/dist/src/types.js +2 -0
- package/dist/src/utils/backup.js +181 -0
- package/dist/src/utils/config.js +220 -0
- package/dist/src/utils/os.js +105 -0
- package/dist/src/utils/paths.js +159 -0
- package/package.json +1 -1
- package/src/bin.ts +1 -1
- package/src/cli/index.ts +8 -0
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
export default defineSchema({
|
|
4
|
+
// Credits per agent
|
|
5
|
+
agentCredits: defineTable({
|
|
6
|
+
agentId: v.string(),
|
|
7
|
+
balanceUsd: v.number(),
|
|
8
|
+
currency: v.string(),
|
|
9
|
+
createdAt: v.number(),
|
|
10
|
+
updatedAt: v.number(),
|
|
11
|
+
}).index("by_agentId", ["agentId"]),
|
|
12
|
+
// Purchases (API access bought by agents)
|
|
13
|
+
purchases: defineTable({
|
|
14
|
+
agentId: v.string(),
|
|
15
|
+
providerId: v.string(),
|
|
16
|
+
amountUsd: v.number(),
|
|
17
|
+
creditsGranted: v.number(),
|
|
18
|
+
status: v.string(), // active, exhausted, refunded
|
|
19
|
+
credentials: v.optional(v.any()),
|
|
20
|
+
createdAt: v.number(),
|
|
21
|
+
})
|
|
22
|
+
.index("by_agentId", ["agentId"])
|
|
23
|
+
.index("by_providerId", ["providerId"])
|
|
24
|
+
.index("by_agentId_providerId", ["agentId", "providerId"]),
|
|
25
|
+
// Usage tracking per purchase
|
|
26
|
+
usage: defineTable({
|
|
27
|
+
purchaseId: v.id("purchases"),
|
|
28
|
+
providerId: v.string(),
|
|
29
|
+
unitsUsed: v.number(),
|
|
30
|
+
unitsRemaining: v.number(),
|
|
31
|
+
costIncurredUsd: v.number(),
|
|
32
|
+
lastUsedAt: v.number(),
|
|
33
|
+
})
|
|
34
|
+
.index("by_purchaseId", ["purchaseId"])
|
|
35
|
+
.index("by_providerId", ["providerId"]),
|
|
36
|
+
// ============================================
|
|
37
|
+
// WORKSPACE TABLES (MCP Agent Authentication)
|
|
38
|
+
// ============================================
|
|
39
|
+
// Workspaces (agent owner accounts)
|
|
40
|
+
workspaces: defineTable({
|
|
41
|
+
email: v.string(),
|
|
42
|
+
passwordHash: v.optional(v.string()),
|
|
43
|
+
status: v.string(), // "pending" | "active" | "suspended"
|
|
44
|
+
tier: v.string(), // "free" | "pro" | "enterprise" | "backer"
|
|
45
|
+
usageCount: v.number(), // total API calls made (lifetime)
|
|
46
|
+
usageLimit: v.number(), // max API calls for tier
|
|
47
|
+
// Weekly usage (resets every Monday 00:00 UTC)
|
|
48
|
+
weeklyUsageCount: v.optional(v.number()), // calls this week
|
|
49
|
+
weeklyUsageLimit: v.optional(v.number()), // 50 for free, unlimited for backer
|
|
50
|
+
lastWeeklyResetAt: v.optional(v.number()), // timestamp of last reset
|
|
51
|
+
// Hourly rate limit
|
|
52
|
+
hourlyUsageCount: v.optional(v.number()), // calls this hour
|
|
53
|
+
lastHourlyResetAt: v.optional(v.number()), // timestamp of last hourly reset
|
|
54
|
+
// Backer status (Founding Backer = unlimited until end of 2026)
|
|
55
|
+
backerUntil: v.optional(v.number()), // timestamp when backer status expires
|
|
56
|
+
// Main agent identification
|
|
57
|
+
mainAgentId: v.optional(v.string()), // UUID, auto-generated on first call
|
|
58
|
+
mainAgentName: v.optional(v.string()), // Auto-generated name (e.g., "Crimson Phoenix")
|
|
59
|
+
// AI Backend tracking
|
|
60
|
+
aiBackend: v.optional(v.string()), // "claude-3-opus", "gpt-4", etc.
|
|
61
|
+
aiBackendLastSeen: v.optional(v.number()), // timestamp of last AI backend header
|
|
62
|
+
// Stripe billing fields
|
|
63
|
+
stripeCustomerId: v.optional(v.string()),
|
|
64
|
+
stripeSubscriptionId: v.optional(v.string()),
|
|
65
|
+
billingPlan: v.optional(v.string()), // "free" | "usage_based" | "starter" | "pro" | "scale"
|
|
66
|
+
creditBalance: v.optional(v.number()), // prepaid credits in cents
|
|
67
|
+
lastBillingDate: v.optional(v.number()),
|
|
68
|
+
// Payment method fields
|
|
69
|
+
hasPaymentMethod: v.optional(v.boolean()),
|
|
70
|
+
paymentMethodType: v.optional(v.string()),
|
|
71
|
+
cardBrand: v.optional(v.string()),
|
|
72
|
+
cardLast4: v.optional(v.string()),
|
|
73
|
+
// Referral fields
|
|
74
|
+
referralCode: v.optional(v.string()), // CLAW-XXXXXX format
|
|
75
|
+
referredBy: v.optional(v.id("workspaces")), // who referred this user
|
|
76
|
+
// Budget & Spend Alerts (PRD 2.6)
|
|
77
|
+
budgetCap: v.optional(v.number()), // Monthly budget cap in USD cents (null = unlimited)
|
|
78
|
+
budgetAlertSentAt: v.optional(v.number()), // When 80% alert was last sent (resets monthly)
|
|
79
|
+
pauseOnBudgetExceeded: v.optional(v.boolean()), // If true, block execution when budget exceeded
|
|
80
|
+
monthlySpendCents: v.optional(v.number()), // Current month's spend in cents
|
|
81
|
+
lastSpendResetAt: v.optional(v.number()), // When monthly spend was last reset
|
|
82
|
+
// Activity tracking
|
|
83
|
+
lastActiveAt: v.optional(v.number()), // Last API call timestamp (main agent)
|
|
84
|
+
createdAt: v.number(),
|
|
85
|
+
updatedAt: v.number(),
|
|
86
|
+
})
|
|
87
|
+
.index("by_email", ["email"])
|
|
88
|
+
.index("by_stripeCustomerId", ["stripeCustomerId"])
|
|
89
|
+
.index("by_stripeSubscriptionId", ["stripeSubscriptionId"])
|
|
90
|
+
.index("by_status", ["status"])
|
|
91
|
+
.index("by_referralCode", ["referralCode"])
|
|
92
|
+
.index("by_mainAgentId", ["mainAgentId"]),
|
|
93
|
+
// Invoices (Stripe invoice records)
|
|
94
|
+
invoices: defineTable({
|
|
95
|
+
workspaceId: v.id("workspaces"),
|
|
96
|
+
stripeInvoiceId: v.string(),
|
|
97
|
+
amount: v.number(), // in cents
|
|
98
|
+
status: v.string(), // "paid" | "pending" | "failed" | "void"
|
|
99
|
+
periodStart: v.number(),
|
|
100
|
+
periodEnd: v.number(),
|
|
101
|
+
callCount: v.number(),
|
|
102
|
+
pdfUrl: v.optional(v.string()),
|
|
103
|
+
createdAt: v.number(),
|
|
104
|
+
})
|
|
105
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
106
|
+
.index("by_stripeInvoiceId", ["stripeInvoiceId"])
|
|
107
|
+
.index("by_workspaceId_createdAt", ["workspaceId", "createdAt"]),
|
|
108
|
+
// Usage records (daily aggregation for billing)
|
|
109
|
+
usageRecords: defineTable({
|
|
110
|
+
workspaceId: v.id("workspaces"),
|
|
111
|
+
date: v.string(), // "2026-02-28" format
|
|
112
|
+
callCount: v.number(),
|
|
113
|
+
reportedToStripe: v.boolean(),
|
|
114
|
+
stripeUsageRecordId: v.optional(v.string()),
|
|
115
|
+
createdAt: v.number(),
|
|
116
|
+
updatedAt: v.number(),
|
|
117
|
+
})
|
|
118
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
119
|
+
.index("by_date", ["date"])
|
|
120
|
+
.index("by_workspaceId_date", ["workspaceId", "date"])
|
|
121
|
+
.index("by_reportedToStripe", ["reportedToStripe"]),
|
|
122
|
+
// Agent sessions (for MCP server authentication)
|
|
123
|
+
agentSessions: defineTable({
|
|
124
|
+
workspaceId: v.id("workspaces"),
|
|
125
|
+
sessionToken: v.string(),
|
|
126
|
+
fingerprint: v.optional(v.string()), // machine fingerprint
|
|
127
|
+
customName: v.optional(v.string()), // user-defined name
|
|
128
|
+
lastUsedAt: v.number(),
|
|
129
|
+
createdAt: v.number(),
|
|
130
|
+
})
|
|
131
|
+
.index("by_sessionToken", ["sessionToken"])
|
|
132
|
+
.index("by_workspaceId", ["workspaceId"]),
|
|
133
|
+
// Subagent tracking (tasks spawned by main agent)
|
|
134
|
+
subagents: defineTable({
|
|
135
|
+
workspaceId: v.id("workspaces"),
|
|
136
|
+
subagentId: v.string(), // from X-APIClaw-Subagent header
|
|
137
|
+
name: v.optional(v.string()), // optional display name
|
|
138
|
+
description: v.optional(v.string()), // user-provided description
|
|
139
|
+
aiBackend: v.optional(v.string()), // "claude-3-opus", "gpt-4", etc.
|
|
140
|
+
isRegistered: v.optional(v.boolean()), // true if pre-registered (not implicit)
|
|
141
|
+
callCount: v.number(),
|
|
142
|
+
firstSeenAt: v.number(),
|
|
143
|
+
lastActiveAt: v.number(),
|
|
144
|
+
})
|
|
145
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
146
|
+
.index("by_workspaceId_subagentId", ["workspaceId", "subagentId"])
|
|
147
|
+
.index("by_lastActiveAt", ["lastActiveAt"]),
|
|
148
|
+
// Search logs (analytics for workspace searches)
|
|
149
|
+
searchLogs: defineTable({
|
|
150
|
+
workspaceId: v.id("workspaces"),
|
|
151
|
+
subagentId: v.optional(v.string()),
|
|
152
|
+
query: v.string(),
|
|
153
|
+
resultCount: v.number(),
|
|
154
|
+
hasResults: v.boolean(),
|
|
155
|
+
matchedProviders: v.optional(v.array(v.string())),
|
|
156
|
+
responseTimeMs: v.number(),
|
|
157
|
+
timestamp: v.number(),
|
|
158
|
+
})
|
|
159
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
160
|
+
.index("by_timestamp", ["timestamp"])
|
|
161
|
+
.index("by_hasResults", ["hasResults"])
|
|
162
|
+
.index("by_workspaceId_timestamp", ["workspaceId", "timestamp"]),
|
|
163
|
+
// Workspace team members (invite-based access)
|
|
164
|
+
workspaceMembers: defineTable({
|
|
165
|
+
workspaceId: v.id("workspaces"),
|
|
166
|
+
email: v.string(),
|
|
167
|
+
role: v.union(v.literal("owner"), v.literal("admin"), v.literal("member")),
|
|
168
|
+
invitedBy: v.optional(v.string()), // email of inviter
|
|
169
|
+
inviteToken: v.optional(v.string()),
|
|
170
|
+
status: v.union(v.literal("pending"), v.literal("active"), v.literal("revoked")),
|
|
171
|
+
createdAt: v.number(),
|
|
172
|
+
acceptedAt: v.optional(v.number()),
|
|
173
|
+
})
|
|
174
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
175
|
+
.index("by_email", ["email"])
|
|
176
|
+
.index("by_inviteToken", ["inviteToken"])
|
|
177
|
+
.index("by_workspaceId_email", ["workspaceId", "email"]),
|
|
178
|
+
// Magic links for workspace email verification
|
|
179
|
+
workspaceMagicLinks: defineTable({
|
|
180
|
+
email: v.string(),
|
|
181
|
+
token: v.string(),
|
|
182
|
+
sessionFingerprint: v.optional(v.string()),
|
|
183
|
+
expiresAt: v.number(),
|
|
184
|
+
usedAt: v.optional(v.number()),
|
|
185
|
+
createdAt: v.number(),
|
|
186
|
+
})
|
|
187
|
+
.index("by_token", ["token"])
|
|
188
|
+
.index("by_email", ["email"]),
|
|
189
|
+
// Credit top-ups (from Stripe payments)
|
|
190
|
+
creditTopups: defineTable({
|
|
191
|
+
agentId: v.string(),
|
|
192
|
+
stripePaymentIntentId: v.optional(v.string()),
|
|
193
|
+
stripeSessionId: v.optional(v.string()),
|
|
194
|
+
amountUsd: v.number(),
|
|
195
|
+
creditsGranted: v.number(),
|
|
196
|
+
packageType: v.string(), // starter, growth, scale
|
|
197
|
+
status: v.string(), // pending, completed, failed
|
|
198
|
+
createdAt: v.number(),
|
|
199
|
+
completedAt: v.optional(v.number()),
|
|
200
|
+
})
|
|
201
|
+
.index("by_agentId", ["agentId"])
|
|
202
|
+
.index("by_stripeSessionId", ["stripeSessionId"])
|
|
203
|
+
.index("by_stripePaymentIntentId", ["stripePaymentIntentId"]),
|
|
204
|
+
// ============================================
|
|
205
|
+
// PROVIDER TABLES (for provider dashboard)
|
|
206
|
+
// ============================================
|
|
207
|
+
// API Providers (companies/individuals offering APIs)
|
|
208
|
+
providers: defineTable({
|
|
209
|
+
email: v.string(),
|
|
210
|
+
name: v.string(),
|
|
211
|
+
company: v.optional(v.string()),
|
|
212
|
+
website: v.optional(v.string()),
|
|
213
|
+
avatarUrl: v.optional(v.string()),
|
|
214
|
+
stripeConnectId: v.optional(v.string()), // for payouts
|
|
215
|
+
stripeOnboardingComplete: v.optional(v.boolean()),
|
|
216
|
+
status: v.string(), // pending, approved, rejected, suspended
|
|
217
|
+
createdAt: v.number(),
|
|
218
|
+
updatedAt: v.number(),
|
|
219
|
+
approvedAt: v.optional(v.number()),
|
|
220
|
+
})
|
|
221
|
+
.index("by_email", ["email"])
|
|
222
|
+
.index("by_stripeConnectId", ["stripeConnectId"])
|
|
223
|
+
.index("by_status", ["status"]),
|
|
224
|
+
// APIs listed by providers (self-service onboarding)
|
|
225
|
+
providerAPIs: defineTable({
|
|
226
|
+
providerId: v.id("providers"),
|
|
227
|
+
name: v.string(),
|
|
228
|
+
description: v.string(),
|
|
229
|
+
category: v.string(),
|
|
230
|
+
openApiUrl: v.optional(v.string()),
|
|
231
|
+
docsUrl: v.optional(v.string()),
|
|
232
|
+
pricingModel: v.string(), // free, freemium, paid
|
|
233
|
+
pricingNotes: v.optional(v.string()),
|
|
234
|
+
status: v.string(), // pending, approved, rejected, suspended
|
|
235
|
+
createdAt: v.number(),
|
|
236
|
+
approvedAt: v.optional(v.number()),
|
|
237
|
+
// Analytics
|
|
238
|
+
discoveryCount: v.optional(v.number()),
|
|
239
|
+
lastDiscoveredAt: v.optional(v.number()),
|
|
240
|
+
})
|
|
241
|
+
.index("by_providerId", ["providerId"])
|
|
242
|
+
.index("by_category", ["category"])
|
|
243
|
+
.index("by_status", ["status"])
|
|
244
|
+
.index("by_status_category", ["status", "category"]),
|
|
245
|
+
// APIs listed by providers (for full dashboard)
|
|
246
|
+
apis: defineTable({
|
|
247
|
+
providerId: v.id("providers"),
|
|
248
|
+
name: v.string(),
|
|
249
|
+
description: v.string(),
|
|
250
|
+
category: v.string(),
|
|
251
|
+
icon: v.optional(v.string()), // emoji or URL
|
|
252
|
+
baseUrl: v.string(),
|
|
253
|
+
docsUrl: v.optional(v.string()),
|
|
254
|
+
authType: v.string(), // api_key, oauth, basic, bearer
|
|
255
|
+
pricingModel: v.string(), // free, per_call, monthly, credits
|
|
256
|
+
pricePerCall: v.optional(v.number()), // in USD cents
|
|
257
|
+
monthlyPrice: v.optional(v.number()), // in USD cents
|
|
258
|
+
rateLimitPerMinute: v.optional(v.number()),
|
|
259
|
+
regions: v.optional(v.array(v.string())),
|
|
260
|
+
tags: v.optional(v.array(v.string())),
|
|
261
|
+
status: v.string(), // active, paused, pending_review
|
|
262
|
+
isPublic: v.boolean(),
|
|
263
|
+
// Credentials (encrypted in production)
|
|
264
|
+
credentialTemplate: v.optional(v.any()),
|
|
265
|
+
createdAt: v.number(),
|
|
266
|
+
updatedAt: v.number(),
|
|
267
|
+
})
|
|
268
|
+
.index("by_providerId", ["providerId"])
|
|
269
|
+
.index("by_category", ["category"])
|
|
270
|
+
.index("by_status", ["status"]),
|
|
271
|
+
// API Calls / Usage logs (for analytics)
|
|
272
|
+
apiCalls: defineTable({
|
|
273
|
+
apiId: v.id("apis"),
|
|
274
|
+
providerId: v.id("providers"),
|
|
275
|
+
agentId: v.string(),
|
|
276
|
+
endpoint: v.optional(v.string()),
|
|
277
|
+
method: v.optional(v.string()),
|
|
278
|
+
statusCode: v.optional(v.number()),
|
|
279
|
+
latencyMs: v.optional(v.number()),
|
|
280
|
+
costUsd: v.number(), // cost in USD (fractional)
|
|
281
|
+
region: v.optional(v.string()),
|
|
282
|
+
timestamp: v.number(),
|
|
283
|
+
})
|
|
284
|
+
.index("by_apiId", ["apiId"])
|
|
285
|
+
.index("by_providerId", ["providerId"])
|
|
286
|
+
.index("by_agentId", ["agentId"])
|
|
287
|
+
.index("by_timestamp", ["timestamp"])
|
|
288
|
+
.index("by_providerId_timestamp", ["providerId", "timestamp"]),
|
|
289
|
+
// Provider Payouts
|
|
290
|
+
payouts: defineTable({
|
|
291
|
+
providerId: v.id("providers"),
|
|
292
|
+
amountUsd: v.number(),
|
|
293
|
+
status: v.string(), // pending, processing, completed, failed
|
|
294
|
+
stripePayoutId: v.optional(v.string()),
|
|
295
|
+
periodStart: v.number(),
|
|
296
|
+
periodEnd: v.number(),
|
|
297
|
+
createdAt: v.number(),
|
|
298
|
+
completedAt: v.optional(v.number()),
|
|
299
|
+
})
|
|
300
|
+
.index("by_providerId", ["providerId"])
|
|
301
|
+
.index("by_status", ["status"]),
|
|
302
|
+
// Magic link tokens for email auth
|
|
303
|
+
magicLinks: defineTable({
|
|
304
|
+
email: v.string(),
|
|
305
|
+
token: v.string(),
|
|
306
|
+
expiresAt: v.number(),
|
|
307
|
+
usedAt: v.optional(v.number()),
|
|
308
|
+
createdAt: v.number(),
|
|
309
|
+
})
|
|
310
|
+
.index("by_token", ["token"])
|
|
311
|
+
.index("by_email", ["email"]),
|
|
312
|
+
// Sessions for authenticated providers
|
|
313
|
+
sessions: defineTable({
|
|
314
|
+
providerId: v.id("providers"),
|
|
315
|
+
token: v.string(),
|
|
316
|
+
expiresAt: v.number(),
|
|
317
|
+
createdAt: v.number(),
|
|
318
|
+
})
|
|
319
|
+
.index("by_token", ["token"])
|
|
320
|
+
.index("by_providerId", ["providerId"]),
|
|
321
|
+
// Rate limiting
|
|
322
|
+
rateLimits: defineTable({
|
|
323
|
+
key: v.string(),
|
|
324
|
+
identifier: v.string(),
|
|
325
|
+
action: v.string(),
|
|
326
|
+
count: v.number(),
|
|
327
|
+
hourBucket: v.number(),
|
|
328
|
+
createdAt: v.number(),
|
|
329
|
+
})
|
|
330
|
+
.index("by_key", ["key"])
|
|
331
|
+
.index("by_identifier", ["identifier"]),
|
|
332
|
+
// Usage analytics
|
|
333
|
+
analytics: defineTable({
|
|
334
|
+
event: v.string(), // "discovery", "instant", "search_query"
|
|
335
|
+
provider: v.optional(v.string()),
|
|
336
|
+
query: v.optional(v.string()),
|
|
337
|
+
identifier: v.string(),
|
|
338
|
+
workspaceId: v.optional(v.id("workspaces")), // Claimed workspace (null = anonymous)
|
|
339
|
+
metadata: v.optional(v.any()),
|
|
340
|
+
timestamp: v.number(),
|
|
341
|
+
})
|
|
342
|
+
.index("by_event", ["event"])
|
|
343
|
+
.index("by_timestamp", ["timestamp"])
|
|
344
|
+
.index("by_provider", ["provider"])
|
|
345
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
346
|
+
.index("by_identifier", ["identifier"]),
|
|
347
|
+
// MCP Server telemetry (anonymous usage tracking)
|
|
348
|
+
telemetry: defineTable({
|
|
349
|
+
type: v.string(), // "startup", "search", "execute", "discovery"
|
|
350
|
+
query: v.optional(v.string()),
|
|
351
|
+
apiId: v.optional(v.string()),
|
|
352
|
+
resultCount: v.optional(v.number()),
|
|
353
|
+
responseTimeMs: v.optional(v.number()),
|
|
354
|
+
version: v.string(),
|
|
355
|
+
platform: v.string(),
|
|
356
|
+
nodeVersion: v.string(),
|
|
357
|
+
timestamp: v.number(),
|
|
358
|
+
})
|
|
359
|
+
.index("by_type", ["type"])
|
|
360
|
+
.index("by_timestamp", ["timestamp"]),
|
|
361
|
+
// ============================================
|
|
362
|
+
// SELF-SERVICE DIRECT CALL TABLES
|
|
363
|
+
// ============================================
|
|
364
|
+
// Provider Direct Call configuration (master key, limits, pricing)
|
|
365
|
+
providerDirectCall: defineTable({
|
|
366
|
+
providerId: v.id("providers"),
|
|
367
|
+
apiId: v.optional(v.id("providerAPIs")),
|
|
368
|
+
baseUrl: v.string(),
|
|
369
|
+
authType: v.string(), // "bearer" | "basic" | "api_key" | "none"
|
|
370
|
+
authHeader: v.string(), // e.g. "Authorization", "X-API-Key"
|
|
371
|
+
authPrefix: v.string(), // e.g. "Bearer ", "Basic ", ""
|
|
372
|
+
encryptedMasterKey: v.string(),
|
|
373
|
+
rateLimitPerUser: v.number(), // requests per minute per user
|
|
374
|
+
rateLimitPerDay: v.number(), // requests per day per user
|
|
375
|
+
pricePerRequest: v.number(), // in USD cents
|
|
376
|
+
status: v.string(), // "draft" | "testing" | "live"
|
|
377
|
+
// Customer key passthrough settings
|
|
378
|
+
allowCustomerKeys: v.optional(v.boolean()), // Allow agents to pass their own API key (default: true)
|
|
379
|
+
requireCustomerKeys: v.optional(v.boolean()), // Require customer key, no master key fallback (default: false)
|
|
380
|
+
createdAt: v.number(),
|
|
381
|
+
updatedAt: v.number(),
|
|
382
|
+
publishedAt: v.optional(v.number()),
|
|
383
|
+
})
|
|
384
|
+
.index("by_providerId", ["providerId"])
|
|
385
|
+
.index("by_apiId", ["apiId"])
|
|
386
|
+
.index("by_status", ["status"]),
|
|
387
|
+
// Actions defined by providers for their Direct Call APIs
|
|
388
|
+
providerActions: defineTable({
|
|
389
|
+
directCallId: v.id("providerDirectCall"),
|
|
390
|
+
name: v.string(), // machine name, e.g. "send_sms"
|
|
391
|
+
displayName: v.string(), // human-friendly, e.g. "Send SMS"
|
|
392
|
+
description: v.string(),
|
|
393
|
+
method: v.string(), // "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
|
394
|
+
path: v.string(), // e.g. "/v1/messages" or "/users/{userId}"
|
|
395
|
+
params: v.array(v.object({
|
|
396
|
+
name: v.string(),
|
|
397
|
+
type: v.string(), // "string" | "number" | "boolean" | "object"
|
|
398
|
+
required: v.boolean(),
|
|
399
|
+
description: v.string(),
|
|
400
|
+
default: v.optional(v.any()),
|
|
401
|
+
in: v.string(), // "body" | "query" | "path"
|
|
402
|
+
})),
|
|
403
|
+
responseMapping: v.array(v.object({
|
|
404
|
+
name: v.string(),
|
|
405
|
+
path: v.string(), // JSON path, e.g. "data.id" or "results[0].name"
|
|
406
|
+
})),
|
|
407
|
+
enabled: v.boolean(),
|
|
408
|
+
// Confirmation settings for costly actions
|
|
409
|
+
requiresConfirmation: v.optional(v.boolean()), // If true, requires user confirmation before executing
|
|
410
|
+
estimatedCost: v.optional(v.string()), // Human-readable cost estimate, e.g. "~2-5 SEK per invoice"
|
|
411
|
+
createdAt: v.number(),
|
|
412
|
+
updatedAt: v.number(),
|
|
413
|
+
})
|
|
414
|
+
.index("by_directCallId", ["directCallId"])
|
|
415
|
+
.index("by_directCallId_name", ["directCallId", "name"]),
|
|
416
|
+
// Usage logs for Direct Call actions
|
|
417
|
+
usageLog: defineTable({
|
|
418
|
+
userId: v.string(),
|
|
419
|
+
providerId: v.id("providers"),
|
|
420
|
+
directCallId: v.id("providerDirectCall"),
|
|
421
|
+
actionName: v.string(),
|
|
422
|
+
timestamp: v.number(),
|
|
423
|
+
success: v.boolean(),
|
|
424
|
+
latencyMs: v.number(),
|
|
425
|
+
creditsUsed: v.number(), // in USD cents
|
|
426
|
+
errorMessage: v.optional(v.string()),
|
|
427
|
+
})
|
|
428
|
+
.index("by_userId", ["userId"])
|
|
429
|
+
.index("by_providerId", ["providerId"])
|
|
430
|
+
.index("by_directCallId", ["directCallId"])
|
|
431
|
+
.index("by_timestamp", ["timestamp"])
|
|
432
|
+
.index("by_userId_providerId", ["userId", "providerId"])
|
|
433
|
+
.index("by_userId_timestamp", ["userId", "timestamp"]),
|
|
434
|
+
// ============================================
|
|
435
|
+
// API LOGS (workspace/consumer view)
|
|
436
|
+
// ============================================
|
|
437
|
+
apiLogs: defineTable({
|
|
438
|
+
workspaceId: v.id("workspaces"),
|
|
439
|
+
sessionToken: v.string(),
|
|
440
|
+
subagentId: v.optional(v.string()), // from X-APIClaw-Subagent header
|
|
441
|
+
provider: v.string(),
|
|
442
|
+
action: v.string(),
|
|
443
|
+
status: v.union(v.literal("success"), v.literal("error")),
|
|
444
|
+
latencyMs: v.number(),
|
|
445
|
+
errorMessage: v.optional(v.string()),
|
|
446
|
+
createdAt: v.number(),
|
|
447
|
+
})
|
|
448
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
449
|
+
.index("by_createdAt", ["createdAt"])
|
|
450
|
+
.index("by_workspaceId_createdAt", ["workspaceId", "createdAt"])
|
|
451
|
+
.index("by_subagentId", ["subagentId"]),
|
|
452
|
+
// ============================================
|
|
453
|
+
// WAITLIST (for Direct Call provider leads)
|
|
454
|
+
// ============================================
|
|
455
|
+
waitlist: defineTable({
|
|
456
|
+
email: v.string(),
|
|
457
|
+
type: v.string(), // "provider" | "agent" | "general"
|
|
458
|
+
source: v.optional(v.string()), // "landing", "docs", etc.
|
|
459
|
+
createdAt: v.number(),
|
|
460
|
+
})
|
|
461
|
+
.index("by_email", ["email"])
|
|
462
|
+
.index("by_type", ["type"]),
|
|
463
|
+
// ============================================
|
|
464
|
+
// CAPABILITY LAYER (abstraction over providers)
|
|
465
|
+
// ============================================
|
|
466
|
+
// Capability definitions (sms, email, invoice, search, etc.)
|
|
467
|
+
capabilities: defineTable({
|
|
468
|
+
id: v.string(), // "sms", "email", "invoice"
|
|
469
|
+
name: v.string(), // "SMS Messaging"
|
|
470
|
+
description: v.string(),
|
|
471
|
+
category: v.string(), // "communication", "business", "ai"
|
|
472
|
+
standardParams: v.array(v.object({
|
|
473
|
+
name: v.string(),
|
|
474
|
+
type: v.string(), // "string" | "number" | "boolean"
|
|
475
|
+
required: v.boolean(),
|
|
476
|
+
description: v.string(),
|
|
477
|
+
default: v.optional(v.any()),
|
|
478
|
+
})),
|
|
479
|
+
createdAt: v.number(),
|
|
480
|
+
updatedAt: v.number(),
|
|
481
|
+
})
|
|
482
|
+
.index("by_capability_id", ["id"])
|
|
483
|
+
.index("by_category", ["category"]),
|
|
484
|
+
// Provider → Capability mappings (which providers offer which capabilities)
|
|
485
|
+
providerCapabilities: defineTable({
|
|
486
|
+
providerId: v.string(), // "46elks", "twilio"
|
|
487
|
+
capabilityId: v.string(), // "sms"
|
|
488
|
+
priority: v.number(), // 1 = primary, 2 = fallback
|
|
489
|
+
regions: v.array(v.string()), // ["SE", "EU", "US"]
|
|
490
|
+
pricePerUnit: v.number(), // in smallest currency unit (cents/öre)
|
|
491
|
+
currency: v.string(), // "SEK", "USD"
|
|
492
|
+
avgLatencyMs: v.number(),
|
|
493
|
+
paramMapping: v.any(), // Record<string, string> - capability param → provider param
|
|
494
|
+
enabled: v.boolean(),
|
|
495
|
+
healthStatus: v.string(), // "healthy" | "degraded" | "down"
|
|
496
|
+
lastHealthCheck: v.optional(v.number()),
|
|
497
|
+
createdAt: v.number(),
|
|
498
|
+
updatedAt: v.number(),
|
|
499
|
+
})
|
|
500
|
+
.index("by_providerId", ["providerId"])
|
|
501
|
+
.index("by_capabilityId", ["capabilityId"])
|
|
502
|
+
.index("by_capabilityId_enabled", ["capabilityId", "enabled"])
|
|
503
|
+
.index("by_healthStatus", ["healthStatus"]),
|
|
504
|
+
// Capability usage logs (for analytics and billing)
|
|
505
|
+
capabilityLogs: defineTable({
|
|
506
|
+
capabilityId: v.string(),
|
|
507
|
+
providerId: v.string(),
|
|
508
|
+
userId: v.string(),
|
|
509
|
+
action: v.string(),
|
|
510
|
+
success: v.boolean(),
|
|
511
|
+
fallbackUsed: v.boolean(),
|
|
512
|
+
fallbackReason: v.optional(v.string()),
|
|
513
|
+
latencyMs: v.number(),
|
|
514
|
+
cost: v.number(),
|
|
515
|
+
currency: v.string(),
|
|
516
|
+
timestamp: v.number(),
|
|
517
|
+
})
|
|
518
|
+
.index("by_capabilityId", ["capabilityId"])
|
|
519
|
+
.index("by_providerId", ["providerId"])
|
|
520
|
+
.index("by_userId", ["userId"])
|
|
521
|
+
.index("by_timestamp", ["timestamp"]),
|
|
522
|
+
// ============================================
|
|
523
|
+
// WEBHOOKS
|
|
524
|
+
// ============================================
|
|
525
|
+
webhooks: defineTable({
|
|
526
|
+
workspaceId: v.id("workspaces"),
|
|
527
|
+
url: v.string(),
|
|
528
|
+
events: v.array(v.string()),
|
|
529
|
+
secret: v.string(), // For signature verification
|
|
530
|
+
enabled: v.boolean(),
|
|
531
|
+
lastTriggeredAt: v.optional(v.number()),
|
|
532
|
+
lastStatus: v.optional(v.string()), // "success" | "failed"
|
|
533
|
+
failCount: v.number(),
|
|
534
|
+
createdAt: v.number(),
|
|
535
|
+
})
|
|
536
|
+
.index("by_workspaceId", ["workspaceId"]),
|
|
537
|
+
// ============================================
|
|
538
|
+
// BYOK - BRING YOUR OWN KEY
|
|
539
|
+
// ============================================
|
|
540
|
+
// User-provided API keys for providers
|
|
541
|
+
providerKeys: defineTable({
|
|
542
|
+
workspaceId: v.id("workspaces"),
|
|
543
|
+
provider: v.string(), // "brave_search", "openrouter", etc.
|
|
544
|
+
encryptedKey: v.string(), // Base64 encoded for MVP
|
|
545
|
+
keyHint: v.string(), // Last 4 chars for display
|
|
546
|
+
isCustom: v.boolean(), // true if custom provider (not built-in)
|
|
547
|
+
customConfig: v.optional(v.object({
|
|
548
|
+
baseUrl: v.string(),
|
|
549
|
+
authType: v.string(), // "bearer", "api_key", "basic"
|
|
550
|
+
authHeader: v.optional(v.string()), // e.g. "X-API-Key"
|
|
551
|
+
})),
|
|
552
|
+
createdAt: v.number(),
|
|
553
|
+
updatedAt: v.number(),
|
|
554
|
+
})
|
|
555
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
556
|
+
.index("by_provider", ["workspaceId", "provider"]),
|
|
557
|
+
// ============================================
|
|
558
|
+
// EARN PROGRESS TRACKING
|
|
559
|
+
// ============================================
|
|
560
|
+
earnProgress: defineTable({
|
|
561
|
+
workspaceId: v.id("workspaces"),
|
|
562
|
+
// Usage tasks
|
|
563
|
+
firstDirectCall: v.boolean(),
|
|
564
|
+
firstDirectCallAt: v.optional(v.number()),
|
|
565
|
+
apisUsed: v.array(v.string()), // Track unique provider/action combos
|
|
566
|
+
apisUsedComplete: v.boolean(),
|
|
567
|
+
agentListed: v.boolean(),
|
|
568
|
+
agentListedAt: v.optional(v.number()),
|
|
569
|
+
apiListed: v.boolean(),
|
|
570
|
+
apiListedAt: v.optional(v.number()),
|
|
571
|
+
byokSetup: v.boolean(),
|
|
572
|
+
byokSetupAt: v.optional(v.number()),
|
|
573
|
+
// Growth tasks
|
|
574
|
+
githubStarred: v.boolean(),
|
|
575
|
+
githubStarredAt: v.optional(v.number()),
|
|
576
|
+
twitterFollowed: v.boolean(),
|
|
577
|
+
twitterFollowedAt: v.optional(v.number()),
|
|
578
|
+
// Referrals (tracked separately but stored here for convenience)
|
|
579
|
+
referralCount: v.number(),
|
|
580
|
+
// Calculated total
|
|
581
|
+
totalEarned: v.number(),
|
|
582
|
+
createdAt: v.number(),
|
|
583
|
+
updatedAt: v.number(),
|
|
584
|
+
})
|
|
585
|
+
.index("by_workspaceId", ["workspaceId"]),
|
|
586
|
+
// ============================================
|
|
587
|
+
// FEEDBACK SYSTEM
|
|
588
|
+
// ============================================
|
|
589
|
+
// ============================================
|
|
590
|
+
// CHAIN ORCHESTRATION TABLES
|
|
591
|
+
// ============================================
|
|
592
|
+
// Chain executions (main orchestration record)
|
|
593
|
+
chains: defineTable({
|
|
594
|
+
workspaceId: v.id("workspaces"),
|
|
595
|
+
// Chain definition
|
|
596
|
+
steps: v.array(v.any()), // Array of step definitions (raw, unresolved)
|
|
597
|
+
// Execution state
|
|
598
|
+
status: v.union(v.literal("pending"), v.literal("running"), v.literal("completed"), v.literal("failed"), v.literal("paused")),
|
|
599
|
+
currentStep: v.number(), // Index of current step (0-based)
|
|
600
|
+
// Results storage
|
|
601
|
+
results: v.any(), // Record<stepId, result>
|
|
602
|
+
// Error tracking
|
|
603
|
+
error: v.optional(v.object({
|
|
604
|
+
stepId: v.string(),
|
|
605
|
+
code: v.string(),
|
|
606
|
+
message: v.string(),
|
|
607
|
+
retryAfter: v.optional(v.number()),
|
|
608
|
+
})),
|
|
609
|
+
// Execution options
|
|
610
|
+
continueOnError: v.optional(v.boolean()),
|
|
611
|
+
timeout: v.optional(v.number()), // ms
|
|
612
|
+
// Resume capability
|
|
613
|
+
resumeToken: v.optional(v.string()),
|
|
614
|
+
canResume: v.optional(v.boolean()),
|
|
615
|
+
// Cost tracking
|
|
616
|
+
totalCostCents: v.optional(v.number()),
|
|
617
|
+
totalLatencyMs: v.optional(v.number()),
|
|
618
|
+
// Timestamps
|
|
619
|
+
createdAt: v.number(),
|
|
620
|
+
startedAt: v.optional(v.number()),
|
|
621
|
+
completedAt: v.optional(v.number()),
|
|
622
|
+
})
|
|
623
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
624
|
+
.index("by_status", ["status"])
|
|
625
|
+
.index("by_workspaceId_status", ["workspaceId", "status"])
|
|
626
|
+
.index("by_resumeToken", ["resumeToken"]),
|
|
627
|
+
// Chain templates (reusable chain definitions)
|
|
628
|
+
chainTemplates: defineTable({
|
|
629
|
+
workspaceId: v.id("workspaces"),
|
|
630
|
+
name: v.string(),
|
|
631
|
+
description: v.optional(v.string()),
|
|
632
|
+
// Input schema for the template
|
|
633
|
+
inputs: v.optional(v.any()), // JSON Schema for inputs
|
|
634
|
+
// Chain definition
|
|
635
|
+
chain: v.array(v.any()), // Array of step definitions
|
|
636
|
+
// Usage tracking
|
|
637
|
+
useCount: v.optional(v.number()),
|
|
638
|
+
lastUsedAt: v.optional(v.number()),
|
|
639
|
+
// Timestamps
|
|
640
|
+
createdAt: v.number(),
|
|
641
|
+
updatedAt: v.number(),
|
|
642
|
+
})
|
|
643
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
644
|
+
.index("by_name", ["workspaceId", "name"]),
|
|
645
|
+
// Chain step executions (detailed trace per step)
|
|
646
|
+
chainExecutions: defineTable({
|
|
647
|
+
chainId: v.id("chains"),
|
|
648
|
+
stepId: v.string(), // The id from step definition
|
|
649
|
+
stepIndex: v.number(), // Position in chain
|
|
650
|
+
// Execution state
|
|
651
|
+
status: v.union(v.literal("pending"), v.literal("running"), v.literal("completed"), v.literal("failed"), v.literal("skipped")),
|
|
652
|
+
// I/O
|
|
653
|
+
input: v.optional(v.any()), // Resolved params sent to provider
|
|
654
|
+
output: v.optional(v.any()), // Result from provider
|
|
655
|
+
// Metrics
|
|
656
|
+
latencyMs: v.optional(v.number()),
|
|
657
|
+
costCents: v.optional(v.number()),
|
|
658
|
+
// Error info
|
|
659
|
+
error: v.optional(v.object({
|
|
660
|
+
code: v.string(),
|
|
661
|
+
message: v.string(),
|
|
662
|
+
retryCount: v.optional(v.number()),
|
|
663
|
+
})),
|
|
664
|
+
// Parallel execution tracking
|
|
665
|
+
parallelGroup: v.optional(v.string()), // Group ID if part of parallel batch
|
|
666
|
+
// Timestamps
|
|
667
|
+
createdAt: v.number(),
|
|
668
|
+
startedAt: v.optional(v.number()),
|
|
669
|
+
completedAt: v.optional(v.number()),
|
|
670
|
+
})
|
|
671
|
+
.index("by_chainId", ["chainId"])
|
|
672
|
+
.index("by_chainId_stepId", ["chainId", "stepId"])
|
|
673
|
+
.index("by_chainId_stepIndex", ["chainId", "stepIndex"]),
|
|
674
|
+
// User feedback with voting
|
|
675
|
+
feedback: defineTable({
|
|
676
|
+
workspaceId: v.id("workspaces"),
|
|
677
|
+
type: v.union(v.literal("bug"), v.literal("feature"), v.literal("general")),
|
|
678
|
+
content: v.string(),
|
|
679
|
+
votes: v.number(),
|
|
680
|
+
votedBy: v.array(v.string()), // workspace IDs that voted
|
|
681
|
+
status: v.union(v.literal("new"), v.literal("reviewing"), v.literal("planned"), v.literal("shipped")),
|
|
682
|
+
createdAt: v.number(),
|
|
683
|
+
})
|
|
684
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
685
|
+
.index("by_type", ["type"])
|
|
686
|
+
.index("by_status", ["status"])
|
|
687
|
+
.index("by_votes", ["votes"])
|
|
688
|
+
.index("by_createdAt", ["createdAt"]),
|
|
689
|
+
// ============================================
|
|
690
|
+
// MOU SIGNATURES
|
|
691
|
+
// ============================================
|
|
692
|
+
mouDocuments: defineTable({
|
|
693
|
+
partnerId: v.string(), // e.g., "apilayer"
|
|
694
|
+
partnerName: v.string(),
|
|
695
|
+
partnerEmail: v.string(),
|
|
696
|
+
partnerRepresentative: v.optional(v.string()),
|
|
697
|
+
documentHtml: v.optional(v.string()),
|
|
698
|
+
sections: v.optional(v.any()), // Alternative document format
|
|
699
|
+
status: v.string(), // "pending" | "signed"
|
|
700
|
+
signedAt: v.optional(v.number()),
|
|
701
|
+
signatureDataUrl: v.optional(v.string()), // base64 signature image
|
|
702
|
+
signerName: v.optional(v.string()),
|
|
703
|
+
signerTitle: v.optional(v.string()),
|
|
704
|
+
signerIp: v.optional(v.string()),
|
|
705
|
+
createdAt: v.number(),
|
|
706
|
+
})
|
|
707
|
+
.index("by_partnerId", ["partnerId"])
|
|
708
|
+
.index("by_status", ["status"]),
|
|
709
|
+
});
|