@nordsym/apiclaw 1.0.0 → 1.1.0
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/AGENTS.md +74 -0
- package/HEARTBEAT.md +4 -0
- package/IDENTITY.md +22 -0
- package/README.md +197 -202
- package/SOUL.md +36 -0
- package/STATUS.md +237 -0
- package/TOOLS.md +36 -0
- package/USER.md +17 -0
- package/{backend/convex → convex}/_generated/api.d.ts +6 -6
- package/convex/credits.ts +211 -0
- package/convex/http.ts +490 -0
- package/convex/providers.ts +516 -0
- package/convex/purchases.ts +183 -0
- package/convex/schema.ts +180 -0
- package/convex.json +3 -0
- package/dist/credentials.d.ts +19 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +158 -0
- package/dist/credentials.js.map +1 -0
- package/dist/credits.d.ts +14 -11
- package/dist/credits.d.ts.map +1 -1
- package/dist/credits.js +151 -99
- package/dist/credits.js.map +1 -1
- package/dist/discovery.d.ts +7 -16
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +33 -40
- package/dist/discovery.js.map +1 -1
- package/dist/execute.d.ts +19 -0
- package/dist/execute.d.ts.map +1 -0
- package/dist/execute.js +285 -0
- package/dist/execute.js.map +1 -0
- package/dist/index.js +106 -30
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +6 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +19 -0
- package/dist/proxy.js.map +1 -0
- package/dist/registry/apis.json +95362 -202
- package/dist/registry/apis_expanded.json +100853 -0
- package/dist/stripe.d.ts +68 -0
- package/dist/stripe.d.ts.map +1 -0
- package/dist/stripe.js +196 -0
- package/dist/stripe.js.map +1 -0
- package/dist/test.d.ts +3 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +105 -75
- package/dist/test.js.map +1 -1
- package/dist/types.d.ts +0 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/webhook.d.ts +2 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +90 -0
- package/dist/webhook.js.map +1 -0
- package/landing/DESIGN.md +343 -0
- package/landing/package-lock.json +1190 -40
- package/landing/package.json +5 -2
- package/landing/public/android-chrome-192x192.png +0 -0
- package/landing/public/android-chrome-512x512.png +0 -0
- package/landing/public/apple-touch-icon.png +0 -0
- package/landing/public/demo.gif +0 -0
- package/landing/public/demo.mp4 +0 -0
- package/landing/public/favicon-16x16.png +0 -0
- package/landing/public/favicon-32x32.png +0 -0
- package/landing/public/favicon.ico +0 -0
- package/landing/public/favicon.svg +3 -0
- package/landing/public/icon.svg +47 -0
- package/landing/public/logo-mono.svg +37 -0
- package/landing/public/logo-simple.svg +45 -0
- package/landing/public/logo.svg +84 -0
- package/landing/public/og-image.png +0 -0
- package/landing/public/og-template.html +184 -0
- package/landing/public/site.webmanifest +31 -0
- package/landing/scripts/generate-assets.js +284 -0
- package/landing/scripts/generate-pngs.js +48 -0
- package/landing/scripts/generate-stats.js +42 -0
- package/landing/src/app/admin/page.tsx +348 -0
- package/landing/src/app/api/auth/magic-link/route.ts +73 -0
- package/landing/src/app/api/auth/session/route.ts +38 -0
- package/landing/src/app/api/auth/verify/route.ts +43 -0
- package/landing/src/app/api/og/route.tsx +74 -0
- package/landing/src/app/globals.css +439 -100
- package/landing/src/app/layout.tsx +37 -9
- package/landing/src/app/page.tsx +640 -552
- package/landing/src/app/providers/dashboard/login/page.tsx +176 -0
- package/landing/src/app/providers/dashboard/page.tsx +589 -0
- package/landing/src/app/providers/dashboard/verify/page.tsx +106 -0
- package/landing/src/app/providers/layout.tsx +14 -0
- package/landing/src/app/providers/page.tsx +402 -0
- package/landing/src/app/providers/register/page.tsx +670 -0
- package/landing/src/components/ProviderDashboard.tsx +794 -0
- package/landing/src/hooks/useDashboardData.ts +99 -0
- package/landing/src/lib/apis.json +116054 -0
- package/landing/src/lib/convex-client.ts +106 -0
- package/landing/src/lib/mock-data.ts +285 -0
- package/landing/src/lib/stats.json +6 -0
- package/landing/tailwind.config.ts +12 -11
- package/landing/tsconfig.tsbuildinfo +1 -0
- package/package.json +21 -20
- package/scripts/SYMBOT-FIX.md +238 -0
- package/scripts/demo-simulation.py +177 -0
- package/scripts/expand-more.py +502 -0
- package/scripts/expand-registry.py +434 -0
- package/scripts/history-sanitizer.ts +272 -0
- package/scripts/mass-scrape.py +1308 -0
- package/scripts/sync-and-deploy.sh +36 -0
- package/src/credentials.ts +177 -0
- package/src/credits.ts +190 -122
- package/src/discovery.ts +45 -58
- package/src/execute.ts +350 -0
- package/src/index.ts +113 -31
- package/src/proxy.ts +24 -0
- package/src/registry/apis.json +95362 -202
- package/src/registry/apis_expanded.json +100853 -0
- package/src/stripe.ts +243 -0
- package/src/test.ts +127 -89
- package/src/types.ts +0 -34
- package/src/webhook.ts +107 -0
- package/.github/ISSUE_TEMPLATE/add-api.yml +0 -123
- package/BRIEFING.md +0 -30
- package/backend/convex/apiKeys.ts +0 -75
- package/backend/convex/purchases.ts +0 -74
- package/backend/convex/schema.ts +0 -45
- package/backend/convex/transactions.ts +0 -57
- package/backend/convex/users.ts +0 -94
- package/backend/package-lock.json +0 -521
- package/backend/package.json +0 -15
- package/dist/registry/parse_apis.py +0 -146
- package/dist/revenuecat.d.ts +0 -61
- package/dist/revenuecat.d.ts.map +0 -1
- package/dist/revenuecat.js +0 -166
- package/dist/revenuecat.js.map +0 -1
- package/dist/webhooks/revenuecat.d.ts +0 -48
- package/dist/webhooks/revenuecat.d.ts.map +0 -1
- package/dist/webhooks/revenuecat.js +0 -119
- package/dist/webhooks/revenuecat.js.map +0 -1
- package/docs/revenuecat-setup.md +0 -89
- package/landing/src/app/api/keys/route.ts +0 -71
- package/landing/src/app/api/log/route.ts +0 -37
- package/landing/src/app/api/stats/route.ts +0 -37
- package/landing/src/app/page.tsx.bak +0 -567
- package/landing/src/components/AddKeyModal.tsx +0 -159
- package/newsletter-template.html +0 -71
- package/outreach/OUTREACH-SYSTEM.md +0 -211
- package/outreach/email-template.html +0 -179
- package/outreach/targets.md +0 -133
- package/src/registry/parse_apis.py +0 -146
- package/src/revenuecat.ts +0 -239
- package/src/webhooks/revenuecat.ts +0 -187
- /package/{backend/convex → convex}/README.md +0 -0
- /package/{backend/convex → convex}/_generated/api.js +0 -0
- /package/{backend/convex → convex}/_generated/dataModel.d.ts +0 -0
- /package/{backend/convex → convex}/_generated/server.d.ts +0 -0
- /package/{backend/convex → convex}/_generated/server.js +0 -0
- /package/{backend/convex → convex}/tsconfig.json +0 -0
package/src/stripe.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// Stripe integration for APIClaw credit purchases
|
|
2
|
+
import Stripe from 'stripe';
|
|
3
|
+
import { config } from 'dotenv';
|
|
4
|
+
|
|
5
|
+
// Load environment
|
|
6
|
+
config({ path: '.env.local' });
|
|
7
|
+
|
|
8
|
+
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
|
|
9
|
+
|
|
10
|
+
if (!stripeSecretKey) {
|
|
11
|
+
console.warn('STRIPE_SECRET_KEY not set - Stripe features disabled');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const stripe = stripeSecretKey ? new Stripe(stripeSecretKey) : null;
|
|
15
|
+
|
|
16
|
+
// Credit packages
|
|
17
|
+
export const CREDIT_PACKAGES = {
|
|
18
|
+
starter: {
|
|
19
|
+
id: 'starter',
|
|
20
|
+
name: 'Starter Pack',
|
|
21
|
+
amountUsd: 10,
|
|
22
|
+
credits: 100,
|
|
23
|
+
bonus: 0,
|
|
24
|
+
description: '$10 → 100 credits',
|
|
25
|
+
},
|
|
26
|
+
growth: {
|
|
27
|
+
id: 'growth',
|
|
28
|
+
name: 'Growth Pack',
|
|
29
|
+
amountUsd: 50,
|
|
30
|
+
credits: 550,
|
|
31
|
+
bonus: 50,
|
|
32
|
+
description: '$50 → 550 credits (10% bonus)',
|
|
33
|
+
},
|
|
34
|
+
scale: {
|
|
35
|
+
id: 'scale',
|
|
36
|
+
name: 'Scale Pack',
|
|
37
|
+
amountUsd: 100,
|
|
38
|
+
credits: 1200,
|
|
39
|
+
bonus: 200,
|
|
40
|
+
description: '$100 → 1,200 credits (20% bonus)',
|
|
41
|
+
},
|
|
42
|
+
} as const;
|
|
43
|
+
|
|
44
|
+
export type PackageType = keyof typeof CREDIT_PACKAGES;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create a Stripe Checkout Session for credit purchase
|
|
48
|
+
*/
|
|
49
|
+
export async function createCheckoutSession(
|
|
50
|
+
agentId: string,
|
|
51
|
+
packageType: PackageType,
|
|
52
|
+
successUrl: string,
|
|
53
|
+
cancelUrl: string
|
|
54
|
+
): Promise<{ sessionId: string; url: string } | { error: string }> {
|
|
55
|
+
if (!stripe) {
|
|
56
|
+
return { error: 'Stripe not configured' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const pkg = CREDIT_PACKAGES[packageType];
|
|
60
|
+
if (!pkg) {
|
|
61
|
+
return { error: `Invalid package: ${packageType}` };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const session = await stripe.checkout.sessions.create({
|
|
66
|
+
mode: 'payment',
|
|
67
|
+
payment_method_types: ['card'],
|
|
68
|
+
line_items: [
|
|
69
|
+
{
|
|
70
|
+
price_data: {
|
|
71
|
+
currency: 'usd',
|
|
72
|
+
product_data: {
|
|
73
|
+
name: `APIClaw ${pkg.name}`,
|
|
74
|
+
description: pkg.description,
|
|
75
|
+
metadata: {
|
|
76
|
+
packageType,
|
|
77
|
+
credits: pkg.credits.toString(),
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
unit_amount: pkg.amountUsd * 100, // Stripe uses cents
|
|
81
|
+
},
|
|
82
|
+
quantity: 1,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
metadata: {
|
|
86
|
+
agentId,
|
|
87
|
+
packageType,
|
|
88
|
+
credits: pkg.credits.toString(),
|
|
89
|
+
},
|
|
90
|
+
success_url: successUrl,
|
|
91
|
+
cancel_url: cancelUrl,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
sessionId: session.id,
|
|
96
|
+
url: session.url!,
|
|
97
|
+
};
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('Stripe checkout error:', error);
|
|
100
|
+
return {
|
|
101
|
+
error: error instanceof Error ? error.message : 'Checkout failed',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Create a Payment Intent for programmatic payment (agent-to-agent)
|
|
108
|
+
*/
|
|
109
|
+
export async function createPaymentIntent(
|
|
110
|
+
agentId: string,
|
|
111
|
+
packageType: PackageType
|
|
112
|
+
): Promise<
|
|
113
|
+
| { clientSecret: string; paymentIntentId: string; amount: number }
|
|
114
|
+
| { error: string }
|
|
115
|
+
> {
|
|
116
|
+
if (!stripe) {
|
|
117
|
+
return { error: 'Stripe not configured' };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const pkg = CREDIT_PACKAGES[packageType];
|
|
121
|
+
if (!pkg) {
|
|
122
|
+
return { error: `Invalid package: ${packageType}` };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const paymentIntent = await stripe.paymentIntents.create({
|
|
127
|
+
amount: pkg.amountUsd * 100,
|
|
128
|
+
currency: 'usd',
|
|
129
|
+
metadata: {
|
|
130
|
+
agentId,
|
|
131
|
+
packageType,
|
|
132
|
+
credits: pkg.credits.toString(),
|
|
133
|
+
},
|
|
134
|
+
automatic_payment_methods: {
|
|
135
|
+
enabled: true,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
clientSecret: paymentIntent.client_secret!,
|
|
141
|
+
paymentIntentId: paymentIntent.id,
|
|
142
|
+
amount: pkg.amountUsd,
|
|
143
|
+
};
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error('Stripe payment intent error:', error);
|
|
146
|
+
return {
|
|
147
|
+
error: error instanceof Error ? error.message : 'Payment intent failed',
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Verify Stripe webhook signature
|
|
154
|
+
*/
|
|
155
|
+
export function verifyWebhookSignature(
|
|
156
|
+
payload: string | Buffer,
|
|
157
|
+
signature: string
|
|
158
|
+
): Stripe.Event | null {
|
|
159
|
+
if (!stripe) return null;
|
|
160
|
+
|
|
161
|
+
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
|
|
162
|
+
if (!webhookSecret) {
|
|
163
|
+
console.error('STRIPE_WEBHOOK_SECRET not set');
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
return stripe.webhooks.constructEvent(payload, signature, webhookSecret);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error('Webhook signature verification failed:', error);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Process webhook event and return credit grant info
|
|
177
|
+
*/
|
|
178
|
+
export interface CreditGrant {
|
|
179
|
+
agentId: string;
|
|
180
|
+
packageType: string;
|
|
181
|
+
credits: number;
|
|
182
|
+
amountUsd: number;
|
|
183
|
+
stripeSessionId?: string;
|
|
184
|
+
stripePaymentIntentId?: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function processWebhookEvent(event: Stripe.Event): CreditGrant | null {
|
|
188
|
+
switch (event.type) {
|
|
189
|
+
case 'checkout.session.completed': {
|
|
190
|
+
const session = event.data.object as Stripe.Checkout.Session;
|
|
191
|
+
if (session.payment_status !== 'paid') return null;
|
|
192
|
+
|
|
193
|
+
const metadata = session.metadata || {};
|
|
194
|
+
const agentId = metadata.agentId;
|
|
195
|
+
const packageType = metadata.packageType as PackageType;
|
|
196
|
+
const credits = parseInt(metadata.credits || '0', 10);
|
|
197
|
+
|
|
198
|
+
if (!agentId || !packageType || !credits) {
|
|
199
|
+
console.error('Missing metadata in checkout session:', metadata);
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
agentId,
|
|
205
|
+
packageType,
|
|
206
|
+
credits,
|
|
207
|
+
amountUsd: (session.amount_total || 0) / 100,
|
|
208
|
+
stripeSessionId: session.id,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
case 'payment_intent.succeeded': {
|
|
213
|
+
const paymentIntent = event.data.object as Stripe.PaymentIntent;
|
|
214
|
+
const metadata = paymentIntent.metadata || {};
|
|
215
|
+
const agentId = metadata.agentId;
|
|
216
|
+
const packageType = metadata.packageType as PackageType;
|
|
217
|
+
const credits = parseInt(metadata.credits || '0', 10);
|
|
218
|
+
|
|
219
|
+
if (!agentId || !packageType || !credits) {
|
|
220
|
+
console.error('Missing metadata in payment intent:', metadata);
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
agentId,
|
|
226
|
+
packageType,
|
|
227
|
+
credits,
|
|
228
|
+
amountUsd: paymentIntent.amount / 100,
|
|
229
|
+
stripePaymentIntentId: paymentIntent.id,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
default:
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get Stripe instance (for advanced usage)
|
|
240
|
+
*/
|
|
241
|
+
export function getStripe(): Stripe | null {
|
|
242
|
+
return stripe;
|
|
243
|
+
}
|
package/src/test.ts
CHANGED
|
@@ -1,97 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
3
|
+
* APIClaw End-to-End Test
|
|
4
|
+
* Tests the full flow: credits → purchase → real credentials
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
console.log(`
|
|
23
|
-
console.log(`Results: ${smsResults.length}`);
|
|
24
|
-
smsResults.slice(0, 5).forEach(r => {
|
|
25
|
-
console.log(` - ${r.api.name} (score: ${r.relevance_score})`);
|
|
26
|
-
console.log(` ${r.api.description.slice(0, 60)}...`);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const weatherResults = discoverAPIs('weather');
|
|
30
|
-
console.log(`\nQuery: "weather"`);
|
|
31
|
-
console.log(`Results: ${weatherResults.length}`);
|
|
32
|
-
weatherResults.slice(0, 5).forEach(r => {
|
|
33
|
-
console.log(` - ${r.api.name} (score: ${r.relevance_score})`);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const paymentResults = discoverAPIs('payment');
|
|
37
|
-
console.log(`\nQuery: "payment"`);
|
|
38
|
-
console.log(`Results: ${paymentResults.length}`);
|
|
39
|
-
paymentResults.slice(0, 5).forEach(r => {
|
|
40
|
-
console.log(` - ${r.api.name} (score: ${r.relevance_score})`);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// Test 3: Get API Details
|
|
44
|
-
console.log('\n📋 Test 3: API Details');
|
|
45
|
-
console.log('-'.repeat(40));
|
|
46
|
-
|
|
47
|
-
const stripe = getAPIDetails('stripe');
|
|
48
|
-
if (stripe) {
|
|
49
|
-
console.log(`\n${stripe.name}:`);
|
|
50
|
-
console.log(` Category: ${stripe.category}`);
|
|
51
|
-
console.log(` Auth: ${stripe.auth}`);
|
|
52
|
-
console.log(` HTTPS: ${stripe.https}`);
|
|
53
|
-
console.log(` Link: ${stripe.link}`);
|
|
7
|
+
import { config } from 'dotenv';
|
|
8
|
+
config({ path: '.env.local' });
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
getAgentCredits,
|
|
12
|
+
addCredits,
|
|
13
|
+
purchaseAPIAccess,
|
|
14
|
+
getBalanceSummary,
|
|
15
|
+
getProvidersWithRealCredentials,
|
|
16
|
+
} from './credits.js';
|
|
17
|
+
import { hasRealCredentials } from './credentials.js';
|
|
18
|
+
import { CREDIT_PACKAGES, createCheckoutSession, createPaymentIntent } from './stripe.js';
|
|
19
|
+
|
|
20
|
+
const TEST_AGENT_ID = 'test_agent_001';
|
|
21
|
+
|
|
22
|
+
function log(msg: string) {
|
|
23
|
+
console.log(`\n${'='.repeat(60)}\n${msg}\n${'='.repeat(60)}`);
|
|
54
24
|
}
|
|
55
25
|
|
|
56
|
-
|
|
57
|
-
console.log(
|
|
58
|
-
console.log(
|
|
59
|
-
|
|
60
|
-
const categories = getCategories();
|
|
61
|
-
console.log(`Total categories: ${categories.length}`);
|
|
62
|
-
console.log(`Sample: ${categories.slice(0, 10).join(', ')}...`);
|
|
63
|
-
|
|
64
|
-
// Test 5: Credits System
|
|
65
|
-
console.log('\n💰 Test 5: Credit System');
|
|
66
|
-
console.log('-'.repeat(40));
|
|
67
|
-
|
|
68
|
-
const testAgent = 'agent_test_123';
|
|
69
|
-
|
|
70
|
-
// Check initial balance
|
|
71
|
-
let balance = getBalanceSummary(testAgent);
|
|
72
|
-
console.log(`Initial balance: $${balance.credits.balance_usd}`);
|
|
73
|
-
|
|
74
|
-
// Add credits
|
|
75
|
-
addCredits(testAgent, 50);
|
|
76
|
-
balance = getBalanceSummary(testAgent);
|
|
77
|
-
console.log(`After adding $50: $${balance.credits.balance_usd}`);
|
|
78
|
-
|
|
79
|
-
// Purchase API access
|
|
80
|
-
console.log('\n🛒 Purchasing stripe access for $10...');
|
|
81
|
-
const purchase = purchaseAPIAccess(testAgent, 'stripe', 10);
|
|
82
|
-
if (purchase.success) {
|
|
83
|
-
console.log(` ✅ Purchase successful!`);
|
|
84
|
-
console.log(` Purchase ID: ${purchase.purchase!.id}`);
|
|
85
|
-
console.log(` Credits received: ${purchase.purchase!.credits_purchased}`);
|
|
86
|
-
} else {
|
|
87
|
-
console.log(` ❌ Purchase failed: ${purchase.error}`);
|
|
26
|
+
function logResult(label: string, data: unknown) {
|
|
27
|
+
console.log(`\n[${label}]`);
|
|
28
|
+
console.log(JSON.stringify(data, null, 2));
|
|
88
29
|
}
|
|
89
30
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
31
|
+
async function runTests() {
|
|
32
|
+
console.log('\n🧪 APIClaw Connected Infrastructure Test\n');
|
|
33
|
+
|
|
34
|
+
// Test 1: Check real credentials availability
|
|
35
|
+
log('TEST 1: Real Credentials Check');
|
|
36
|
+
const realCredProviders = getProvidersWithRealCredentials();
|
|
37
|
+
console.log('Providers with real credentials:', realCredProviders);
|
|
38
|
+
console.log('46elks real:', hasRealCredentials('46elks'));
|
|
39
|
+
console.log('Twilio real:', hasRealCredentials('twilio'));
|
|
40
|
+
|
|
41
|
+
// Test 2: Credit packages
|
|
42
|
+
log('TEST 2: Credit Packages');
|
|
43
|
+
logResult('Packages', CREDIT_PACKAGES);
|
|
44
|
+
|
|
45
|
+
// Test 3: Add credits to agent
|
|
46
|
+
log('TEST 3: Add Credits');
|
|
47
|
+
const initialCredits = getAgentCredits(TEST_AGENT_ID);
|
|
48
|
+
logResult('Initial balance', initialCredits);
|
|
49
|
+
|
|
50
|
+
const afterAdd = addCredits(TEST_AGENT_ID, 50);
|
|
51
|
+
logResult('After adding $50', afterAdd);
|
|
52
|
+
|
|
53
|
+
// Test 4: Check balance summary
|
|
54
|
+
log('TEST 4: Balance Summary');
|
|
55
|
+
const summary = getBalanceSummary(TEST_AGENT_ID);
|
|
56
|
+
logResult('Summary', summary);
|
|
57
|
+
|
|
58
|
+
// Test 5: Purchase 46elks access
|
|
59
|
+
log('TEST 5: Purchase 46elks Access');
|
|
60
|
+
const purchase46elks = purchaseAPIAccess(TEST_AGENT_ID, '46elks', 10);
|
|
61
|
+
logResult('46elks Purchase Result', purchase46elks);
|
|
62
|
+
|
|
63
|
+
if (purchase46elks.success && purchase46elks.purchase) {
|
|
64
|
+
console.log('\n🔑 CREDENTIALS RECEIVED:');
|
|
65
|
+
console.log(' Type:', purchase46elks.purchase.credentials?.type);
|
|
66
|
+
if (purchase46elks.purchase.credentials?.type === 'basic') {
|
|
67
|
+
console.log(' Username:', purchase46elks.purchase.credentials?.username);
|
|
68
|
+
console.log(' Password:', purchase46elks.purchase.credentials?.password?.slice(0, 8) + '...');
|
|
69
|
+
}
|
|
70
|
+
console.log(' Real credentials:', hasRealCredentials('46elks'));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Test 6: Purchase Twilio access
|
|
74
|
+
log('TEST 6: Purchase Twilio Access');
|
|
75
|
+
const purchaseTwilio = purchaseAPIAccess(TEST_AGENT_ID, 'twilio', 10);
|
|
76
|
+
logResult('Twilio Purchase Result', purchaseTwilio);
|
|
77
|
+
|
|
78
|
+
if (purchaseTwilio.success && purchaseTwilio.purchase) {
|
|
79
|
+
console.log('\n🔑 CREDENTIALS RECEIVED:');
|
|
80
|
+
console.log(' Type:', purchaseTwilio.purchase.credentials?.type);
|
|
81
|
+
if (purchaseTwilio.purchase.credentials?.type === 'basic') {
|
|
82
|
+
console.log(' Account SID:', purchaseTwilio.purchase.credentials?.username);
|
|
83
|
+
console.log(' Auth Token:', purchaseTwilio.purchase.credentials?.password?.slice(0, 8) + '...');
|
|
84
|
+
}
|
|
85
|
+
console.log(' Real credentials:', hasRealCredentials('twilio'));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Test 7: Final balance
|
|
89
|
+
log('TEST 7: Final Balance');
|
|
90
|
+
const finalSummary = getBalanceSummary(TEST_AGENT_ID);
|
|
91
|
+
logResult('Final Summary', {
|
|
92
|
+
balance: finalSummary.credits.balance_usd,
|
|
93
|
+
active_purchases: finalSummary.active_purchases.length,
|
|
94
|
+
total_spent: finalSummary.total_spent_usd,
|
|
95
|
+
real_credential_providers: finalSummary.real_credentials_available,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Test 8: Stripe integration (if configured)
|
|
99
|
+
log('TEST 8: Stripe Integration');
|
|
100
|
+
if (process.env.STRIPE_SECRET_KEY) {
|
|
101
|
+
console.log('Stripe is configured!');
|
|
102
|
+
|
|
103
|
+
// Test creating a payment intent
|
|
104
|
+
const paymentIntent = await createPaymentIntent('test_agent_stripe', 'starter');
|
|
105
|
+
if ('error' in paymentIntent) {
|
|
106
|
+
console.log('Payment Intent Error:', paymentIntent.error);
|
|
107
|
+
} else {
|
|
108
|
+
console.log('Payment Intent Created:');
|
|
109
|
+
console.log(' ID:', paymentIntent.paymentIntentId);
|
|
110
|
+
console.log(' Amount: $' + paymentIntent.amount);
|
|
111
|
+
console.log(' Client Secret:', paymentIntent.clientSecret.slice(0, 20) + '...');
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
console.log('Stripe not configured - skipping');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Test 9: Insufficient balance
|
|
118
|
+
log('TEST 9: Insufficient Balance Check');
|
|
119
|
+
const insufficientPurchase = purchaseAPIAccess(TEST_AGENT_ID, '46elks', 1000);
|
|
120
|
+
logResult('Should fail', insufficientPurchase);
|
|
121
|
+
|
|
122
|
+
// Summary
|
|
123
|
+
log('TEST SUMMARY');
|
|
124
|
+
console.log(`
|
|
125
|
+
✅ Real credentials available for: ${realCredProviders.join(', ') || 'none'}
|
|
126
|
+
✅ Agent can add credits
|
|
127
|
+
✅ Agent can purchase API access
|
|
128
|
+
✅ Real 46elks/Twilio credentials returned when available
|
|
129
|
+
✅ Insufficient balance check works
|
|
130
|
+
${process.env.STRIPE_SECRET_KEY ? '✅ Stripe integration working' : '⚠️ Stripe not configured'}
|
|
131
|
+
`);
|
|
132
|
+
}
|
|
95
133
|
|
|
96
|
-
|
|
97
|
-
console.
|
|
134
|
+
// Run tests
|
|
135
|
+
runTests().catch(console.error);
|
package/src/types.ts
CHANGED
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
// Core types for APIvault
|
|
2
2
|
|
|
3
|
-
// Simplified API from public-apis import
|
|
4
|
-
export interface SimpleAPI {
|
|
5
|
-
id: string;
|
|
6
|
-
name: string;
|
|
7
|
-
description: string;
|
|
8
|
-
category: string;
|
|
9
|
-
auth: string;
|
|
10
|
-
https: boolean;
|
|
11
|
-
cors: string;
|
|
12
|
-
link: string;
|
|
13
|
-
pricing: string;
|
|
14
|
-
keywords: string[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Full API provider (for detailed/curated APIs)
|
|
18
3
|
export interface APIProvider {
|
|
19
4
|
id: string;
|
|
20
5
|
name: string;
|
|
@@ -89,22 +74,3 @@ export interface UsageRecord {
|
|
|
89
74
|
cost_incurred_usd: number;
|
|
90
75
|
last_used_at: string;
|
|
91
76
|
}
|
|
92
|
-
|
|
93
|
-
// Subscription types
|
|
94
|
-
export interface UserSubscription {
|
|
95
|
-
user_id: string;
|
|
96
|
-
tier: 'free' | 'pro';
|
|
97
|
-
status: 'active' | 'cancelled' | 'expired' | 'paused' | 'billing_issue';
|
|
98
|
-
product_id: string | null;
|
|
99
|
-
expires_at: string | null;
|
|
100
|
-
fee_percentage: number; // 0.05 for free, 0.02 for pro
|
|
101
|
-
updated_at: string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export interface TransactionFee {
|
|
105
|
-
amount_usd: number;
|
|
106
|
-
fee_percentage: number;
|
|
107
|
-
fee_amount_usd: number;
|
|
108
|
-
net_amount_usd: number;
|
|
109
|
-
tier: 'free' | 'pro';
|
|
110
|
-
}
|
package/src/webhook.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Stripe webhook handler for APIClaw
|
|
2
|
+
// Run as: npx ts-node src/webhook.ts
|
|
3
|
+
|
|
4
|
+
import { serve } from '@hono/node-server';
|
|
5
|
+
import { Hono } from 'hono';
|
|
6
|
+
import { verifyWebhookSignature, processWebhookEvent, CREDIT_PACKAGES } from './stripe.js';
|
|
7
|
+
import { addCredits } from './credits.js';
|
|
8
|
+
import { config } from 'dotenv';
|
|
9
|
+
|
|
10
|
+
// Load environment
|
|
11
|
+
config({ path: '.env.local' });
|
|
12
|
+
|
|
13
|
+
const app = new Hono();
|
|
14
|
+
|
|
15
|
+
// Health check
|
|
16
|
+
app.get('/', (c) => {
|
|
17
|
+
return c.json({
|
|
18
|
+
service: 'APIClaw Webhook',
|
|
19
|
+
status: 'ok',
|
|
20
|
+
packages: Object.values(CREDIT_PACKAGES).map(p => ({
|
|
21
|
+
id: p.id,
|
|
22
|
+
price: `$${p.amountUsd}`,
|
|
23
|
+
credits: p.credits,
|
|
24
|
+
})),
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Stripe webhook endpoint
|
|
29
|
+
app.post('/webhook/stripe', async (c) => {
|
|
30
|
+
const signature = c.req.header('stripe-signature');
|
|
31
|
+
if (!signature) {
|
|
32
|
+
return c.json({ error: 'Missing stripe-signature header' }, 400);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Get raw body
|
|
36
|
+
const rawBody = await c.req.text();
|
|
37
|
+
|
|
38
|
+
// Verify webhook signature
|
|
39
|
+
const event = verifyWebhookSignature(rawBody, signature);
|
|
40
|
+
if (!event) {
|
|
41
|
+
return c.json({ error: 'Invalid webhook signature' }, 400);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(`[Webhook] Received: ${event.type}`);
|
|
45
|
+
|
|
46
|
+
// Process the event
|
|
47
|
+
const creditGrant = processWebhookEvent(event);
|
|
48
|
+
if (!creditGrant) {
|
|
49
|
+
// Not a credit-related event, acknowledge and move on
|
|
50
|
+
return c.json({ received: true, processed: false });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Grant credits to the agent
|
|
54
|
+
try {
|
|
55
|
+
const credits = addCredits(creditGrant.agentId, creditGrant.credits);
|
|
56
|
+
|
|
57
|
+
console.log(`[Webhook] Granted ${creditGrant.credits} credits to ${creditGrant.agentId}`);
|
|
58
|
+
console.log(`[Webhook] New balance: $${credits.balance_usd.toFixed(2)}`);
|
|
59
|
+
|
|
60
|
+
return c.json({
|
|
61
|
+
received: true,
|
|
62
|
+
processed: true,
|
|
63
|
+
grant: {
|
|
64
|
+
agentId: creditGrant.agentId,
|
|
65
|
+
credits: creditGrant.credits,
|
|
66
|
+
newBalance: credits.balance_usd,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('[Webhook] Error granting credits:', error);
|
|
71
|
+
return c.json({ error: 'Failed to grant credits' }, 500);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Manual credit grant endpoint (for testing)
|
|
76
|
+
app.post('/admin/grant-credits', async (c) => {
|
|
77
|
+
const body = await c.req.json();
|
|
78
|
+
const { agentId, credits, adminKey } = body;
|
|
79
|
+
|
|
80
|
+
// Simple admin key check (use proper auth in production)
|
|
81
|
+
if (adminKey !== process.env.ADMIN_KEY && adminKey !== 'test-admin-key') {
|
|
82
|
+
return c.json({ error: 'Invalid admin key' }, 403);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!agentId || typeof credits !== 'number') {
|
|
86
|
+
return c.json({ error: 'Missing agentId or credits' }, 400);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = addCredits(agentId, credits);
|
|
90
|
+
return c.json({
|
|
91
|
+
success: true,
|
|
92
|
+
agentId,
|
|
93
|
+
creditsGranted: credits,
|
|
94
|
+
newBalance: result.balance_usd,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Start server
|
|
99
|
+
const port = parseInt(process.env.WEBHOOK_PORT || '3001', 10);
|
|
100
|
+
|
|
101
|
+
console.log(`[APIClaw] Webhook server starting on port ${port}`);
|
|
102
|
+
console.log(`[APIClaw] Endpoints:`);
|
|
103
|
+
console.log(` GET / - Health check`);
|
|
104
|
+
console.log(` POST /webhook/stripe - Stripe webhook`);
|
|
105
|
+
console.log(` POST /admin/grant-credits - Manual credit grant`);
|
|
106
|
+
|
|
107
|
+
serve({ fetch: app.fetch, port });
|