@nordsym/apiclaw 1.0.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.
Files changed (79) hide show
  1. package/.github/ISSUE_TEMPLATE/add-api.yml +123 -0
  2. package/BRIEFING.md +30 -0
  3. package/CONCEPT.md +494 -0
  4. package/README.md +272 -0
  5. package/backend/convex/README.md +90 -0
  6. package/backend/convex/_generated/api.d.ts +55 -0
  7. package/backend/convex/_generated/api.js +23 -0
  8. package/backend/convex/_generated/dataModel.d.ts +60 -0
  9. package/backend/convex/_generated/server.d.ts +143 -0
  10. package/backend/convex/_generated/server.js +93 -0
  11. package/backend/convex/apiKeys.ts +75 -0
  12. package/backend/convex/purchases.ts +74 -0
  13. package/backend/convex/schema.ts +45 -0
  14. package/backend/convex/transactions.ts +57 -0
  15. package/backend/convex/tsconfig.json +25 -0
  16. package/backend/convex/users.ts +94 -0
  17. package/backend/package-lock.json +521 -0
  18. package/backend/package.json +15 -0
  19. package/dist/credits.d.ts +54 -0
  20. package/dist/credits.d.ts.map +1 -0
  21. package/dist/credits.js +209 -0
  22. package/dist/credits.js.map +1 -0
  23. package/dist/discovery.d.ts +37 -0
  24. package/dist/discovery.d.ts.map +1 -0
  25. package/dist/discovery.js +109 -0
  26. package/dist/discovery.js.map +1 -0
  27. package/dist/index.d.ts +13 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +355 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/registry/apis.json +20894 -0
  32. package/dist/registry/parse_apis.py +146 -0
  33. package/dist/revenuecat.d.ts +61 -0
  34. package/dist/revenuecat.d.ts.map +1 -0
  35. package/dist/revenuecat.js +166 -0
  36. package/dist/revenuecat.js.map +1 -0
  37. package/dist/test.d.ts +6 -0
  38. package/dist/test.d.ts.map +1 -0
  39. package/dist/test.js +81 -0
  40. package/dist/test.js.map +1 -0
  41. package/dist/types.d.ts +96 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +3 -0
  44. package/dist/types.js.map +1 -0
  45. package/dist/webhooks/revenuecat.d.ts +48 -0
  46. package/dist/webhooks/revenuecat.d.ts.map +1 -0
  47. package/dist/webhooks/revenuecat.js +119 -0
  48. package/dist/webhooks/revenuecat.js.map +1 -0
  49. package/docs/revenuecat-setup.md +89 -0
  50. package/landing/next-env.d.ts +5 -0
  51. package/landing/next.config.mjs +6 -0
  52. package/landing/package-lock.json +1666 -0
  53. package/landing/package.json +27 -0
  54. package/landing/postcss.config.js +6 -0
  55. package/landing/src/app/api/keys/route.ts +71 -0
  56. package/landing/src/app/api/log/route.ts +37 -0
  57. package/landing/src/app/api/stats/route.ts +37 -0
  58. package/landing/src/app/globals.css +261 -0
  59. package/landing/src/app/layout.tsx +37 -0
  60. package/landing/src/app/page.tsx +753 -0
  61. package/landing/src/app/page.tsx.bak +567 -0
  62. package/landing/src/components/AddKeyModal.tsx +159 -0
  63. package/landing/tailwind.config.ts +34 -0
  64. package/landing/tsconfig.json +20 -0
  65. package/newsletter-template.html +71 -0
  66. package/outreach/OUTREACH-SYSTEM.md +211 -0
  67. package/outreach/email-template.html +179 -0
  68. package/outreach/targets.md +133 -0
  69. package/package.json +39 -0
  70. package/src/credits.ts +261 -0
  71. package/src/discovery.ts +147 -0
  72. package/src/index.ts +396 -0
  73. package/src/registry/apis.json +20894 -0
  74. package/src/registry/parse_apis.py +146 -0
  75. package/src/revenuecat.ts +239 -0
  76. package/src/test.ts +97 -0
  77. package/src/types.ts +110 -0
  78. package/src/webhooks/revenuecat.ts +187 -0
  79. package/tsconfig.json +20 -0
