@nordsym/apiclaw 2.1.0 → 2.2.1

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 (185) hide show
  1. package/README.md +15 -2
  2. package/dist/bin-http.js +0 -0
  3. package/dist/bin.bundled.js +79288 -0
  4. package/dist/funnel-client.d.ts +24 -0
  5. package/dist/funnel-client.d.ts.map +1 -0
  6. package/dist/funnel-client.js +131 -0
  7. package/dist/funnel-client.js.map +1 -0
  8. package/dist/funnel.test.d.ts +2 -0
  9. package/dist/funnel.test.d.ts.map +1 -0
  10. package/dist/funnel.test.js +145 -0
  11. package/dist/funnel.test.js.map +1 -0
  12. package/dist/gateway-client.d.ts.map +1 -1
  13. package/dist/gateway-client.js +24 -2
  14. package/dist/gateway-client.js.map +1 -1
  15. package/dist/index.bundled.js +61263 -0
  16. package/dist/index.js +161 -74
  17. package/dist/index.js.map +1 -1
  18. package/dist/postinstall.d.ts +0 -5
  19. package/dist/postinstall.d.ts.map +1 -1
  20. package/dist/postinstall.js +24 -3
  21. package/dist/postinstall.js.map +1 -1
  22. package/dist/registration-guard.d.ts +29 -0
  23. package/dist/registration-guard.d.ts.map +1 -0
  24. package/dist/registration-guard.js +87 -0
  25. package/dist/registration-guard.js.map +1 -0
  26. package/package.json +7 -2
  27. package/.claude/settings.local.json +0 -9
  28. package/.env.prod +0 -1
  29. package/apiclaw-README.md +0 -494
  30. package/convex/_generated/api.d.ts +0 -137
  31. package/convex/_generated/api.js +0 -23
  32. package/convex/_generated/dataModel.d.ts +0 -60
  33. package/convex/_generated/server.d.ts +0 -143
  34. package/convex/_generated/server.js +0 -93
  35. package/convex/adminActivate.ts +0 -53
  36. package/convex/adminStats.ts +0 -306
  37. package/convex/agents.ts +0 -939
  38. package/convex/analytics.ts +0 -187
  39. package/convex/apiKeys.ts +0 -220
  40. package/convex/backfillAnalytics.ts +0 -272
  41. package/convex/backfillSearchLogs.ts +0 -35
  42. package/convex/billing.ts +0 -834
  43. package/convex/capabilities.ts +0 -157
  44. package/convex/chains.ts +0 -1318
  45. package/convex/credits.ts +0 -211
  46. package/convex/crons.ts +0 -50
  47. package/convex/debugFilestackLogs.ts +0 -16
  48. package/convex/debugGetToken.ts +0 -18
  49. package/convex/directCall.ts +0 -713
  50. package/convex/earnProgress.ts +0 -753
  51. package/convex/email.ts +0 -329
  52. package/convex/feedback.ts +0 -265
  53. package/convex/http.ts +0 -3430
  54. package/convex/inbound.ts +0 -32
  55. package/convex/logs.ts +0 -701
  56. package/convex/migrateFilestack.ts +0 -81
  57. package/convex/migratePartnersProd.ts +0 -174
  58. package/convex/migratePratham.ts +0 -126
  59. package/convex/migrateProviderWorkspaces.ts +0 -175
  60. package/convex/mou.ts +0 -91
  61. package/convex/providerKeys.ts +0 -289
  62. package/convex/providers.ts +0 -1135
  63. package/convex/purchases.ts +0 -183
  64. package/convex/ratelimit.ts +0 -104
  65. package/convex/schema.ts +0 -869
  66. package/convex/searchLogs.ts +0 -265
  67. package/convex/seedAPILayerAPIs.ts +0 -191
  68. package/convex/seedDirectCallConfigs.ts +0 -336
  69. package/convex/seedPratham.ts +0 -149
  70. package/convex/spendAlerts.ts +0 -442
  71. package/convex/stripeActions.ts +0 -607
  72. package/convex/teams.ts +0 -243
  73. package/convex/telemetry.ts +0 -81
  74. package/convex/tsconfig.json +0 -25
  75. package/convex/updateAPIStatus.ts +0 -44
  76. package/convex/usage.ts +0 -260
  77. package/convex/usageReports.ts +0 -357
  78. package/convex/waitlist.ts +0 -55
  79. package/convex/webhooks.ts +0 -494
  80. package/convex/workspaceSettings.ts +0 -143
  81. package/convex/workspaces.ts +0 -1331
  82. package/convex.json +0 -3
  83. package/direct-test.mjs +0 -51
  84. package/email-templates/filestack-provider-outreach.html +0 -162
  85. package/email-templates/partnership-template.html +0 -116
  86. package/email-templates/pratham-draft-preview.txt +0 -57
  87. package/email-templates/pratham-partnership-draft.html +0 -141
  88. package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
  89. package/reports/pipeline/PIPELINE-REPORT.json +0 -153
  90. package/reports/pipeline/acquire_apisguru.json +0 -17
  91. package/reports/pipeline/capabilities.json +0 -38
  92. package/reports/pipeline/discover_azure_recursive.json +0 -1551
  93. package/reports/pipeline/discover_github.json +0 -25
  94. package/reports/pipeline/discover_github_repos.json +0 -49
  95. package/reports/pipeline/discover_swaggerhub.json +0 -24
  96. package/reports/pipeline/discover_well_known.json +0 -23
  97. package/reports/pipeline/fetch_specs.json +0 -19
  98. package/reports/pipeline/generate_providers.json +0 -14
  99. package/reports/pipeline/match_registry.json +0 -11
  100. package/reports/pipeline/parse_specs.json +0 -17
  101. package/reports/pipeline/promote_candidates.json +0 -34
  102. package/reports/pipeline/validate.json +0 -30
  103. package/reports/pipeline/validate_smoke_details.json +0 -3835
  104. package/reports/session-report-2026-04-05.html +0 -433
  105. package/seed-apis-direct.mjs +0 -106
  106. package/src/access-control.ts +0 -174
  107. package/src/adapters/base.ts +0 -364
  108. package/src/adapters/claude-desktop.ts +0 -41
  109. package/src/adapters/cline.ts +0 -88
  110. package/src/adapters/continue.ts +0 -91
  111. package/src/adapters/cursor.ts +0 -43
  112. package/src/adapters/custom.ts +0 -188
  113. package/src/adapters/detect.ts +0 -202
  114. package/src/adapters/index.ts +0 -47
  115. package/src/adapters/windsurf.ts +0 -44
  116. package/src/bin-http.ts +0 -45
  117. package/src/bin.ts +0 -34
  118. package/src/capability-router.ts +0 -331
  119. package/src/chainExecutor.ts +0 -730
  120. package/src/chainResolver.test.ts +0 -246
  121. package/src/chainResolver.ts +0 -658
  122. package/src/cli/commands/demo.ts +0 -109
  123. package/src/cli/commands/doctor.ts +0 -435
  124. package/src/cli/commands/index.ts +0 -9
  125. package/src/cli/commands/login.ts +0 -203
  126. package/src/cli/commands/mcp-install.ts +0 -373
  127. package/src/cli/commands/restore.ts +0 -333
  128. package/src/cli/commands/setup.ts +0 -297
  129. package/src/cli/commands/uninstall.ts +0 -240
  130. package/src/cli/index.ts +0 -148
  131. package/src/cli.ts +0 -370
  132. package/src/confirmation.ts +0 -296
  133. package/src/credentials.ts +0 -455
  134. package/src/credits.ts +0 -329
  135. package/src/crypto.ts +0 -75
  136. package/src/discovery.ts +0 -568
  137. package/src/enterprise/env.ts +0 -156
  138. package/src/enterprise/index.ts +0 -7
  139. package/src/enterprise/script-generator.ts +0 -481
  140. package/src/execute-dynamic.ts +0 -617
  141. package/src/execute.ts +0 -2386
  142. package/src/gateway-client.ts +0 -192
  143. package/src/hivr-whitelist.ts +0 -110
  144. package/src/http-api.ts +0 -286
  145. package/src/http-server-minimal.ts +0 -154
  146. package/src/index.ts +0 -2611
  147. package/src/intelligent-gateway.ts +0 -339
  148. package/src/mcp-analytics.ts +0 -156
  149. package/src/metered.ts +0 -149
  150. package/src/open-apis-generated.ts +0 -157
  151. package/src/open-apis.ts +0 -558
  152. package/src/postinstall.ts +0 -18
  153. package/src/product-whitelist.ts +0 -246
  154. package/src/proxy.ts +0 -36
  155. package/src/session.ts +0 -129
  156. package/src/stripe.ts +0 -497
  157. package/src/telemetry.ts +0 -71
  158. package/src/test.ts +0 -135
  159. package/src/types/convex-api.d.ts +0 -20
  160. package/src/types/convex-api.ts +0 -21
  161. package/src/types.ts +0 -109
  162. package/src/ui/colors.ts +0 -219
  163. package/src/ui/errors.ts +0 -394
  164. package/src/ui/index.ts +0 -17
  165. package/src/ui/prompts.ts +0 -390
  166. package/src/ui/spinner.ts +0 -325
  167. package/src/utils/backup.ts +0 -224
  168. package/src/utils/config.ts +0 -318
  169. package/src/utils/os.ts +0 -124
  170. package/src/utils/paths.ts +0 -203
  171. package/src/webhook.ts +0 -107
  172. package/test-10-working.cjs +0 -97
  173. package/test-14-final.cjs +0 -96
  174. package/test-actual-handlers.ts +0 -92
  175. package/test-apilayer-all-14.ts +0 -249
  176. package/test-apilayer-fixed.ts +0 -248
  177. package/test-direct-endpoints.ts +0 -174
  178. package/test-exact-endpoints.ts +0 -144
  179. package/test-final.ts +0 -83
  180. package/test-full-routing.ts +0 -100
  181. package/test-handlers-correct.ts +0 -217
  182. package/test-numverify-key.ts +0 -41
  183. package/test-via-handlers.ts +0 -92
  184. package/test-worldnews.mjs +0 -26
  185. package/tsconfig.json +0 -20
