@nordsym/apiclaw 1.3.7 → 1.3.8
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 +420 -200
- package/convex/_generated/api.d.ts +4 -0
- package/convex/agents.ts +403 -0
- package/convex/directCall.ts +80 -0
- package/convex/earnProgress.ts +753 -0
- package/convex/logs.ts +17 -0
- package/convex/providerKeys.ts +82 -2
- package/convex/schema.ts +71 -2
- package/convex/workspaces.ts +84 -2
- package/dist/adapters/base.d.ts +112 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +247 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/claude-desktop.d.ts +12 -0
- package/dist/adapters/claude-desktop.d.ts.map +1 -0
- package/dist/adapters/claude-desktop.js +36 -0
- package/dist/adapters/claude-desktop.js.map +1 -0
- package/dist/adapters/cline.d.ts +20 -0
- package/dist/adapters/cline.d.ts.map +1 -0
- package/dist/adapters/cline.js +77 -0
- package/dist/adapters/cline.js.map +1 -0
- package/dist/adapters/continue.d.ts +26 -0
- package/dist/adapters/continue.d.ts.map +1 -0
- package/dist/adapters/continue.js +68 -0
- package/dist/adapters/continue.js.map +1 -0
- package/dist/adapters/cursor.d.ts +12 -0
- package/dist/adapters/cursor.d.ts.map +1 -0
- package/dist/adapters/cursor.js +38 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/custom.d.ts +47 -0
- package/dist/adapters/custom.d.ts.map +1 -0
- package/dist/adapters/custom.js +146 -0
- package/dist/adapters/custom.js.map +1 -0
- package/dist/adapters/detect.d.ts +69 -0
- package/dist/adapters/detect.d.ts.map +1 -0
- package/dist/adapters/detect.js +158 -0
- package/dist/adapters/detect.js.map +1 -0
- package/dist/adapters/index.d.ts +21 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +23 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/windsurf.d.ts +12 -0
- package/dist/adapters/windsurf.d.ts.map +1 -0
- package/dist/adapters/windsurf.js +39 -0
- package/dist/adapters/windsurf.js.map +1 -0
- package/dist/bin.d.ts +9 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +19 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +34 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +312 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/index.d.ts +9 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +9 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/restore.d.ts +50 -0
- package/dist/cli/commands/restore.d.ts.map +1 -0
- package/dist/cli/commands/restore.js +260 -0
- package/dist/cli/commands/restore.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +19 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +206 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/uninstall.d.ts +37 -0
- package/dist/cli/commands/uninstall.d.ts.map +1 -0
- package/dist/cli/commands/uninstall.js +189 -0
- package/dist/cli/commands/uninstall.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +97 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/discovery.d.ts +6 -2
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +296 -2
- package/dist/discovery.js.map +1 -1
- package/dist/enterprise/env.d.ts +56 -0
- package/dist/enterprise/env.d.ts.map +1 -0
- package/dist/enterprise/env.js +124 -0
- package/dist/enterprise/env.js.map +1 -0
- package/dist/enterprise/index.d.ts +7 -0
- package/dist/enterprise/index.d.ts.map +1 -0
- package/dist/enterprise/index.js +7 -0
- package/dist/enterprise/index.js.map +1 -0
- package/dist/enterprise/script-generator.d.ts +32 -0
- package/dist/enterprise/script-generator.d.ts.map +1 -0
- package/dist/enterprise/script-generator.js +461 -0
- package/dist/enterprise/script-generator.js.map +1 -0
- package/dist/execute.d.ts +21 -0
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +231 -0
- package/dist/execute.js.map +1 -1
- package/dist/index.js +79 -7
- package/dist/index.js.map +1 -1
- package/dist/stripe.d.ts +1 -1
- package/dist/stripe.js +1 -1
- package/dist/stripe.js.map +1 -1
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/colors.d.ts +111 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +185 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/ui/errors.d.ts +69 -0
- package/dist/ui/errors.d.ts.map +1 -0
- package/dist/ui/errors.js +334 -0
- package/dist/ui/errors.js.map +1 -0
- package/dist/ui/index.d.ts +10 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +14 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/prompts.d.ts +88 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +295 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/ui/spinner.d.ts +112 -0
- package/dist/ui/spinner.d.ts.map +1 -0
- package/dist/ui/spinner.js +229 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/utils/backup.d.ts +48 -0
- package/dist/utils/backup.d.ts.map +1 -0
- package/dist/utils/backup.js +182 -0
- package/dist/utils/backup.js.map +1 -0
- package/dist/utils/config.d.ts +80 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +221 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/os.d.ts +45 -0
- package/dist/utils/os.d.ts.map +1 -0
- package/dist/utils/os.js +106 -0
- package/dist/utils/os.js.map +1 -0
- package/dist/utils/paths.d.ts +38 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +160 -0
- package/dist/utils/paths.js.map +1 -0
- package/docs/PRD-BILLING.md +226 -0
- package/docs/PRD-EARN-SYSTEM.md +261 -0
- package/docs/PRD-MCP-AUTO-SETUP.md +623 -0
- package/docs/enterprise-deployment.md +728 -0
- package/landing/next.config.mjs +14 -0
- package/landing/public/stats.json +4 -2
- package/landing/scripts/generate-stats.js +12 -0
- package/landing/src/app/api/workspace-auth/magic-link/route.ts +6 -3
- package/landing/src/app/auth/verify/page.tsx +11 -4
- package/landing/src/app/docs/page.tsx +1 -1
- package/landing/src/app/join/page.tsx +49 -0
- package/landing/src/app/login/page.tsx +7 -1
- package/landing/src/app/page.tsx +13 -28
- package/landing/src/app/providers/register/page.tsx +1 -1
- package/landing/src/app/workspace/page.tsx +483 -710
- package/landing/src/components/CheckoutButton.tsx +1 -1
- package/landing/src/components/EarnCreditsTab.tsx +842 -0
- package/landing/src/lib/stats.json +3 -1
- package/package.json +9 -2
- package/src/adapters/base.ts +363 -0
- package/src/adapters/claude-desktop.ts +41 -0
- package/src/adapters/cline.ts +88 -0
- package/src/adapters/continue.ts +91 -0
- package/src/adapters/cursor.ts +43 -0
- package/src/adapters/custom.ts +188 -0
- package/src/adapters/detect.ts +202 -0
- package/src/adapters/index.ts +47 -0
- package/src/adapters/windsurf.ts +44 -0
- package/src/bin.ts +19 -0
- package/src/cli/commands/doctor.ts +367 -0
- package/src/cli/commands/index.ts +9 -0
- package/src/cli/commands/restore.ts +333 -0
- package/src/cli/commands/setup.ts +276 -0
- package/src/cli/commands/uninstall.ts +240 -0
- package/src/cli/index.ts +107 -0
- package/src/discovery.ts +328 -3
- package/src/enterprise/env.ts +156 -0
- package/src/enterprise/index.ts +7 -0
- package/src/enterprise/script-generator.ts +481 -0
- package/src/execute.ts +256 -0
- package/src/index.ts +85 -7
- package/src/stripe.ts +1 -1
- package/src/types.ts +32 -0
- package/src/ui/colors.ts +219 -0
- package/src/ui/errors.ts +394 -0
- package/src/ui/index.ts +17 -0
- package/src/ui/prompts.ts +390 -0
- package/src/ui/spinner.ts +325 -0
- package/src/utils/backup.ts +224 -0
- package/src/utils/config.ts +315 -0
- package/src/utils/os.ts +124 -0
- package/src/utils/paths.ts +203 -0
- package/landing/tsconfig.tsbuildinfo +0 -1
package/convex/logs.ts
CHANGED
|
@@ -17,6 +17,7 @@ export const createLog = mutation({
|
|
|
17
17
|
status: v.union(v.literal("success"), v.literal("error")),
|
|
18
18
|
latencyMs: v.number(),
|
|
19
19
|
errorMessage: v.optional(v.string()),
|
|
20
|
+
subagentId: v.optional(v.string()), // from X-APIClaw-Subagent header
|
|
20
21
|
},
|
|
21
22
|
handler: async (ctx, args) => {
|
|
22
23
|
// Verify session and get workspace
|
|
@@ -33,6 +34,7 @@ export const createLog = mutation({
|
|
|
33
34
|
return await ctx.db.insert("apiLogs", {
|
|
34
35
|
workspaceId: session.workspaceId,
|
|
35
36
|
sessionToken: args.token,
|
|
37
|
+
subagentId: args.subagentId,
|
|
36
38
|
provider: args.provider,
|
|
37
39
|
action: args.action,
|
|
38
40
|
status: args.status,
|
|
@@ -56,11 +58,13 @@ export const createLogInternal = mutation({
|
|
|
56
58
|
status: v.union(v.literal("success"), v.literal("error")),
|
|
57
59
|
latencyMs: v.number(),
|
|
58
60
|
errorMessage: v.optional(v.string()),
|
|
61
|
+
subagentId: v.optional(v.string()), // from X-APIClaw-Subagent header
|
|
59
62
|
},
|
|
60
63
|
handler: async (ctx, args) => {
|
|
61
64
|
return await ctx.db.insert("apiLogs", {
|
|
62
65
|
workspaceId: args.workspaceId,
|
|
63
66
|
sessionToken: args.sessionToken,
|
|
67
|
+
subagentId: args.subagentId,
|
|
64
68
|
provider: args.provider,
|
|
65
69
|
action: args.action,
|
|
66
70
|
status: args.status,
|
|
@@ -85,11 +89,13 @@ export const getLogs = query({
|
|
|
85
89
|
cursor: v.optional(v.number()), // createdAt timestamp for pagination
|
|
86
90
|
status: v.optional(v.union(v.literal("success"), v.literal("error"), v.literal("all"))),
|
|
87
91
|
provider: v.optional(v.string()),
|
|
92
|
+
subagentId: v.optional(v.string()), // filter by subagent
|
|
88
93
|
},
|
|
89
94
|
handler: async (ctx, args) => {
|
|
90
95
|
const limit = args.limit ?? 50;
|
|
91
96
|
const status = args.status ?? "all";
|
|
92
97
|
const provider = args.provider;
|
|
98
|
+
const subagentId = args.subagentId;
|
|
93
99
|
const cursor = args.cursor;
|
|
94
100
|
|
|
95
101
|
// Verify session
|
|
@@ -127,6 +133,16 @@ export const getLogs = query({
|
|
|
127
133
|
filteredLogs = filteredLogs.filter((log) => log.provider === provider);
|
|
128
134
|
}
|
|
129
135
|
|
|
136
|
+
// Filter by subagent
|
|
137
|
+
if (subagentId) {
|
|
138
|
+
if (subagentId === "main") {
|
|
139
|
+
// Main agent calls (no subagentId)
|
|
140
|
+
filteredLogs = filteredLogs.filter((log) => !log.subagentId);
|
|
141
|
+
} else {
|
|
142
|
+
filteredLogs = filteredLogs.filter((log) => log.subagentId === subagentId);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
130
146
|
const hasMore = filteredLogs.length > limit;
|
|
131
147
|
const logs = filteredLogs.slice(0, limit);
|
|
132
148
|
|
|
@@ -138,6 +154,7 @@ export const getLogs = query({
|
|
|
138
154
|
status: log.status,
|
|
139
155
|
latencyMs: log.latencyMs,
|
|
140
156
|
errorMessage: log.errorMessage,
|
|
157
|
+
subagentId: log.subagentId || null,
|
|
141
158
|
createdAt: log.createdAt,
|
|
142
159
|
})),
|
|
143
160
|
hasMore,
|
package/convex/providerKeys.ts
CHANGED
|
@@ -64,6 +64,8 @@ export const addKey = mutation({
|
|
|
64
64
|
const encryptedKey = encryptKey(args.apiKey);
|
|
65
65
|
const keyHint = getKeyHint(args.apiKey);
|
|
66
66
|
|
|
67
|
+
let isFirstKey = false;
|
|
68
|
+
|
|
67
69
|
if (existingKey) {
|
|
68
70
|
// Update existing key
|
|
69
71
|
await ctx.db.patch(existingKey._id, {
|
|
@@ -71,7 +73,6 @@ export const addKey = mutation({
|
|
|
71
73
|
keyHint,
|
|
72
74
|
updatedAt: now,
|
|
73
75
|
});
|
|
74
|
-
return { success: true, action: "updated" };
|
|
75
76
|
} else {
|
|
76
77
|
// Create new key
|
|
77
78
|
await ctx.db.insert("providerKeys", {
|
|
@@ -83,11 +84,90 @@ export const addKey = mutation({
|
|
|
83
84
|
createdAt: now,
|
|
84
85
|
updatedAt: now,
|
|
85
86
|
});
|
|
86
|
-
|
|
87
|
+
|
|
88
|
+
// Check if this is the first BYOK key for earn progress
|
|
89
|
+
const allKeys = await ctx.db
|
|
90
|
+
.query("providerKeys")
|
|
91
|
+
.withIndex("by_workspaceId", (q) => q.eq("workspaceId", workspaceId))
|
|
92
|
+
.collect();
|
|
93
|
+
|
|
94
|
+
// If this is the only key (the one we just created), mark BYOK setup
|
|
95
|
+
if (allKeys.length === 1) {
|
|
96
|
+
isFirstKey = true;
|
|
97
|
+
// Import and call markByokSetup
|
|
98
|
+
const earnProgress = await ctx.db
|
|
99
|
+
.query("earnProgress")
|
|
100
|
+
.withIndex("by_workspaceId", (q) => q.eq("workspaceId", workspaceId))
|
|
101
|
+
.first();
|
|
102
|
+
|
|
103
|
+
if (earnProgress && !earnProgress.byokSetup) {
|
|
104
|
+
const newTotal = calculateEarnTotal({ ...earnProgress, byokSetup: true });
|
|
105
|
+
await ctx.db.patch(earnProgress._id, {
|
|
106
|
+
byokSetup: true,
|
|
107
|
+
byokSetupAt: now,
|
|
108
|
+
totalEarned: newTotal,
|
|
109
|
+
updatedAt: now,
|
|
110
|
+
});
|
|
111
|
+
// Add 5 calls to workspace limit
|
|
112
|
+
const workspace = await ctx.db.get(workspaceId);
|
|
113
|
+
if (workspace) {
|
|
114
|
+
await ctx.db.patch(workspaceId, {
|
|
115
|
+
usageLimit: workspace.usageLimit + 5,
|
|
116
|
+
updatedAt: now,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
} else if (!earnProgress) {
|
|
120
|
+
// Create earn progress with byokSetup
|
|
121
|
+
await ctx.db.insert("earnProgress", {
|
|
122
|
+
workspaceId,
|
|
123
|
+
firstDirectCall: false,
|
|
124
|
+
apisUsed: [],
|
|
125
|
+
apisUsedComplete: false,
|
|
126
|
+
agentListed: false,
|
|
127
|
+
apiListed: false,
|
|
128
|
+
byokSetup: true,
|
|
129
|
+
byokSetupAt: now,
|
|
130
|
+
githubStarred: false,
|
|
131
|
+
twitterFollowed: false,
|
|
132
|
+
referralCount: 0,
|
|
133
|
+
totalEarned: 5, // BYOK reward
|
|
134
|
+
createdAt: now,
|
|
135
|
+
updatedAt: now,
|
|
136
|
+
});
|
|
137
|
+
// Add 5 calls to workspace limit
|
|
138
|
+
const workspace = await ctx.db.get(workspaceId);
|
|
139
|
+
if (workspace) {
|
|
140
|
+
await ctx.db.patch(workspaceId, {
|
|
141
|
+
usageLimit: workspace.usageLimit + 5,
|
|
142
|
+
updatedAt: now,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
87
147
|
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
success: true,
|
|
151
|
+
action: existingKey ? "updated" : "created",
|
|
152
|
+
earnedByok: isFirstKey,
|
|
153
|
+
};
|
|
88
154
|
},
|
|
89
155
|
});
|
|
90
156
|
|
|
157
|
+
// Helper to calculate earn total (duplicated to avoid circular import)
|
|
158
|
+
function calculateEarnTotal(progress: any): number {
|
|
159
|
+
let total = 0;
|
|
160
|
+
if (progress.firstDirectCall) total += 15;
|
|
161
|
+
if (progress.apisUsedComplete) total += 10;
|
|
162
|
+
if (progress.agentListed) total += 10;
|
|
163
|
+
if (progress.apiListed) total += 10;
|
|
164
|
+
if (progress.byokSetup) total += 5;
|
|
165
|
+
if (progress.githubStarred) total += 10;
|
|
166
|
+
if (progress.twitterFollowed) total += 5;
|
|
167
|
+
total += (progress.referralCount || 0) * 10;
|
|
168
|
+
return total;
|
|
169
|
+
}
|
|
170
|
+
|
|
91
171
|
// ============================================
|
|
92
172
|
// REMOVE KEY
|
|
93
173
|
// ============================================
|
package/convex/schema.ts
CHANGED
|
@@ -49,19 +49,32 @@ export default defineSchema({
|
|
|
49
49
|
tier: v.string(), // "free" | "pro" | "enterprise"
|
|
50
50
|
usageCount: v.number(), // total API calls made
|
|
51
51
|
usageLimit: v.number(), // max API calls for tier
|
|
52
|
+
// Main agent identification
|
|
53
|
+
mainAgentId: v.optional(v.string()), // UUID, auto-generated on first call
|
|
54
|
+
mainAgentName: v.optional(v.string()), // Auto-generated name (e.g., "Crimson Phoenix")
|
|
52
55
|
// Stripe billing fields
|
|
53
56
|
stripeCustomerId: v.optional(v.string()),
|
|
54
57
|
stripeSubscriptionId: v.optional(v.string()),
|
|
55
58
|
billingPlan: v.optional(v.string()), // "free" | "usage_based" | "starter" | "pro" | "scale"
|
|
56
59
|
creditBalance: v.optional(v.number()), // prepaid credits in cents
|
|
57
60
|
lastBillingDate: v.optional(v.number()),
|
|
61
|
+
// Payment method fields
|
|
62
|
+
hasPaymentMethod: v.optional(v.boolean()),
|
|
63
|
+
paymentMethodType: v.optional(v.string()),
|
|
64
|
+
cardBrand: v.optional(v.string()),
|
|
65
|
+
cardLast4: v.optional(v.string()),
|
|
66
|
+
// Referral fields
|
|
67
|
+
referralCode: v.optional(v.string()), // CLAW-XXXXXX format
|
|
68
|
+
referredBy: v.optional(v.id("workspaces")), // who referred this user
|
|
58
69
|
createdAt: v.number(),
|
|
59
70
|
updatedAt: v.number(),
|
|
60
71
|
})
|
|
61
72
|
.index("by_email", ["email"])
|
|
62
73
|
.index("by_stripeCustomerId", ["stripeCustomerId"])
|
|
63
74
|
.index("by_stripeSubscriptionId", ["stripeSubscriptionId"])
|
|
64
|
-
.index("by_status", ["status"])
|
|
75
|
+
.index("by_status", ["status"])
|
|
76
|
+
.index("by_referralCode", ["referralCode"])
|
|
77
|
+
.index("by_mainAgentId", ["mainAgentId"]),
|
|
65
78
|
|
|
66
79
|
// Invoices (Stripe invoice records)
|
|
67
80
|
invoices: defineTable({
|
|
@@ -106,6 +119,19 @@ export default defineSchema({
|
|
|
106
119
|
.index("by_sessionToken", ["sessionToken"])
|
|
107
120
|
.index("by_workspaceId", ["workspaceId"]),
|
|
108
121
|
|
|
122
|
+
// Subagent tracking (tasks spawned by main agent)
|
|
123
|
+
subagents: defineTable({
|
|
124
|
+
workspaceId: v.id("workspaces"),
|
|
125
|
+
subagentId: v.string(), // from X-APIClaw-Subagent header
|
|
126
|
+
name: v.optional(v.string()), // optional display name
|
|
127
|
+
callCount: v.number(),
|
|
128
|
+
firstSeenAt: v.number(),
|
|
129
|
+
lastActiveAt: v.number(),
|
|
130
|
+
})
|
|
131
|
+
.index("by_workspaceId", ["workspaceId"])
|
|
132
|
+
.index("by_workspaceId_subagentId", ["workspaceId", "subagentId"])
|
|
133
|
+
.index("by_lastActiveAt", ["lastActiveAt"]),
|
|
134
|
+
|
|
109
135
|
// Magic links for workspace email verification
|
|
110
136
|
workspaceMagicLinks: defineTable({
|
|
111
137
|
email: v.string(),
|
|
@@ -383,6 +409,7 @@ export default defineSchema({
|
|
|
383
409
|
apiLogs: defineTable({
|
|
384
410
|
workspaceId: v.id("workspaces"),
|
|
385
411
|
sessionToken: v.string(),
|
|
412
|
+
subagentId: v.optional(v.string()), // from X-APIClaw-Subagent header
|
|
386
413
|
provider: v.string(),
|
|
387
414
|
action: v.string(),
|
|
388
415
|
status: v.union(v.literal("success"), v.literal("error")),
|
|
@@ -392,7 +419,8 @@ export default defineSchema({
|
|
|
392
419
|
})
|
|
393
420
|
.index("by_workspaceId", ["workspaceId"])
|
|
394
421
|
.index("by_createdAt", ["createdAt"])
|
|
395
|
-
.index("by_workspaceId_createdAt", ["workspaceId", "createdAt"])
|
|
422
|
+
.index("by_workspaceId_createdAt", ["workspaceId", "createdAt"])
|
|
423
|
+
.index("by_subagentId", ["subagentId"]),
|
|
396
424
|
|
|
397
425
|
// ============================================
|
|
398
426
|
// WAITLIST (for Direct Call provider leads)
|
|
@@ -509,6 +537,47 @@ export default defineSchema({
|
|
|
509
537
|
.index("by_workspaceId", ["workspaceId"])
|
|
510
538
|
.index("by_provider", ["workspaceId", "provider"]),
|
|
511
539
|
|
|
540
|
+
// ============================================
|
|
541
|
+
// EARN PROGRESS TRACKING
|
|
542
|
+
// ============================================
|
|
543
|
+
|
|
544
|
+
earnProgress: defineTable({
|
|
545
|
+
workspaceId: v.id("workspaces"),
|
|
546
|
+
|
|
547
|
+
// Usage tasks
|
|
548
|
+
firstDirectCall: v.boolean(),
|
|
549
|
+
firstDirectCallAt: v.optional(v.number()),
|
|
550
|
+
|
|
551
|
+
apisUsed: v.array(v.string()), // Track unique provider/action combos
|
|
552
|
+
apisUsedComplete: v.boolean(),
|
|
553
|
+
|
|
554
|
+
agentListed: v.boolean(),
|
|
555
|
+
agentListedAt: v.optional(v.number()),
|
|
556
|
+
|
|
557
|
+
apiListed: v.boolean(),
|
|
558
|
+
apiListedAt: v.optional(v.number()),
|
|
559
|
+
|
|
560
|
+
byokSetup: v.boolean(),
|
|
561
|
+
byokSetupAt: v.optional(v.number()),
|
|
562
|
+
|
|
563
|
+
// Growth tasks
|
|
564
|
+
githubStarred: v.boolean(),
|
|
565
|
+
githubStarredAt: v.optional(v.number()),
|
|
566
|
+
|
|
567
|
+
twitterFollowed: v.boolean(),
|
|
568
|
+
twitterFollowedAt: v.optional(v.number()),
|
|
569
|
+
|
|
570
|
+
// Referrals (tracked separately but stored here for convenience)
|
|
571
|
+
referralCount: v.number(),
|
|
572
|
+
|
|
573
|
+
// Calculated total
|
|
574
|
+
totalEarned: v.number(),
|
|
575
|
+
|
|
576
|
+
createdAt: v.number(),
|
|
577
|
+
updatedAt: v.number(),
|
|
578
|
+
})
|
|
579
|
+
.index("by_workspaceId", ["workspaceId"]),
|
|
580
|
+
|
|
512
581
|
// ============================================
|
|
513
582
|
// FEEDBACK SYSTEM
|
|
514
583
|
// ============================================
|
package/convex/workspaces.ts
CHANGED
|
@@ -27,13 +27,24 @@ export const createMagicLink = mutation({
|
|
|
27
27
|
},
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
// Generate a unique referral code (CLAW-XXXXXX format)
|
|
31
|
+
function generateReferralCode(): string {
|
|
32
|
+
const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
33
|
+
let code = "";
|
|
34
|
+
for (let i = 0; i < 6; i++) {
|
|
35
|
+
code += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
36
|
+
}
|
|
37
|
+
return `CLAW-${code}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
// Verify magic link and create workspace + session
|
|
31
41
|
export const verifyMagicLink = mutation({
|
|
32
42
|
args: {
|
|
33
43
|
token: v.string(),
|
|
34
44
|
fingerprint: v.optional(v.string()),
|
|
45
|
+
referralCode: v.optional(v.string()), // Referral code from signup URL
|
|
35
46
|
},
|
|
36
|
-
handler: async (ctx, { token, fingerprint }) => {
|
|
47
|
+
handler: async (ctx, { token, fingerprint, referralCode }) => {
|
|
37
48
|
const magicLink = await ctx.db
|
|
38
49
|
.query("workspaceMagicLinks")
|
|
39
50
|
.withIndex("by_token", (q) => q.eq("token", token))
|
|
@@ -60,20 +71,90 @@ export const verifyMagicLink = mutation({
|
|
|
60
71
|
.withIndex("by_email", (q) => q.eq("email", magicLink.email))
|
|
61
72
|
.first();
|
|
62
73
|
|
|
74
|
+
let isNewUser = false;
|
|
63
75
|
if (!workspace) {
|
|
64
|
-
|
|
76
|
+
isNewUser = true;
|
|
77
|
+
|
|
78
|
+
// Generate unique referral code for new user
|
|
79
|
+
let newReferralCode: string;
|
|
80
|
+
let attempts = 0;
|
|
81
|
+
do {
|
|
82
|
+
newReferralCode = generateReferralCode();
|
|
83
|
+
const existing = await ctx.db
|
|
84
|
+
.query("workspaces")
|
|
85
|
+
.withIndex("by_referralCode", (q) => q.eq("referralCode", newReferralCode))
|
|
86
|
+
.first();
|
|
87
|
+
if (!existing) break;
|
|
88
|
+
attempts++;
|
|
89
|
+
} while (attempts < 10);
|
|
90
|
+
|
|
91
|
+
// Create new workspace with free tier + referral code
|
|
65
92
|
const workspaceId = await ctx.db.insert("workspaces", {
|
|
66
93
|
email: magicLink.email,
|
|
67
94
|
status: "active",
|
|
68
95
|
tier: "free",
|
|
69
96
|
usageCount: 0,
|
|
70
97
|
usageLimit: 50, // 50 free API calls
|
|
98
|
+
referralCode: newReferralCode!,
|
|
71
99
|
createdAt: Date.now(),
|
|
72
100
|
updatedAt: Date.now(),
|
|
73
101
|
});
|
|
74
102
|
workspace = await ctx.db.get(workspaceId);
|
|
75
103
|
}
|
|
76
104
|
|
|
105
|
+
// Process referral for new users
|
|
106
|
+
if (isNewUser && referralCode) {
|
|
107
|
+
// Find referrer by code
|
|
108
|
+
const referrer = await ctx.db
|
|
109
|
+
.query("workspaces")
|
|
110
|
+
.withIndex("by_referralCode", (q) => q.eq("referralCode", referralCode))
|
|
111
|
+
.first();
|
|
112
|
+
|
|
113
|
+
if (referrer && referrer._id !== workspace!._id) {
|
|
114
|
+
// Update new user's referredBy
|
|
115
|
+
await ctx.db.patch(workspace!._id, {
|
|
116
|
+
referredBy: referrer._id,
|
|
117
|
+
updatedAt: Date.now(),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Get or create referrer's earn progress
|
|
121
|
+
let referrerProgress = await ctx.db
|
|
122
|
+
.query("earnProgress")
|
|
123
|
+
.withIndex("by_workspaceId", (q) => q.eq("workspaceId", referrer._id))
|
|
124
|
+
.first();
|
|
125
|
+
|
|
126
|
+
if (!referrerProgress) {
|
|
127
|
+
await ctx.db.insert("earnProgress", {
|
|
128
|
+
workspaceId: referrer._id,
|
|
129
|
+
firstDirectCall: false,
|
|
130
|
+
apisUsed: [],
|
|
131
|
+
apisUsedComplete: false,
|
|
132
|
+
agentListed: false,
|
|
133
|
+
apiListed: false,
|
|
134
|
+
byokSetup: false,
|
|
135
|
+
githubStarred: false,
|
|
136
|
+
twitterFollowed: false,
|
|
137
|
+
referralCount: 1,
|
|
138
|
+
totalEarned: 10,
|
|
139
|
+
createdAt: Date.now(),
|
|
140
|
+
updatedAt: Date.now(),
|
|
141
|
+
});
|
|
142
|
+
} else {
|
|
143
|
+
await ctx.db.patch(referrerProgress._id, {
|
|
144
|
+
referralCount: referrerProgress.referralCount + 1,
|
|
145
|
+
totalEarned: referrerProgress.totalEarned + 10,
|
|
146
|
+
updatedAt: Date.now(),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Credit referrer with +10 API calls
|
|
151
|
+
await ctx.db.patch(referrer._id, {
|
|
152
|
+
usageLimit: referrer.usageLimit + 10,
|
|
153
|
+
updatedAt: Date.now(),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
77
158
|
// Create agent session
|
|
78
159
|
const sessionToken = generateToken();
|
|
79
160
|
|
|
@@ -92,6 +173,7 @@ export const verifyMagicLink = mutation({
|
|
|
92
173
|
id: workspace!._id,
|
|
93
174
|
email: workspace!.email,
|
|
94
175
|
tier: workspace!.tier,
|
|
176
|
+
referralCode: workspace!.referralCode,
|
|
95
177
|
},
|
|
96
178
|
};
|
|
97
179
|
},
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Client Adapter - Base Interface & Abstract Class
|
|
3
|
+
* Defines the contract for all MCP client adapters
|
|
4
|
+
*/
|
|
5
|
+
import { MCPClient } from '../utils/paths.js';
|
|
6
|
+
import { Platform } from '../utils/os.js';
|
|
7
|
+
import { MCPConfig, ContinueConfig } from '../utils/config.js';
|
|
8
|
+
export interface ConfigResult {
|
|
9
|
+
success: boolean;
|
|
10
|
+
message: string;
|
|
11
|
+
configPath: string;
|
|
12
|
+
backupPath?: string | null;
|
|
13
|
+
isNew?: boolean;
|
|
14
|
+
alreadyConfigured?: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface VerifyResult {
|
|
18
|
+
success: boolean;
|
|
19
|
+
hasConfig: boolean;
|
|
20
|
+
configValid: boolean;
|
|
21
|
+
message: string;
|
|
22
|
+
}
|
|
23
|
+
export interface InstallInfo {
|
|
24
|
+
installed: boolean;
|
|
25
|
+
appPath?: string;
|
|
26
|
+
configExists: boolean;
|
|
27
|
+
configPath: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ConfigureOptions {
|
|
30
|
+
workspaceId?: string;
|
|
31
|
+
serverName?: string;
|
|
32
|
+
force?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* MCP Client Adapter Interface
|
|
36
|
+
*/
|
|
37
|
+
export interface MCPClientAdapter {
|
|
38
|
+
/** Internal client name */
|
|
39
|
+
name: MCPClient | 'custom';
|
|
40
|
+
/** Human-readable display name */
|
|
41
|
+
displayName: string;
|
|
42
|
+
/** Check if the client is installed on the system */
|
|
43
|
+
isInstalled(): Promise<boolean>;
|
|
44
|
+
/** Get the config file path for this client */
|
|
45
|
+
getConfigPath(): string;
|
|
46
|
+
/** Configure APIClaw MCP server for this client */
|
|
47
|
+
configure(options?: ConfigureOptions): Promise<ConfigResult>;
|
|
48
|
+
/** Verify the current configuration */
|
|
49
|
+
verify(): Promise<VerifyResult>;
|
|
50
|
+
/** Get detailed installation info */
|
|
51
|
+
getInstallInfo(): Promise<InstallInfo>;
|
|
52
|
+
/** Remove APIClaw configuration */
|
|
53
|
+
unconfigure(serverName?: string): Promise<ConfigResult>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Abstract base class for MCP client adapters
|
|
57
|
+
* Provides common functionality for all adapters
|
|
58
|
+
*/
|
|
59
|
+
export declare abstract class BaseAdapter implements MCPClientAdapter {
|
|
60
|
+
abstract name: MCPClient | 'custom';
|
|
61
|
+
abstract displayName: string;
|
|
62
|
+
protected os: Platform;
|
|
63
|
+
constructor();
|
|
64
|
+
/**
|
|
65
|
+
* Get the config file path
|
|
66
|
+
*/
|
|
67
|
+
getConfigPath(): string;
|
|
68
|
+
/**
|
|
69
|
+
* Check if config file exists
|
|
70
|
+
*/
|
|
71
|
+
protected configExists(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Get application paths to check for installation
|
|
74
|
+
* Override in subclasses for specific clients
|
|
75
|
+
*/
|
|
76
|
+
protected abstract getAppPaths(): string[];
|
|
77
|
+
/**
|
|
78
|
+
* Check if the client application is installed
|
|
79
|
+
*/
|
|
80
|
+
isInstalled(): Promise<boolean>;
|
|
81
|
+
/**
|
|
82
|
+
* Get detailed installation information
|
|
83
|
+
*/
|
|
84
|
+
getInstallInfo(): Promise<InstallInfo>;
|
|
85
|
+
/**
|
|
86
|
+
* Configure APIClaw MCP server
|
|
87
|
+
*/
|
|
88
|
+
configure(options?: ConfigureOptions): Promise<ConfigResult>;
|
|
89
|
+
/**
|
|
90
|
+
* Merge APIClaw config into existing config
|
|
91
|
+
* Override in subclasses for special formats (e.g., Continue)
|
|
92
|
+
*/
|
|
93
|
+
protected mergeConfig(config: MCPConfig | ContinueConfig, options: {
|
|
94
|
+
workspace?: string;
|
|
95
|
+
serverName?: string;
|
|
96
|
+
force?: boolean;
|
|
97
|
+
}): MCPConfig | ContinueConfig;
|
|
98
|
+
/**
|
|
99
|
+
* Verify the current configuration
|
|
100
|
+
*/
|
|
101
|
+
verify(): Promise<VerifyResult>;
|
|
102
|
+
/**
|
|
103
|
+
* Remove APIClaw configuration
|
|
104
|
+
*/
|
|
105
|
+
unconfigure(serverName?: string): Promise<ConfigResult>;
|
|
106
|
+
/**
|
|
107
|
+
* Remove APIClaw from config
|
|
108
|
+
* Override in subclasses for special formats
|
|
109
|
+
*/
|
|
110
|
+
protected removeFromConfig(config: MCPConfig | ContinueConfig, serverName: string): MCPConfig | ContinueConfig;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"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"}
|