@@ -0,0 +1,133 @@
1
+ # APIClaw Outreach Targets
2
+
3
+ > API providers att kontakta för listing i APIClaw registry
4
+ > Senast uppdaterad: 2026-06-17
5
+
6
+ ## 🇸🇪 Sverige / Norden — PRIORITET 1
7
+
8
+ ### SMS & Communications
9
+ | Företag | Kategori | API Docs | Kontakt | Status |
10
+ |---------|----------|----------|---------|--------|
11
+ | **46elks** | SMS, Voice | https://46elks.com/docs | hello@46elks.com | ⬜ Pending |
12
+ | **Sinch** | SMS, Voice, Video | https://developers.sinch.com/ | - | ⬜ Pending |
13
+ | **Link Mobility** | SMS, RCS | https://linkmobility.com/developers | - | ⬜ Pending |
14
+
15
+ ### Payments & Banking
16
+ | Företag | Kategori | API Docs | Kontakt | Status |
17
+ |---------|----------|----------|---------|--------|
18
+ | **Klarna** | BNPL, Payments | https://docs.klarna.com/ | - | ⬜ Pending |
19
+ | **Trustly** | Bank payments | https://developers.trustly.com/ | - | ⬜ Pending |
20
+ | **Tink** | Open Banking | https://docs.tink.com/ | - | ⬜ Pending |
21
+ | **Open Payments** | PSD2, Banking | https://docs.openpayments.io/ | - | ⬜ Pending |
22
+ | **Neonomics** | Open Banking | https://developer.neonomics.io/ | - | ⬜ Pending |
23
+ | **Nordea API Market** | Banking | https://developer.nordeaopenbanking.com/ | - | ⬜ Pending |
24
+ | **Yapily** | Open Banking | https://docs.yapily.com/ | - | ⬜ Pending |
25
+
26
+ ### Identity & Verification
27
+ | Företag | Kategori | API Docs | Kontakt | Status |
28
+ |---------|----------|----------|---------|--------|
29
+ | **BankID** | eID | https://www.bankid.com/utvecklare | - | ⬜ Pending |
30
+ | **Freja eID** | eID | https://org.frejaeid.com/developers | - | ⬜ Pending |
31
+ | **BehavioSec** | Behavioral biometrics | - | - | ⬜ Pending |
32
+
33
+ ---
34
+
35
+ ## 🌍 Internationella — PRIORITET 2
36
+
37
+ ### SMS & Communications
38
+ | Företag | Kategori | API Docs | Kontakt | Status |
39
+ |---------|----------|----------|---------|--------|
40
+ | **Twilio** | SMS, Voice, Video | https://www.twilio.com/docs | - | ⬜ Pending |
41
+ | **Plivo** | SMS, Voice | https://www.plivo.com/docs/ | - | ⬜ Pending |
42
+ | **Infobip** | Omnichannel | https://www.infobip.com/docs | - | ⬜ Pending |
43
+ | **MessageBird (Bird)** | SMS, Email | https://developers.messagebird.com/ | - | ⬜ Pending |
44
+ | **Telnyx** | SMS, Voice | https://developers.telnyx.com/ | - | ⬜ Pending |
45
+ | **Vonage** | Communications | https://developer.vonage.com/ | - | ⬜ Pending |
46
+
47
+ ### Email
48
+ | Företag | Kategori | API Docs | Kontakt | Status |
49
+ |---------|----------|----------|---------|--------|
50
+ | **SendGrid** | Transactional email | https://docs.sendgrid.com/ | - | ⬜ Pending |
51
+ | **Mailgun** | Transactional email | https://documentation.mailgun.com/ | - | ⬜ Pending |
52
+ | **Postmark** | Transactional email | https://postmarkapp.com/developer | - | ⬜ Pending |
53
+ | **Mailjet** | Email | https://dev.mailjet.com/ | - | ⬜ Pending |
54
+ | **Resend** | Dev-first email | https://resend.com/docs | - | ⬜ Pending |
55
+ | **Amazon SES** | Email | https://docs.aws.amazon.com/ses/ | - | ⬜ Pending |
56
+
57
+ ### Payments
58
+ | Företag | Kategori | API Docs | Kontakt | Status |
59
+ |---------|----------|----------|---------|--------|
60
+ | **Stripe** | Payments | https://stripe.com/docs/api | - | ⬜ Pending |
61
+ | **PayPal** | Payments | https://developer.paypal.com/ | - | ⬜ Pending |
62
+ | **Adyen** | Payments | https://docs.adyen.com/ | - | ⬜ Pending |
63
+ | **Square** | Payments, POS | https://developer.squareup.com/ | - | ⬜ Pending |
64
+ | **Paddle** | SaaS billing | https://developer.paddle.com/ | - | ⬜ Pending |
65
+ | **Lemon Squeezy** | SaaS billing | https://docs.lemonsqueezy.com/ | - | ⬜ Pending |
66
+
67
+ ### Search
68
+ | Företag | Kategori | API Docs | Kontakt | Status |
69
+ |---------|----------|----------|---------|--------|
70
+ | **Algolia** | Search-as-a-service | https://www.algolia.com/doc/ | - | ⬜ Pending |
71
+ | **Meilisearch** | Open-source search | https://www.meilisearch.com/docs | - | ⬜ Pending |
72
+ | **Typesense** | Search | https://typesense.org/docs/ | - | ⬜ Pending |
73
+ | **Elasticsearch** | Search/Analytics | https://www.elastic.co/guide/ | - | ⬜ Pending |
74
+
75
+ ### AI & Machine Learning
76
+ | Företag | Kategori | API Docs | Kontakt | Status |
77
+ |---------|----------|----------|---------|--------|
78
+ | **OpenAI** | LLM | https://platform.openai.com/docs | - | ⬜ Pending |
79
+ | **Anthropic** | LLM | https://docs.anthropic.com/ | - | ⬜ Pending |
80
+ | **Cohere** | NLP/Embeddings | https://docs.cohere.com/ | - | ⬜ Pending |
81
+ | **Replicate** | ML models | https://replicate.com/docs | - | ⬜ Pending |
82
+ | **Hugging Face** | ML models | https://huggingface.co/docs | - | ⬜ Pending |
83
+
84
+ ### Storage & CDN
85
+ | Företag | Kategori | API Docs | Kontakt | Status |
86
+ |---------|----------|----------|---------|--------|
87
+ | **Cloudflare** | CDN, Workers | https://developers.cloudflare.com/ | - | ⬜ Pending |
88
+ | **Cloudinary** | Media management | https://cloudinary.com/documentation | - | ⬜ Pending |
89
+ | **Imgix** | Image CDN | https://docs.imgix.com/ | - | ⬜ Pending |
90
+ | **Uploadcare** | File uploads | https://uploadcare.com/docs/ | - | ⬜ Pending |
91
+
92
+ ### Analytics & Data
93
+ | Företag | Kategori | API Docs | Kontakt | Status |
94
+ |---------|----------|----------|---------|--------|
95
+ | **Mixpanel** | Product analytics | https://developer.mixpanel.com/ | - | ⬜ Pending |
96
+ | **Amplitude** | Product analytics | https://www.docs.developers.amplitude.com/ | - | ⬜ Pending |
97
+ | **Segment** | CDP | https://segment.com/docs/ | - | ⬜ Pending |
98
+ | **PostHog** | Open-source analytics | https://posthog.com/docs | - | ⬜ Pending |
99
+
100
+ ### Auth & Identity
101
+ | Företag | Kategori | API Docs | Kontakt | Status |
102
+ |---------|----------|----------|---------|--------|
103
+ | **Auth0** | Identity | https://auth0.com/docs | - | ⬜ Pending |
104
+ | **Clerk** | Auth for devs | https://clerk.com/docs | - | ⬜ Pending |
105
+ | **Stytch** | Passwordless auth | https://stytch.com/docs | - | ⬜ Pending |
106
+ | **WorkOS** | Enterprise SSO | https://workos.com/docs | - | ⬜ Pending |
107
+
108
+ ---
109
+
110
+ ## 📊 Status Legend
111
+ - ⬜ Pending — Inte kontaktad
112
+ - 📧 Sent — Email skickat
113
+ - 👀 Viewed — Öppnat/läst
114
+ - 💬 Replied — Svar mottaget
115
+ - ✅ Listed — Listad i APIClaw
116
+ - ❌ Declined — Avböjt
117
+
118
+ ---
119
+
120
+ ## 📝 Notes
121
+
122
+ ### Prioritering
123
+ 1. **Svenska/Nordiska först** — närmare relation, enklare pitch
124
+ 2. **Dev-fokuserade APIs** — förstår värdet av discoverability
125
+ 3. **Mindre/nyare APIs** — hungrigare på exposure
126
+
127
+ ### Kontakt-strategi
128
+ - Hitta developer relations / partnerships email
129
+ - Alternativt: support@ eller info@
130
+ - LinkedIn outreach som backup
131
+
132
+ ### Tracking
133
+ Uppdatera status här ELLER synka till Airtable (TBD).
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@nordsym/apiclaw",
3
+ "version": "1.0.0",
4
+ "description": "The API layer for autonomous agents. MCP-native API discovery.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "apiclaw": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc && cp -r src/registry dist/",
12
+ "dev": "tsx watch src/index.ts",
13
+ "start": "node dist/index.js",
14
+ "test": "tsx src/test.ts",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "keywords": ["mcp", "api", "agent", "discovery", "marketplace", "claude", "openai", "autonomous", "apiclaw"],
18
+ "author": "NordSym <gustav@nordsym.com>",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/nordsym/apiclaw"
23
+ },
24
+ "homepage": "https://apiclaw.nordsym.com",
25
+ "bugs": {
26
+ "url": "https://github.com/nordsym/apiclaw/issues"
27
+ },
28
+ "engines": {
29
+ "node": ">=18"
30
+ },
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^20.10.0",
36
+ "tsx": "^4.7.0",
37
+ "typescript": "^5.3.0"
38
+ }
39
+ }
package/src/credits.ts ADDED
@@ -0,0 +1,261 @@
1
+ // Credit system for APIvault
2
+ // MVP: In-memory store. Production: Supabase
3
+
4
+ import { AgentCredits, Purchase, APICredentials, UsageRecord, TransactionFee } from './types.js';
5
+ import { randomUUID } from 'crypto';
6
+ import { getFeePercentage, hasProSubscription } from './revenuecat.js';
7
+
8
+ // In-memory stores (replace with Supabase in production)
9
+ const agentCredits: Map<string, AgentCredits> = new Map();
10
+ const purchases: Map<string, Purchase> = new Map();
11
+ const usage: Map<string, UsageRecord> = new Map();
12
+
13
+ // Mock API keys for demo (in production, these would be provisioned from providers)
14
+ const mockCredentials: Record<string, () => APICredentials> = {
15
+ '46elks': () => ({
16
+ type: 'basic',
17
+ username: `u_${randomUUID().slice(0, 8)}`,
18
+ password: `p_${randomUUID().slice(0, 16)}`
19
+ }),
20
+ 'resend': () => ({
21
+ type: 'api_key',
22
+ api_key: `re_${randomUUID().replace(/-/g, '')}`
23
+ }),
24
+ 'brave_search': () => ({
25
+ type: 'api_key',
26
+ api_key: `BSA${randomUUID().replace(/-/g, '').toUpperCase().slice(0, 20)}`
27
+ }),
28
+ 'openrouter': () => ({
29
+ type: 'bearer',
30
+ api_key: `sk-or-v1-${randomUUID().replace(/-/g, '')}`
31
+ }),
32
+ 'elevenlabs': () => ({
33
+ type: 'api_key',
34
+ api_key: `${randomUUID().replace(/-/g, '')}`
35
+ })
36
+ };
37
+
38
+ /**
39
+ * Get or create agent credits account
40
+ */
41
+ export function getAgentCredits(agentId: string): AgentCredits {
42
+ if (!agentCredits.has(agentId)) {
43
+ agentCredits.set(agentId, {
44
+ agent_id: agentId,
45
+ balance_usd: 0,
46
+ currency: 'USD',
47
+ created_at: new Date().toISOString(),
48
+ updated_at: new Date().toISOString()
49
+ });
50
+ }
51
+ return agentCredits.get(agentId)!;
52
+ }
53
+
54
+ /**
55
+ * Add credits to an agent's account
56
+ */
57
+ export function addCredits(agentId: string, amountUsd: number): AgentCredits {
58
+ const credits = getAgentCredits(agentId);
59
+ credits.balance_usd += amountUsd;
60
+ credits.updated_at = new Date().toISOString();
61
+ return credits;
62
+ }
63
+
64
+ /**
65
+ * Purchase API access
66
+ * Returns credentials if successful
67
+ */
68
+ export function purchaseAPIAccess(
69
+ agentId: string,
70
+ providerId: string,
71
+ amountUsd: number
72
+ ): { success: boolean; purchase?: Purchase; error?: string } {
73
+ const credits = getAgentCredits(agentId);
74
+
75
+ // Check balance
76
+ if (credits.balance_usd < amountUsd) {
77
+ return {
78
+ success: false,
79
+ error: `Insufficient balance. Have $${credits.balance_usd.toFixed(2)}, need $${amountUsd.toFixed(2)}`
80
+ };
81
+ }
82
+
83
+ // Check if provider exists
84
+ if (!mockCredentials[providerId]) {
85
+ return {
86
+ success: false,
87
+ error: `Unknown provider: ${providerId}`
88
+ };
89
+ }
90
+
91
+ // Deduct credits
92
+ credits.balance_usd -= amountUsd;
93
+ credits.updated_at = new Date().toISOString();
94
+
95
+ // Create purchase record
96
+ const purchaseId = `pur_${randomUUID().slice(0, 12)}`;
97
+ const purchase: Purchase = {
98
+ id: purchaseId,
99
+ agent_id: agentId,
100
+ provider_id: providerId,
101
+ amount_usd: amountUsd,
102
+ credits_purchased: calculateCredits(providerId, amountUsd),
103
+ status: 'active',
104
+ credentials: mockCredentials[providerId](),
105
+ created_at: new Date().toISOString()
106
+ };
107
+
108
+ purchases.set(purchaseId, purchase);
109
+
110
+ // Initialize usage tracking
111
+ usage.set(purchaseId, {
112
+ purchase_id: purchaseId,
113
+ provider_id: providerId,
114
+ units_used: 0,
115
+ units_remaining: purchase.credits_purchased,
116
+ cost_incurred_usd: 0,
117
+ last_used_at: new Date().toISOString()
118
+ });
119
+
120
+ return { success: true, purchase };
121
+ }
122
+
123
+ /**
124
+ * Calculate credits based on provider pricing
125
+ */
126
+ function calculateCredits(providerId: string, amountUsd: number): number {
127
+ // Simplified credit calculation per provider
128
+ const creditsPerDollar: Record<string, number> = {
129
+ '46elks': 30, // ~30 SMS per dollar
130
+ 'resend': 1000, // ~1000 emails per dollar
131
+ 'brave_search': 200, // ~200 searches per dollar
132
+ 'openrouter': 100, // ~100k tokens per dollar (varies by model)
133
+ 'elevenlabs': 3333 // ~3333 characters per dollar
134
+ };
135
+
136
+ return Math.floor(amountUsd * (creditsPerDollar[providerId] || 100));
137
+ }
138
+
139
+ /**
140
+ * Get all purchases for an agent
141
+ */
142
+ export function getAgentPurchases(agentId: string): Purchase[] {
143
+ return Array.from(purchases.values()).filter(p => p.agent_id === agentId);
144
+ }
145
+
146
+ /**
147
+ * Get usage for a purchase
148
+ */
149
+ export function getUsage(purchaseId: string): UsageRecord | null {
150
+ return usage.get(purchaseId) || null;
151
+ }
152
+
153
+ /**
154
+ * Record usage (call this when API is used)
155
+ */
156
+ export function recordUsage(purchaseId: string, unitsUsed: number, costUsd: number): UsageRecord | null {
157
+ const record = usage.get(purchaseId);
158
+ if (!record) return null;
159
+
160
+ record.units_used += unitsUsed;
161
+ record.units_remaining = Math.max(0, record.units_remaining - unitsUsed);
162
+ record.cost_incurred_usd += costUsd;
163
+ record.last_used_at = new Date().toISOString();
164
+
165
+ // Update purchase status if depleted
166
+ if (record.units_remaining === 0) {
167
+ const purchase = purchases.get(purchaseId);
168
+ if (purchase) purchase.status = 'exhausted';
169
+ }
170
+
171
+ return record;
172
+ }
173
+
174
+ /**
175
+ * Get full balance summary for an agent
176
+ */
177
+ export function getBalanceSummary(agentId: string): {
178
+ credits: AgentCredits;
179
+ active_purchases: Purchase[];
180
+ total_spent_usd: number;
181
+ } {
182
+ const credits = getAgentCredits(agentId);
183
+ const agentPurchases = getAgentPurchases(agentId);
184
+ const activePurchases = agentPurchases.filter(p => p.status === 'active');
185
+ const totalSpent = agentPurchases.reduce((sum, p) => sum + p.amount_usd, 0);
186
+
187
+ return {
188
+ credits,
189
+ active_purchases: activePurchases,
190
+ total_spent_usd: totalSpent
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Calculate transaction fee based on user's subscription
196
+ * Pro users: 2% fee
197
+ * Free users: 5% fee
198
+ */
199
+ export async function calculateTransactionFee(
200
+ userId: string,
201
+ amountUsd: number
202
+ ): Promise<TransactionFee> {
203
+ const isPro = await hasProSubscription(userId);
204
+ const feePercentage = isPro ? 0.02 : 0.05;
205
+ const feeAmount = Math.round(amountUsd * feePercentage * 100) / 100;
206
+
207
+ return {
208
+ amount_usd: amountUsd,
209
+ fee_percentage: feePercentage,
210
+ fee_amount_usd: feeAmount,
211
+ net_amount_usd: Math.round((amountUsd - feeAmount) * 100) / 100,
212
+ tier: isPro ? 'pro' : 'free',
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Purchase API access with subscription-aware fees
218
+ */
219
+ export async function purchaseAPIAccessWithFees(
220
+ agentId: string,
221
+ userId: string,
222
+ providerId: string,
223
+ amountUsd: number
224
+ ): Promise<{
225
+ success: boolean;
226
+ purchase?: Purchase;
227
+ fee?: TransactionFee;
228
+ error?: string
229
+ }> {
230
+ // Calculate fees first
231
+ const fee = await calculateTransactionFee(userId, amountUsd);
232
+
233
+ // Total amount user pays = API cost + platform fee
234
+ const totalCost = amountUsd + fee.fee_amount_usd;
235
+
236
+ const credits = getAgentCredits(agentId);
237
+
238
+ if (credits.balance_usd < totalCost) {
239
+ return {
240
+ success: false,
241
+ error: `Insufficient balance. Have $${credits.balance_usd.toFixed(2)}, need $${totalCost.toFixed(2)} (includes ${fee.tier === 'pro' ? '2%' : '5%'} platform fee)`
242
+ };
243
+ }
244
+
245
+ // Make the purchase (deducts API cost)
246
+ const result = purchaseAPIAccess(agentId, providerId, amountUsd);
247
+
248
+ if (result.success && result.purchase) {
249
+ // Deduct platform fee separately
250
+ credits.balance_usd -= fee.fee_amount_usd;
251
+ credits.updated_at = new Date().toISOString();
252
+
253
+ return {
254
+ success: true,
255
+ purchase: result.purchase,
256
+ fee,
257
+ };
258
+ }
259
+
260
+ return result;
261
+ }
@@ -0,0 +1,147 @@
1
+ // Discovery engine for APIvault
2
+ // MVP: Keyword matching. Future: Embeddings + semantic search
3
+
4
+ import { SimpleAPI } from './types.js';
5
+ import { readFileSync } from 'fs';
6
+ import { fileURLToPath } from 'url';
7
+ import { dirname, join } from 'path';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+
12
+ // Load APIs from registry
13
+ const apisData = JSON.parse(
14
+ readFileSync(join(__dirname, 'registry', 'apis.json'), 'utf-8')
15
+ );
16
+ const apis: SimpleAPI[] = apisData.apis;
17
+
18
+ console.error(`[APIvault] Loaded ${apis.length} APIs from registry`);
19
+
20
+ export interface SimpleSearchResult {
21
+ api: SimpleAPI;
22
+ relevance_score: number;
23
+ match_reasons: string[];
24
+ }
25
+
26
+ /**
27
+ * Discover APIs based on a natural language query
28
+ * MVP uses keyword matching; production would use embeddings
29
+ */
30
+ export function discoverAPIs(
31
+ query: string,
32
+ options: {
33
+ category?: string;
34
+ maxResults?: number;
35
+ auth?: string;
36
+ httpsOnly?: boolean;
37
+ } = {}
38
+ ): SimpleSearchResult[] {
39
+ const { category, maxResults = 10, auth, httpsOnly } = options;
40
+
41
+ const queryLower = query.toLowerCase();
42
+ const queryWords = queryLower.split(/\s+/).filter(w => w.length > 2);
43
+
44
+ const results: SimpleSearchResult[] = [];
45
+
46
+ for (const api of apis) {
47
+ // Category filter (case-insensitive)
48
+ if (category && api.category.toLowerCase() !== category.toLowerCase()) continue;
49
+
50
+ // Auth filter
51
+ if (auth && api.auth.toLowerCase() !== auth.toLowerCase()) continue;
52
+
53
+ // HTTPS filter
54
+ if (httpsOnly && !api.https) continue;
55
+
56
+ // Calculate relevance score
57
+ let score = 0;
58
+ const matchReasons: string[] = [];
59
+
60
+ for (const word of queryWords) {
61
+ // Name match (highest weight)
62
+ if (api.name.toLowerCase().includes(word)) {
63
+ score += 25;
64
+ matchReasons.push(`name: ${word}`);
65
+ }
66
+
67
+ // Keyword match
68
+ if (api.keywords && api.keywords.some(k => k.toLowerCase().includes(word))) {
69
+ score += 15;
70
+ matchReasons.push(`keyword: ${word}`);
71
+ }
72
+
73
+ // Description match
74
+ if (api.description.toLowerCase().includes(word)) {
75
+ score += 10;
76
+ matchReasons.push(`description: ${word}`);
77
+ }
78
+
79
+ // Category match
80
+ if (api.category.toLowerCase().includes(word)) {
81
+ score += 8;
82
+ matchReasons.push(`category: ${word}`);
83
+ }
84
+ }
85
+
86
+ // Only add secondary boosts if there's a primary match
87
+ if (score > 0) {
88
+ // Boost for HTTPS
89
+ if (api.https) {
90
+ score += 2;
91
+ }
92
+
93
+ // Boost for known CORS support
94
+ if (api.cors === 'yes') {
95
+ score += 1;
96
+ }
97
+ }
98
+
99
+ if (score > 0) {
100
+ results.push({
101
+ api,
102
+ relevance_score: Math.round(score * 100) / 100,
103
+ match_reasons: [...new Set(matchReasons)]
104
+ });
105
+ }
106
+ }
107
+
108
+ // Sort by relevance
109
+ results.sort((a, b) => b.relevance_score - a.relevance_score);
110
+
111
+ return results.slice(0, maxResults);
112
+ }
113
+
114
+ /**
115
+ * Get detailed information about a specific API
116
+ */
117
+ export function getAPIDetails(apiId: string): SimpleAPI | null {
118
+ return apis.find(api => api.id === apiId) || null;
119
+ }
120
+
121
+ /**
122
+ * List all APIs in a category
123
+ */
124
+ export function listByCategory(category: string): SimpleAPI[] {
125
+ return apis.filter(api => api.category.toLowerCase() === category.toLowerCase());
126
+ }
127
+
128
+ /**
129
+ * Get all available categories
130
+ */
131
+ export function getCategories(): string[] {
132
+ return [...new Set(apis.map(api => api.category))].sort();
133
+ }
134
+
135
+ /**
136
+ * Get all APIs
137
+ */
138
+ export function getAllAPIs(): SimpleAPI[] {
139
+ return apis;
140
+ }
141
+
142
+ /**
143
+ * Get API count
144
+ */
145
+ export function getAPICount(): number {
146
+ return apis.length;
147
+ }