@@ -1,157 +0,0 @@
1
- /**
2
- * Generated open API providers — loaded from src/registry/generated-providers.json.
3
- *
4
- * This file is the runtime adapter that turns the static JSON artifact
5
- * produced by scripts/pipeline/generate_providers.py into OpenAPIConfig
6
- * instances. Manual providers in open-apis.ts always take precedence; this
7
- * loader only adds providers whose ids don't already exist.
8
- *
9
- * Path templating uses simple {var} substitution from params; remaining
10
- * params with `in: query` get appended to the query string.
11
- */
12
-
13
- import { createRequire } from 'module';
14
- import type { OpenAPIConfig, OpenAPIAction } from './open-apis.js';
15
-
16
- const require = createRequire(import.meta.url);
17
-
18
- interface GeneratedActionParam {
19
- name: string;
20
- in: 'path' | 'query' | 'header' | 'body' | string;
21
- required?: boolean;
22
- type?: string;
23
- }
24
-
25
- interface GeneratedAction {
26
- method: 'GET' | 'POST';
27
- pathTemplate: string;
28
- operationId?: string;
29
- summary?: string;
30
- params?: GeneratedActionParam[];
31
- requiresAuth?: boolean;
32
- }
33
-
34
- interface GeneratedProvider {
35
- id: string;
36
- name: string;
37
- description: string;
38
- baseUrl: string;
39
- host: string;
40
- source: string;
41
- sourceUrl: string;
42
- callable: boolean;
43
- matchKind: string;
44
- matchConfidence: number;
45
- actionCount: number;
46
- actions: Record<string, GeneratedAction>;
47
- }
48
-
49
- interface GeneratedArtifact {
50
- version: number;
51
- generatedAt: number;
52
- providerCount: number;
53
- callableCount: number;
54
- providers: GeneratedProvider[];
55
- }
56
-
57
- let cachedArtifact: GeneratedArtifact | null = null;
58
-
59
- function loadArtifact(): GeneratedArtifact {
60
- if (cachedArtifact) return cachedArtifact;
61
- try {
62
- cachedArtifact = require('./registry/generated-providers.json') as GeneratedArtifact;
63
- } catch {
64
- cachedArtifact = {
65
- version: 1,
66
- generatedAt: 0,
67
- providerCount: 0,
68
- callableCount: 0,
69
- providers: [],
70
- };
71
- }
72
- return cachedArtifact;
73
- }
74
-
75
- function buildPath(template: string, params: Record<string, any>, paramDefs: GeneratedActionParam[]): string {
76
- let path = template;
77
- const pathVars = new Set<string>();
78
- for (const def of paramDefs) {
79
- if (def.in === 'path') pathVars.add(def.name);
80
- }
81
- // substitute {var}
82
- path = path.replace(/\{([^}]+)\}/g, (_, name) => {
83
- pathVars.add(name);
84
- const v = params[name];
85
- return v === undefined || v === null ? '' : encodeURIComponent(String(v));
86
- });
87
- // append query string params
88
- const queryPairs: string[] = [];
89
- for (const def of paramDefs) {
90
- if (def.in !== 'query') continue;
91
- const v = params[def.name];
92
- if (v === undefined || v === null) continue;
93
- queryPairs.push(`${encodeURIComponent(def.name)}=${encodeURIComponent(String(v))}`);
94
- }
95
- // also include any explicit params not declared in spec but passed at call time
96
- for (const [k, v] of Object.entries(params)) {
97
- if (pathVars.has(k)) continue;
98
- if (paramDefs.some((d) => d.name === k && d.in === 'query')) continue;
99
- if (k === '_body' || k === '_headers') continue;
100
- if (v === undefined || v === null) continue;
101
- if (typeof v === 'object') continue;
102
- queryPairs.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
103
- }
104
- if (queryPairs.length) {
105
- path += (path.includes('?') ? '&' : '?') + queryPairs.join('&');
106
- }
107
- return path;
108
- }
109
-
110
- function toOpenAPIConfig(p: GeneratedProvider): OpenAPIConfig {
111
- const actions: Record<string, OpenAPIAction> = {};
112
- for (const [aid, a] of Object.entries(p.actions)) {
113
- const defs = a.params || [];
114
- actions[aid] = {
115
- method: a.method,
116
- path: (params) => buildPath(a.pathTemplate, params || {}, defs),
117
- };
118
- }
119
- return {
120
- name: p.name,
121
- description: p.description || `${p.name} (auto-generated from ${p.source})`,
122
- baseUrl: p.baseUrl.replace(/\/$/, ''),
123
- actions,
124
- };
125
- }
126
-
127
- /**
128
- * Build the registry of generated providers ready to merge into openAPIs.
129
- * Only providers marked callable=true are returned by default; pass
130
- * { includeCandidates: true } to surface auth-gated entries as well.
131
- */
132
- export function loadGeneratedProviders(opts: { includeCandidates?: boolean } = {}): Record<string, OpenAPIConfig> {
133
- const artifact = loadArtifact();
134
- const out: Record<string, OpenAPIConfig> = {};
135
- for (const p of artifact.providers) {
136
- if (!p.callable && !opts.includeCandidates) continue;
137
- out[p.id] = toOpenAPIConfig(p);
138
- }
139
- return out;
140
- }
141
-
142
- export function getGeneratedArtifactStats(): {
143
- providerCount: number;
144
- callableCount: number;
145
- generatedAt: number;
146
- } {
147
- const a = loadArtifact();
148
- return {
149
- providerCount: a.providerCount,
150
- callableCount: a.callableCount,
151
- generatedAt: a.generatedAt,
152
- };
153
- }
154
-
155
- export function getGeneratedProviderMeta(id: string): GeneratedProvider | undefined {
156
- return loadArtifact().providers.find((p) => p.id === id);
157
- }
package/src/open-apis.ts DELETED
@@ -1,558 +0,0 @@
1
- /**
2
- * APIClaw Open APIs - Free APIs that don't require authentication
3
- * These are called directly but still logged for analytics
4
- */
5
-
6
- import { loadGeneratedProviders, getGeneratedArtifactStats } from './open-apis-generated.js';
7
-
8
- export interface OpenAPIConfig {
9
- name: string;
10
- description: string;
11
- baseUrl: string;
12
- actions: Record<string, OpenAPIAction>;
13
- }
14
-
15
- export interface OpenAPIAction {
16
- method: 'GET' | 'POST';
17
- path: (params: Record<string, any>) => string;
18
- transform?: (data: any, params: Record<string, any>) => any;
19
- }
20
-
21
- /**
22
- * Registry of open APIs
23
- */
24
- export const openAPIs: Record<string, OpenAPIConfig> = {
25
- // Frankfurter - Free currency exchange rates (ECB data)
26
- frankfurter: {
27
- name: 'Frankfurter',
28
- description: 'Free currency exchange rates from European Central Bank',
29
- baseUrl: 'https://api.frankfurter.app',
30
- actions: {
31
- convert: {
32
- method: 'GET',
33
- path: (p) => `/latest?from=${p.from || 'SEK'}&to=${p.to || 'USD'}&amount=${p.amount || 1}`,
34
- transform: (data, params) => ({
35
- from: params.from || 'SEK',
36
- to: params.to || 'USD',
37
- amount: data.amount,
38
- result: data.rates?.[params.to || 'USD'],
39
- rate: data.rates?.[params.to || 'USD'] / data.amount,
40
- date: data.date,
41
- }),
42
- },
43
- latest: {
44
- method: 'GET',
45
- path: (p) => `/latest?from=${p.base || 'SEK'}${p.symbols ? `&to=${p.symbols}` : ''}`,
46
- transform: (data) => ({
47
- base: data.base,
48
- date: data.date,
49
- rates: data.rates,
50
- }),
51
- },
52
- historical: {
53
- method: 'GET',
54
- path: (p) => `/${p.date}?from=${p.base || 'SEK'}${p.symbols ? `&to=${p.symbols}` : ''}`,
55
- transform: (data) => ({
56
- base: data.base,
57
- date: data.date,
58
- rates: data.rates,
59
- }),
60
- },
61
- currencies: {
62
- method: 'GET',
63
- path: () => '/currencies',
64
- transform: (data) => ({ currencies: data }),
65
- },
66
- },
67
- },
68
-
69
- // CoinGecko - Free crypto data
70
- coingecko: {
71
- name: 'CoinGecko',
72
- description: 'Free cryptocurrency data - prices, market cap, volume',
73
- baseUrl: 'https://api.coingecko.com/api/v3',
74
- actions: {
75
- price: {
76
- method: 'GET',
77
- path: (p) => `/simple/price?ids=${p.ids || 'bitcoin'}&vs_currencies=${p.vs_currencies || 'usd'}`,
78
- },
79
- coins_list: {
80
- method: 'GET',
81
- path: () => '/coins/list',
82
- },
83
- coin: {
84
- method: 'GET',
85
- path: (p) => `/coins/${p.id}?localization=false&tickers=false&community_data=false&developer_data=false`,
86
- transform: (data) => ({
87
- id: data.id,
88
- symbol: data.symbol,
89
- name: data.name,
90
- price_usd: data.market_data?.current_price?.usd,
91
- market_cap_usd: data.market_data?.market_cap?.usd,
92
- price_change_24h: data.market_data?.price_change_percentage_24h,
93
- }),
94
- },
95
- trending: {
96
- method: 'GET',
97
- path: () => '/search/trending',
98
- transform: (data) => ({
99
- coins: data.coins?.map((c: any) => ({
100
- id: c.item.id,
101
- name: c.item.name,
102
- symbol: c.item.symbol,
103
- market_cap_rank: c.item.market_cap_rank,
104
- })),
105
- }),
106
- },
107
- },
108
- },
109
-
110
- // IP-API - Free IP geolocation
111
- ipapi: {
112
- name: 'IP-API',
113
- description: 'Free IP geolocation - get location from IP address',
114
- baseUrl: 'http://ip-api.com',
115
- actions: {
116
- lookup: {
117
- method: 'GET',
118
- path: (p) => `/json/${p.ip || ''}`,
119
- transform: (data) => ({
120
- ip: data.query,
121
- country: data.country,
122
- countryCode: data.countryCode,
123
- region: data.regionName,
124
- city: data.city,
125
- lat: data.lat,
126
- lon: data.lon,
127
- isp: data.isp,
128
- timezone: data.timezone,
129
- }),
130
- },
131
- },
132
- },
133
-
134
- // Kroki - Diagrams as code
135
- kroki: {
136
- name: 'Kroki',
137
- description: 'Generate diagrams from text. Supports: mermaid, d2, plantuml, graphviz, c4plantuml, blockdiag, bpmn. D2 is SVG-only.',
138
- baseUrl: 'https://kroki.io',
139
- actions: {
140
- render: {
141
- method: 'POST',
142
- path: () => '', // Custom handling below
143
- },
144
- },
145
- },
146
- };
147
-
148
- /**
149
- * Merge generated providers into the registry.
150
- * Curated entries with custom transforms take precedence on id collisions.
151
- */
152
- const _generatedProviders = loadGeneratedProviders();
153
- for (const [id, cfg] of Object.entries(_generatedProviders)) {
154
- if (!(id in openAPIs)) {
155
- openAPIs[id] = cfg;
156
- }
157
- }
158
-
159
- /**
160
- * Generic passthrough provider — proxy any open API without pre-configuration.
161
- * Enables all 22,000+ indexed APIs to be callable on demand.
162
- *
163
- * Usage: call_api("generic", "request", {
164
- * url: "https://api.example.com/endpoint",
165
- * method: "GET", // optional, default GET
166
- * query: { key: "value" }, // optional query params
167
- * body: { key: "value" }, // optional body (POST/PUT/PATCH)
168
- * headers: { "X-Foo": "bar" }, // optional extra headers
169
- * })
170
- */
171
-
172
- const SSRF_BLOCKLIST = [
173
- /^https?:\/\/localhost/i,
174
- /^https?:\/\/127\./,
175
- /^https?:\/\/10\./,
176
- /^https?:\/\/172\.(1[6-9]|2\d|3[01])\./,
177
- /^https?:\/\/192\.168\./,
178
- /^https?:\/\/169\.254\./, // link-local / AWS metadata
179
- /^https?:\/\/metadata\.google/i,
180
- /^https?:\/\/\[::1\]/, // IPv6 loopback
181
- ];
182
-
183
- function isSsrfBlocked(url: string): boolean {
184
- return SSRF_BLOCKLIST.some(r => r.test(url));
185
- }
186
-
187
- async function executeGeneric(
188
- action: string,
189
- params: Record<string, any>
190
- ): Promise<{ success: boolean; provider: string; action: string; data?: any; error?: string }> {
191
- const url: string = params.url;
192
- if (!url) {
193
- return { success: false, provider: 'generic', action, error: 'Missing required param: url' };
194
- }
195
-
196
- if (isSsrfBlocked(url)) {
197
- return { success: false, provider: 'generic', action, error: 'URL is not allowed (private/internal range)' };
198
- }
199
-
200
- const method = (params.method || 'GET').toUpperCase();
201
- const query: Record<string, string> = params.query || {};
202
- const body = params.body;
203
- const extraHeaders: Record<string, string> = params.headers || {};
204
-
205
- // Build URL with query params
206
- let finalUrl = url;
207
- const qKeys = Object.keys(query);
208
- if (qKeys.length > 0) {
209
- const sep = url.includes('?') ? '&' : '?';
210
- finalUrl = url + sep + qKeys.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(query[k])}`).join('&');
211
- }
212
-
213
- const headers: Record<string, string> = {
214
- 'Accept': 'application/json',
215
- 'User-Agent': 'APIClaw/1.0 (+https://apiclaw.cloud)',
216
- ...extraHeaders,
217
- };
218
-
219
- const fetchOpts: RequestInit = { method, headers };
220
-
221
- if (body && ['POST', 'PUT', 'PATCH'].includes(method)) {
222
- headers['Content-Type'] = 'application/json';
223
- fetchOpts.body = JSON.stringify(body);
224
- }
225
-
226
- try {
227
- const res = await fetch(finalUrl, fetchOpts);
228
- const contentType = res.headers.get('content-type') || '';
229
-
230
- let data: any;
231
- if (contentType.includes('application/json')) {
232
- data = await res.json();
233
- } else {
234
- data = await res.text();
235
- }
236
-
237
- if (!res.ok) {
238
- return {
239
- success: false,
240
- provider: 'generic',
241
- action,
242
- error: `HTTP ${res.status}: ${res.statusText}`,
243
- data,
244
- };
245
- }
246
-
247
- return { success: true, provider: 'generic', action, data };
248
- } catch (e: any) {
249
- return { success: false, provider: 'generic', action, error: e.message || 'Request failed' };
250
- }
251
- }
252
-
253
- /**
254
- * Check if a provider is an open API
255
- */
256
- export function isOpenAPI(providerId: string): boolean {
257
- return providerId === 'generic' || providerId in openAPIs;
258
- }
259
-
260
- /**
261
- * Get available actions for an open API
262
- */
263
- export function getOpenAPIActions(providerId: string): string[] {
264
- return Object.keys(openAPIs[providerId]?.actions || {});
265
- }
266
-
267
- /**
268
- * Execute an open API call
269
- */
270
- export async function executeOpenAPI(
271
- providerId: string,
272
- action: string,
273
- params: Record<string, any>
274
- ): Promise<{ success: boolean; provider: string; action: string; data?: any; error?: string }> {
275
- // Generic passthrough — proxy any open API on demand
276
- if (providerId === 'generic') {
277
- return await executeGeneric(action, params);
278
- }
279
-
280
- const config = openAPIs[providerId];
281
-
282
- if (!config) {
283
- return {
284
- success: false,
285
- provider: providerId,
286
- action,
287
- error: `Unknown open API: ${providerId}`,
288
- };
289
- }
290
-
291
- const actionConfig = config.actions[action];
292
-
293
- if (!actionConfig) {
294
- return {
295
- success: false,
296
- provider: providerId,
297
- action,
298
- error: `Unknown action '${action}' for ${providerId}. Available: ${Object.keys(config.actions).join(', ')}`,
299
- };
300
- }
301
-
302
- try {
303
-
304
- // Special handling for Kroki - use POST to get actual image data
305
- if (providerId === 'kroki') {
306
- const type = params.type || 'mermaid';
307
- let format = params.format || 'svg';
308
- const diagram = params.diagram || '';
309
-
310
- if (!diagram) {
311
- return {
312
- success: false,
313
- provider: providerId,
314
- action,
315
- error: 'Missing required param: diagram',
316
- };
317
- }
318
-
319
- // D2 only supports SVG
320
- if (type === 'd2' && format !== 'svg') {
321
- format = 'svg';
322
- }
323
-
324
- try {
325
- const response = await fetch(`https://kroki.io/${type}/${format}`, {
326
- method: 'POST',
327
- headers: { 'Content-Type': 'text/plain' },
328
- body: diagram,
329
- });
330
-
331
- if (!response.ok) {
332
- const err = await response.text();
333
- return {
334
- success: false,
335
- provider: providerId,
336
- action,
337
- error: `Kroki error: ${err}`,
338
- };
339
- }
340
-
341
- // For SVG, return the content directly; for binary, return base64
342
- if (format === 'svg') {
343
- const svg = await response.text();
344
- return {
345
- success: true,
346
- provider: providerId,
347
- action,
348
- data: {
349
- type,
350
- format: 'svg',
351
- content: svg,
352
- content_type: 'image/svg+xml',
353
- supported_types: ['mermaid', 'd2', 'plantuml', 'graphviz', 'c4plantuml', 'blockdiag', 'bpmn'],
354
- },
355
- };
356
- } else {
357
- const buffer = await response.arrayBuffer();
358
- const base64 = Buffer.from(buffer).toString('base64');
359
- return {
360
- success: true,
361
- provider: providerId,
362
- action,
363
- data: {
364
- type,
365
- format,
366
- content_base64: base64,
367
- content_type: format === 'png' ? 'image/png' : 'application/pdf',
368
- },
369
- };
370
- }
371
- } catch (e: any) {
372
- return {
373
- success: false,
374
- provider: providerId,
375
- action,
376
- error: e.message || 'Kroki request failed',
377
- };
378
- }
379
- }
380
-
381
- const url = config.baseUrl + actionConfig.path(params);
382
- const response = await fetch(url, {
383
- method: actionConfig.method,
384
- headers: {
385
- 'Accept': 'application/json',
386
- 'User-Agent': 'APIClaw/1.0 (+https://apiclaw.cloud)',
387
- },
388
- });
389
-
390
- if (!response.ok) {
391
- return {
392
- success: false,
393
- provider: providerId,
394
- action,
395
- error: `HTTP ${response.status}: ${response.statusText}`,
396
- };
397
- }
398
-
399
- const ct = response.headers.get('content-type') || '';
400
- let data: any;
401
- if (ct.includes('application/json') || ct.includes('+json')) {
402
- data = await response.json();
403
- } else {
404
- const text = await response.text();
405
- try {
406
- data = JSON.parse(text);
407
- } catch {
408
- data = { raw: text.length > 4096 ? text.slice(0, 4096) + '…' : text, contentType: ct };
409
- }
410
- }
411
-
412
- if (actionConfig.transform) {
413
- data = actionConfig.transform(data, params);
414
- }
415
-
416
- return {
417
- success: true,
418
- provider: providerId,
419
- action,
420
- data,
421
- };
422
- } catch (e: any) {
423
- return {
424
- success: false,
425
- provider: providerId,
426
- action,
427
- error: e.message || 'Request failed',
428
- };
429
- }
430
- }
431
-
432
- /**
433
- * Resolve the full URL that executeOpenAPI would fetch for a given call.
434
- * Used by the gateway client to pass the target URL to the Intelligent Gateway.
435
- *
436
- * Returns undefined if the provider/action is unknown or needs special handling
437
- * that can't be expressed as a simple URL (e.g. Kroki POST with body).
438
- */
439
- export function getOpenAPIBaseUrl(
440
- providerId: string,
441
- action: string,
442
- params: Record<string, any>,
443
- ): string | undefined {
444
- // Generic passthrough -- the caller already supplies the full URL
445
- if (providerId === 'generic') {
446
- return params.url as string | undefined;
447
- }
448
-
449
- const config = openAPIs[providerId];
450
- if (!config) return undefined;
451
-
452
- const actionConfig = config.actions[action];
453
- if (!actionConfig) return undefined;
454
-
455
- // Kroki uses a POST body; return the URL but the gateway will also need the body
456
- if (providerId === 'kroki') {
457
- const type = params.type || 'mermaid';
458
- let format = params.format || 'svg';
459
- if (type === 'd2' && format !== 'svg') format = 'svg';
460
- return `${config.baseUrl}/${type}/${format}`;
461
- }
462
-
463
- try {
464
- return config.baseUrl + actionConfig.path(params);
465
- } catch {
466
- return undefined;
467
- }
468
- }
469
-
470
- /**
471
- * List all open APIs with their actions
472
- */
473
- export function listOpenAPIs(): { provider: string; name: string; description: string; actions: string[] }[] {
474
- const curated = Object.entries(openAPIs).map(([id, config]) => ({
475
- provider: id,
476
- name: config.name,
477
- description: config.description,
478
- actions: Object.keys(config.actions),
479
- }));
480
-
481
- return [
482
- {
483
- provider: 'generic',
484
- name: 'Generic Passthrough',
485
- description: 'Proxy any open/public API on demand. Supply url, method, query, body, headers. No pre-configuration needed — unlocks all 22,000+ indexed APIs.',
486
- actions: ['request'],
487
- },
488
- ...curated,
489
- ];
490
- }
491
-
492
- /**
493
- * Stats about the generated provider artifact.
494
- */
495
- export function getOpenAPIStats(): {
496
- curatedProviders: number;
497
- generatedProviders: number;
498
- generatedCallable: number;
499
- totalProviders: number;
500
- generatedAt: number;
501
- } {
502
- const stats = getGeneratedArtifactStats();
503
- // openAPIs already contains curated + merged generated; subtract to get the curated count
504
- const curatedOnly = Object.keys(openAPIs).length - Object.keys(_generatedProviders).length;
505
- return {
506
- curatedProviders: curatedOnly,
507
- generatedProviders: stats.providerCount,
508
- generatedCallable: stats.callableCount,
509
- totalProviders: Object.keys(openAPIs).length,
510
- generatedAt: stats.generatedAt,
511
- };
512
- }
513
-
514
- /**
515
- * APIClaw operates three tiers. This function returns the unified count
516
- * across all of them, so the dashboard, landing page, and homepage badge
517
- * always cite a coherent number.
518
- *
519
- * - Tier 1 (Discovery): the indexed registry — searchable, free, no auth
520
- * - Tier 2 (Open API): callable without keys, free
521
- * - Tier 3 (Direct Call): premium, APIClaw owns the keys
522
- *
523
- * The Direct Call count is intentionally a constant here. It's small,
524
- * changes rarely, and lives canonically in the Apiclaw MOC. If it ever
525
- * grows past a handful, replace this constant with a Convex query.
526
- */
527
- export const DIRECT_CALL_PROVIDERS = 19; // mirrors Apiclaw MOC: Tier 3
528
- export const DIRECT_CALL_SUB_APIS = 27; // APILayer sub-APIs under one provider
529
-
530
- export function getAPIClawTotalStats(opts: { registryIndexed?: number } = {}): {
531
- tier1_discovery_indexed: number;
532
- tier2_openapi_curated: number;
533
- tier2_openapi_generated: number;
534
- tier2_openapi_generated_callable: number;
535
- tier2_openapi_callable_total: number;
536
- tier3_direct_call_providers: number;
537
- tier3_direct_call_sub_apis: number;
538
- total_callable: number;
539
- total_indexed_or_callable: number;
540
- generatedArtifactAt: number;
541
- } {
542
- const stats = getOpenAPIStats();
543
- const registryIndexed = opts.registryIndexed ?? 22393; // canonical from src/registry/apis.json
544
- const tier2Callable = stats.curatedProviders + stats.generatedCallable;
545
- const tier3 = DIRECT_CALL_PROVIDERS + DIRECT_CALL_SUB_APIS;
546
- return {
547
- tier1_discovery_indexed: registryIndexed,
548
- tier2_openapi_curated: stats.curatedProviders,
549
- tier2_openapi_generated: stats.generatedProviders,
550
- tier2_openapi_generated_callable: stats.generatedCallable,
551
- tier2_openapi_callable_total: tier2Callable,
552
- tier3_direct_call_providers: DIRECT_CALL_PROVIDERS,
553
- tier3_direct_call_sub_apis: DIRECT_CALL_SUB_APIS,
554
- total_callable: tier2Callable + tier3,
555
- total_indexed_or_callable: registryIndexed + stats.generatedProviders + tier3,
556
- generatedArtifactAt: stats.generatedAt,
557
- };
558
- }