@nordsym/apiclaw 2.2.0 → 2.3.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 (176) 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/gateway-client.d.ts.map +1 -1
  5. package/dist/gateway-client.js +24 -2
  6. package/dist/gateway-client.js.map +1 -1
  7. package/dist/index.bundled.js +61263 -0
  8. package/dist/index.js +2 -2
  9. package/dist/index.js.map +1 -1
  10. package/package.json +7 -2
  11. package/.claude/settings.local.json +0 -13
  12. package/.env.prod +0 -1
  13. package/apiclaw-README.md +0 -494
  14. package/convex/_generated/api.d.ts +0 -145
  15. package/convex/_generated/api.js +0 -23
  16. package/convex/_generated/dataModel.d.ts +0 -60
  17. package/convex/_generated/server.d.ts +0 -143
  18. package/convex/_generated/server.js +0 -93
  19. package/convex/_listWorkspaces.ts +0 -13
  20. package/convex/adminActivate.ts +0 -53
  21. package/convex/adminStats.ts +0 -306
  22. package/convex/agents.ts +0 -939
  23. package/convex/analytics.ts +0 -187
  24. package/convex/apiKeys.ts +0 -220
  25. package/convex/backfillAnalytics.ts +0 -272
  26. package/convex/backfillSearchLogs.ts +0 -35
  27. package/convex/billing.ts +0 -834
  28. package/convex/capabilities.ts +0 -157
  29. package/convex/chains.ts +0 -1318
  30. package/convex/credits.ts +0 -211
  31. package/convex/crons.ts +0 -65
  32. package/convex/debugFilestackLogs.ts +0 -16
  33. package/convex/debugGetToken.ts +0 -18
  34. package/convex/directCall.ts +0 -713
  35. package/convex/earnProgress.ts +0 -753
  36. package/convex/email.ts +0 -329
  37. package/convex/feedback.ts +0 -265
  38. package/convex/funnel.ts +0 -431
  39. package/convex/guards.ts +0 -174
  40. package/convex/http.ts +0 -3756
  41. package/convex/inbound.ts +0 -32
  42. package/convex/logs.ts +0 -701
  43. package/convex/migrateFilestack.ts +0 -81
  44. package/convex/migratePartnersProd.ts +0 -174
  45. package/convex/migratePratham.ts +0 -126
  46. package/convex/migrateProviderWorkspaces.ts +0 -175
  47. package/convex/mou.ts +0 -91
  48. package/convex/nurture.ts +0 -355
  49. package/convex/providerKeys.ts +0 -289
  50. package/convex/providers.ts +0 -1135
  51. package/convex/purchases.ts +0 -183
  52. package/convex/ratelimit.ts +0 -104
  53. package/convex/schema.ts +0 -926
  54. package/convex/searchLogs.ts +0 -265
  55. package/convex/seedAPILayerAPIs.ts +0 -191
  56. package/convex/seedDirectCallConfigs.ts +0 -336
  57. package/convex/seedPratham.ts +0 -149
  58. package/convex/spendAlerts.ts +0 -442
  59. package/convex/stripeActions.ts +0 -607
  60. package/convex/teams.ts +0 -243
  61. package/convex/telemetry.ts +0 -81
  62. package/convex/tsconfig.json +0 -25
  63. package/convex/updateAPIStatus.ts +0 -44
  64. package/convex/usage.ts +0 -260
  65. package/convex/usageReports.ts +0 -357
  66. package/convex/waitlist.ts +0 -55
  67. package/convex/webhooks.ts +0 -494
  68. package/convex/workspaceSettings.ts +0 -143
  69. package/convex/workspaces.ts +0 -1331
  70. package/convex.json +0 -3
  71. package/direct-test.mjs +0 -51
  72. package/email-templates/filestack-provider-outreach.html +0 -162
  73. package/email-templates/partnership-template.html +0 -116
  74. package/email-templates/pratham-draft-preview.txt +0 -57
  75. package/email-templates/pratham-partnership-draft.html +0 -141
  76. package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
  77. package/reports/pipeline/PIPELINE-REPORT.json +0 -153
  78. package/reports/pipeline/acquire_apisguru.json +0 -17
  79. package/reports/pipeline/capabilities.json +0 -38
  80. package/reports/pipeline/discover_azure_recursive.json +0 -1551
  81. package/reports/pipeline/discover_github.json +0 -25
  82. package/reports/pipeline/discover_github_repos.json +0 -49
  83. package/reports/pipeline/discover_swaggerhub.json +0 -24
  84. package/reports/pipeline/discover_well_known.json +0 -23
  85. package/reports/pipeline/fetch_specs.json +0 -19
  86. package/reports/pipeline/generate_providers.json +0 -14
  87. package/reports/pipeline/match_registry.json +0 -11
  88. package/reports/pipeline/parse_specs.json +0 -17
  89. package/reports/pipeline/promote_candidates.json +0 -34
  90. package/reports/pipeline/validate.json +0 -30
  91. package/reports/pipeline/validate_smoke_details.json +0 -3835
  92. package/reports/session-report-2026-04-05.html +0 -433
  93. package/seed-apis-direct.mjs +0 -106
  94. package/src/access-control.ts +0 -174
  95. package/src/adapters/base.ts +0 -364
  96. package/src/adapters/claude-desktop.ts +0 -41
  97. package/src/adapters/cline.ts +0 -88
  98. package/src/adapters/continue.ts +0 -91
  99. package/src/adapters/cursor.ts +0 -43
  100. package/src/adapters/custom.ts +0 -188
  101. package/src/adapters/detect.ts +0 -202
  102. package/src/adapters/index.ts +0 -47
  103. package/src/adapters/windsurf.ts +0 -44
  104. package/src/bin-http.ts +0 -45
  105. package/src/bin.ts +0 -34
  106. package/src/capability-router.ts +0 -331
  107. package/src/chainExecutor.ts +0 -730
  108. package/src/chainResolver.test.ts +0 -246
  109. package/src/chainResolver.ts +0 -658
  110. package/src/cli/commands/demo.ts +0 -109
  111. package/src/cli/commands/doctor.ts +0 -435
  112. package/src/cli/commands/index.ts +0 -9
  113. package/src/cli/commands/login.ts +0 -203
  114. package/src/cli/commands/mcp-install.ts +0 -373
  115. package/src/cli/commands/restore.ts +0 -333
  116. package/src/cli/commands/setup.ts +0 -297
  117. package/src/cli/commands/uninstall.ts +0 -240
  118. package/src/cli/index.ts +0 -148
  119. package/src/cli.ts +0 -370
  120. package/src/confirmation.ts +0 -296
  121. package/src/credentials.ts +0 -455
  122. package/src/credits.ts +0 -329
  123. package/src/crypto.ts +0 -75
  124. package/src/discovery.ts +0 -568
  125. package/src/enterprise/env.ts +0 -156
  126. package/src/enterprise/index.ts +0 -7
  127. package/src/enterprise/script-generator.ts +0 -481
  128. package/src/execute-dynamic.ts +0 -617
  129. package/src/execute.ts +0 -2386
  130. package/src/funnel-client.ts +0 -168
  131. package/src/funnel.test.ts +0 -187
  132. package/src/gateway-client.ts +0 -192
  133. package/src/hivr-whitelist.ts +0 -110
  134. package/src/http-api.ts +0 -286
  135. package/src/http-server-minimal.ts +0 -154
  136. package/src/index.ts +0 -2702
  137. package/src/intelligent-gateway.ts +0 -339
  138. package/src/mcp-analytics.ts +0 -156
  139. package/src/metered.ts +0 -149
  140. package/src/open-apis-generated.ts +0 -157
  141. package/src/open-apis.ts +0 -558
  142. package/src/postinstall.ts +0 -40
  143. package/src/product-whitelist.ts +0 -246
  144. package/src/proxy.ts +0 -36
  145. package/src/registration-guard.ts +0 -117
  146. package/src/session.ts +0 -129
  147. package/src/stripe.ts +0 -497
  148. package/src/telemetry.ts +0 -71
  149. package/src/test.ts +0 -135
  150. package/src/types/convex-api.d.ts +0 -20
  151. package/src/types/convex-api.ts +0 -21
  152. package/src/types.ts +0 -109
  153. package/src/ui/colors.ts +0 -219
  154. package/src/ui/errors.ts +0 -394
  155. package/src/ui/index.ts +0 -17
  156. package/src/ui/prompts.ts +0 -390
  157. package/src/ui/spinner.ts +0 -325
  158. package/src/utils/backup.ts +0 -224
  159. package/src/utils/config.ts +0 -318
  160. package/src/utils/os.ts +0 -124
  161. package/src/utils/paths.ts +0 -203
  162. package/src/webhook.ts +0 -107
  163. package/test-10-working.cjs +0 -97
  164. package/test-14-final.cjs +0 -96
  165. package/test-actual-handlers.ts +0 -92
  166. package/test-apilayer-all-14.ts +0 -249
  167. package/test-apilayer-fixed.ts +0 -248
  168. package/test-direct-endpoints.ts +0 -174
  169. package/test-exact-endpoints.ts +0 -144
  170. package/test-final.ts +0 -83
  171. package/test-full-routing.ts +0 -100
  172. package/test-handlers-correct.ts +0 -217
  173. package/test-numverify-key.ts +0 -41
  174. package/test-via-handlers.ts +0 -92
  175. package/test-worldnews.mjs +0 -26
  176. 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
- }