@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,192 +0,0 @@
1
- /**
2
- * APIClaw Gateway Client - Thin HTTP client for the Intelligent Gateway
3
- *
4
- * Routes all API execution through POST /v1/execute on Convex,
5
- * replacing local executeOpenAPI / executeMetered calls.
6
- */
7
-
8
- import { readFileSync } from 'fs';
9
- import { join } from 'path';
10
- import { homedir } from 'os';
11
-
12
- // ---------------------------------------------------------------------------
13
- // Types
14
- // ---------------------------------------------------------------------------
15
-
16
- export interface GatewayResponse {
17
- success: boolean;
18
- provider: string;
19
- action: string;
20
- data?: any;
21
- error?: string;
22
- cost?: number;
23
- _apiclaw?: {
24
- latencyMs: number;
25
- route: string;
26
- gateway: boolean;
27
- model?: string;
28
- };
29
- }
30
-
31
- export interface GatewayExecuteOptions {
32
- workspaceId?: string;
33
- stream?: boolean;
34
- routeOverride?: string;
35
- }
36
-
37
- // ---------------------------------------------------------------------------
38
- // Secret resolution
39
- // ---------------------------------------------------------------------------
40
-
41
- function resolveInternalSecret(): string {
42
- // 1. Environment variable
43
- const envSecret = process.env.APICLAW_INTERNAL_SECRET;
44
- if (envSecret) return envSecret;
45
-
46
- // 2. File on disk
47
- try {
48
- const secretPath = join(homedir(), '.secrets', 'apiclaw-internal-secret');
49
- return readFileSync(secretPath, 'utf-8').trim();
50
- } catch {
51
- // File doesn't exist or unreadable
52
- }
53
-
54
- // 3. Fallback (will fail auth, which is correct)
55
- return '';
56
- }
57
-
58
- // ---------------------------------------------------------------------------
59
- // Feature flag
60
- // ---------------------------------------------------------------------------
61
-
62
- /**
63
- * Returns true if gateway routing is enabled.
64
- * Default: enabled. Set APICLAW_USE_GATEWAY=false to disable.
65
- */
66
- export function isGatewayEnabled(): boolean {
67
- const flag = process.env.APICLAW_USE_GATEWAY;
68
- if (flag === undefined || flag === '') return true; // default on
69
- return flag.toLowerCase() !== 'false';
70
- }
71
-
72
- // ---------------------------------------------------------------------------
73
- // Client
74
- // ---------------------------------------------------------------------------
75
-
76
- const GATEWAY_BASE = 'https://adventurous-avocet-799.convex.site';
77
- const GATEWAY_TIMEOUT_MS = 30_000;
78
-
79
- export class GatewayClient {
80
- private baseUrl: string;
81
- private internalSecret: string;
82
-
83
- constructor() {
84
- this.baseUrl = GATEWAY_BASE;
85
- this.internalSecret = resolveInternalSecret();
86
- }
87
-
88
- async execute(
89
- provider: string,
90
- action: string,
91
- params: Record<string, any>,
92
- options?: GatewayExecuteOptions,
93
- ): Promise<GatewayResponse> {
94
- const url = `${this.baseUrl}/v1/execute`;
95
-
96
- const headers: Record<string, string> = {
97
- 'Content-Type': 'application/json',
98
- 'X-APIClaw-Internal': this.internalSecret,
99
- };
100
-
101
- if (options?.workspaceId) {
102
- headers['X-APIClaw-Workspace'] = options.workspaceId;
103
- }
104
- if (options?.routeOverride) {
105
- headers['X-APIClaw-Route'] = options.routeOverride;
106
- }
107
-
108
- const body = JSON.stringify({ provider, action, params });
109
-
110
- // First attempt
111
- let response = await this.doFetch(url, headers, body);
112
-
113
- // Retry once on network error
114
- if (!response) {
115
- response = await this.doFetch(url, headers, body);
116
- }
117
-
118
- if (!response) {
119
- return {
120
- success: false,
121
- provider,
122
- action,
123
- error: 'Gateway unreachable after retry',
124
- };
125
- }
126
-
127
- try {
128
- const json = await response.json() as any;
129
-
130
- if (!response.ok) {
131
- return {
132
- success: false,
133
- provider: json.provider || provider,
134
- action: json.action || action,
135
- error: json.error || `Gateway HTTP ${response.status}`,
136
- _apiclaw: json._apiclaw,
137
- };
138
- }
139
-
140
- return {
141
- success: json.success ?? true,
142
- provider: json.provider || provider,
143
- action: json.action || action,
144
- data: json.data,
145
- error: json.error,
146
- cost: json.cost,
147
- _apiclaw: json._apiclaw,
148
- };
149
- } catch (e: any) {
150
- return {
151
- success: false,
152
- provider,
153
- action,
154
- error: `Gateway response parse error: ${e.message}`,
155
- };
156
- }
157
- }
158
-
159
- /**
160
- * Perform a single fetch with timeout. Returns null on network error.
161
- */
162
- private async doFetch(
163
- url: string,
164
- headers: Record<string, string>,
165
- body: string,
166
- ): Promise<Response | null> {
167
- try {
168
- const controller = new AbortController();
169
- const timer = setTimeout(() => controller.abort(), GATEWAY_TIMEOUT_MS);
170
-
171
- const response = await fetch(url, {
172
- method: 'POST',
173
- headers,
174
- body,
175
- signal: controller.signal,
176
- });
177
-
178
- clearTimeout(timer);
179
- return response;
180
- } catch {
181
- return null;
182
- }
183
- }
184
- }
185
-
186
- // Singleton
187
- let _gateway: GatewayClient | null = null;
188
-
189
- export function getGateway(): GatewayClient {
190
- if (!_gateway) _gateway = new GatewayClient();
191
- return _gateway;
192
- }
@@ -1,110 +0,0 @@
1
- /**
2
- * Hivr Bees Auto-Whitelist
3
- * Dynamically fetches active agents from Hivr's Convex deployment
4
- * Falls back to static whitelist if Convex is unreachable
5
- */
6
-
7
- // Hivr PROD Convex deployment
8
- const HIVR_CONVEX_URL = "https://brilliant-puffin-712.eu-west-1.convex.cloud";
9
-
10
- // Fallback static whitelist (in case Convex is down)
11
- const STATIC_WHITELIST = [
12
- 'bytebee',
13
- 'analyzerbee',
14
- 'buildbee',
15
- 'buzzwriter',
16
- 'hivemind',
17
- 'hivesage',
18
- 'symbot',
19
- 'hivrqueen',
20
- 'marketmaven',
21
- 'reconbee',
22
- 'sprintbee',
23
- 'quillbee',
24
- ];
25
-
26
- // Cache whitelist for 5 minutes
27
- let cachedWhitelist: string[] | null = null;
28
- let cacheExpiry: number = 0;
29
-
30
- /**
31
- * Fetch all active agents from Hivr Convex
32
- */
33
- async function fetchHivrAgents(): Promise<string[]> {
34
- try {
35
- // Call Convex HTTP API directly
36
- const response = await fetch(`${HIVR_CONVEX_URL}/api/query`, {
37
- method: 'POST',
38
- headers: {
39
- 'Content-Type': 'application/json',
40
- },
41
- body: JSON.stringify({
42
- path: 'agents:list',
43
- args: {},
44
- }),
45
- });
46
-
47
- if (!response.ok) {
48
- console.warn('[Hivr Whitelist] Convex HTTP API error, using static whitelist');
49
- return STATIC_WHITELIST;
50
- }
51
-
52
- const agents = await response.json() as any[];
53
-
54
- if (!agents || !Array.isArray(agents)) {
55
- console.warn('[Hivr Whitelist] Invalid response from Hivr Convex, using static whitelist');
56
- return STATIC_WHITELIST;
57
- }
58
-
59
- // Extract handles (Hivr uses 'handle', not 'agentId')
60
- const handles = agents
61
- .map((a: any) => a.handle?.toLowerCase().trim())
62
- .filter((h: string | undefined) => h && h.length > 0);
63
-
64
- console.log(`[Hivr Whitelist] Fetched ${handles.length} agents from Hivr`);
65
- return handles;
66
-
67
- } catch (error) {
68
- console.error('[Hivr Whitelist] Failed to fetch from Hivr Convex:', error);
69
- return STATIC_WHITELIST;
70
- }
71
- }
72
-
73
- /**
74
- * Get current whitelist (cached or fresh)
75
- */
76
- export async function getWhitelist(): Promise<string[]> {
77
- const now = Date.now();
78
-
79
- // Return cached if still valid
80
- if (cachedWhitelist && now < cacheExpiry) {
81
- return cachedWhitelist;
82
- }
83
-
84
- // Fetch fresh whitelist
85
- cachedWhitelist = await fetchHivrAgents();
86
- cacheExpiry = now + (5 * 60 * 1000); // 5 minutes
87
-
88
- return cachedWhitelist;
89
- }
90
-
91
- /**
92
- * Check if agent is authorized
93
- */
94
- export async function isAuthorized(agentId: string | undefined): Promise<boolean> {
95
- if (!agentId) return false;
96
-
97
- const whitelist = await getWhitelist();
98
- const normalized = agentId.toLowerCase().trim();
99
-
100
- return whitelist.includes(normalized);
101
- }
102
-
103
- /**
104
- * Force refresh whitelist (call after adding new bee)
105
- */
106
- export function invalidateCache(): void {
107
- cachedWhitelist = null;
108
- cacheExpiry = 0;
109
- console.log('[Hivr Whitelist] Cache invalidated');
110
- }
package/src/http-api.ts DELETED
@@ -1,286 +0,0 @@
1
- /**
2
- * APIClaw HTTP API Server
3
- * Provides REST endpoints for headless agents (Hivr bees, webhooks, etc)
4
- *
5
- * Endpoints:
6
- * - GET /api/discover?query=...&agentId=...
7
- * - POST /api/call_api { provider, action, params, agentId }
8
- * - GET /health
9
- *
10
- * Auth: Whitelist-based for Hivr bees
11
- */
12
-
13
- import { createServer, IncomingMessage, ServerResponse } from 'http';
14
- import { URL } from 'url';
15
- import { discoverAPIs } from './discovery.js';
16
- import { isOpenAPI, executeOpenAPI } from './open-apis.js';
17
- import { executeMetered } from './metered.js';
18
- import { logAPICall } from './mcp-analytics.js';
19
- import { getMachineFingerprint } from './session.js';
20
- import { isAuthorized, getProduct } from './product-whitelist.js';
21
-
22
- interface APIRequest {
23
- provider: string;
24
- action: string;
25
- params: Record<string, any>;
26
- agentId: string;
27
- }
28
-
29
- /**
30
- * Parse JSON body from request
31
- */
32
- async function parseBody<T>(req: IncomingMessage): Promise<T> {
33
- return new Promise((resolve, reject) => {
34
- let body = '';
35
- req.on('data', chunk => body += chunk.toString());
36
- req.on('end', () => {
37
- try {
38
- resolve(JSON.parse(body));
39
- } catch (e) {
40
- reject(new Error('Invalid JSON'));
41
- }
42
- });
43
- req.on('error', reject);
44
- });
45
- }
46
-
47
- /**
48
- * Send JSON response
49
- */
50
- function sendJSON(res: ServerResponse, status: number, data: any): void {
51
- res.writeHead(status, {
52
- 'Content-Type': 'application/json',
53
- 'Access-Control-Allow-Origin': '*',
54
- 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
55
- 'Access-Control-Allow-Headers': 'Content-Type, X-Agent-Id',
56
- });
57
- res.end(JSON.stringify(data));
58
- }
59
-
60
- /**
61
- * Handle /api/discover
62
- * GET /api/discover?query=web+search&agentId=bytebee&category=Search&maxResults=5
63
- */
64
- async function handleDiscover(req: IncomingMessage, res: ServerResponse, url: URL): Promise<void> {
65
- const query = url.searchParams.get('query');
66
- const agentId = url.searchParams.get('agentId');
67
- const category = url.searchParams.get('category') || undefined;
68
- const maxResults = parseInt(url.searchParams.get('maxResults') || '5');
69
-
70
- if (!query) {
71
- sendJSON(res, 400, { error: 'Missing query parameter' });
72
- return;
73
- }
74
-
75
- if (!(await isAuthorized(agentId || undefined))) {
76
- sendJSON(res, 403, {
77
- error: 'Unauthorized',
78
- message: 'This endpoint is restricted to Hivr bees. Contact admin@nordsym.com for access.',
79
- });
80
- return;
81
- }
82
-
83
- const startTime = Date.now();
84
- const results = discoverAPIs(query, { category, maxResults });
85
- const responseTimeMs = Date.now() - startTime;
86
-
87
- // Log to analytics with product info
88
- const product = agentId ? getProduct(agentId) : null;
89
- logAPICall({
90
- timestamp: new Date().toISOString(),
91
- provider: 'apiclaw_discovery',
92
- action: 'discover',
93
- type: 'open',
94
- userId: agentId || 'unknown',
95
- success: true,
96
- latencyMs: responseTimeMs,
97
- metadata: product ? { product } : undefined,
98
- });
99
-
100
- sendJSON(res, 200, {
101
- success: true,
102
- query,
103
- results: results.map(r => ({
104
- provider: r.provider,
105
- score: r.relevance_score,
106
- reasons: r.match_reasons,
107
- })),
108
- count: results.length,
109
- responseTimeMs,
110
- });
111
- }
112
-
113
- /**
114
- * Handle /api/call_api
115
- * POST /api/call_api
116
- * Body: { provider: "brave_search", action: "search", params: { query: "AI news" }, agentId: "bytebee" }
117
- */
118
- async function handleCallAPI(req: IncomingMessage, res: ServerResponse): Promise<void> {
119
- let body: APIRequest;
120
-
121
- try {
122
- body = await parseBody<APIRequest>(req);
123
- } catch (e) {
124
- sendJSON(res, 400, { error: 'Invalid JSON body' });
125
- return;
126
- }
127
-
128
- const { provider, action, params, agentId } = body;
129
-
130
- if (!provider || !action || !params || !agentId) {
131
- sendJSON(res, 400, {
132
- error: 'Missing required fields',
133
- required: ['provider', 'action', 'params', 'agentId']
134
- });
135
- return;
136
- }
137
-
138
- // Check whitelist + access control
139
- const { isAllowed } = await import('./access-control.js');
140
- const accessCheck = await isAllowed(agentId, provider);
141
-
142
- if (!accessCheck.allowed) {
143
- sendJSON(res, 403, {
144
- error: 'Access Denied',
145
- message: accessCheck.reason || 'Not authorized',
146
- hint: 'Contact admin@nordsym.com for access',
147
- });
148
- return;
149
- }
150
-
151
- const startTime = Date.now();
152
- let result: any;
153
- let apiType: 'open' | 'direct';
154
- let success = true;
155
- let error: string | undefined;
156
-
157
- try {
158
- if (isOpenAPI(provider)) {
159
- apiType = 'open';
160
- result = await executeOpenAPI(provider, action, params);
161
- success = result.success;
162
- error = result.error;
163
- } else {
164
- apiType = 'direct';
165
- // For Direct Call APIs, use Hivr's workspace/credentials
166
- // TODO: Get Hivr workspace token from env or config
167
- const customerKey = process.env.APICLAW_HIVR_CUSTOMER_KEY;
168
- const stripeCustomerId = process.env.APICLAW_HIVR_STRIPE_CUSTOMER;
169
-
170
- result = await executeMetered(provider, action, params, {
171
- customerId: stripeCustomerId,
172
- customerKey,
173
- userId: `hivr:${agentId}`,
174
- });
175
- success = result.success;
176
- error = result.error;
177
- }
178
- } catch (e: any) {
179
- success = false;
180
- error = e.message;
181
- result = { success: false, error: error };
182
- }
183
-
184
- const latencyMs = Date.now() - startTime;
185
-
186
- // Log to analytics with product info
187
- const product = getProduct(agentId);
188
- logAPICall({
189
- timestamp: new Date().toISOString(),
190
- provider,
191
- action,
192
- type: apiType!,
193
- userId: agentId,
194
- success,
195
- latencyMs,
196
- error,
197
- metadata: product ? { product } : undefined,
198
- });
199
-
200
- sendJSON(res, success ? 200 : 500, {
201
- success,
202
- provider,
203
- action,
204
- agentId,
205
- data: result.data,
206
- error: result.error,
207
- latencyMs,
208
- });
209
- }
210
-
211
- /**
212
- * Handle OPTIONS (CORS preflight)
213
- */
214
- function handleOptions(res: ServerResponse): void {
215
- res.writeHead(204, {
216
- 'Access-Control-Allow-Origin': '*',
217
- 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
218
- 'Access-Control-Allow-Headers': 'Content-Type, X-Agent-Id',
219
- 'Access-Control-Max-Age': '86400',
220
- });
221
- res.end();
222
- }
223
-
224
- /**
225
- * Main request handler
226
- */
227
- async function handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
228
- const url = new URL(req.url || '/', `http://${req.headers.host}`);
229
-
230
- console.log(`[APIClaw HTTP] ${req.method} ${url.pathname}`);
231
-
232
- // CORS preflight
233
- if (req.method === 'OPTIONS') {
234
- handleOptions(res);
235
- return;
236
- }
237
-
238
- // Health check
239
- if (url.pathname === '/health') {
240
- sendJSON(res, 200, { status: 'ok', service: 'apiclaw-http-api' });
241
- return;
242
- }
243
-
244
- // Route requests
245
- if (url.pathname === '/api/discover' && req.method === 'GET') {
246
- await handleDiscover(req, res, url);
247
- return;
248
- }
249
-
250
- if (url.pathname === '/api/call_api' && req.method === 'POST') {
251
- await handleCallAPI(req, res);
252
- return;
253
- }
254
-
255
- // 404
256
- sendJSON(res, 404, { error: 'Not found' });
257
- }
258
-
259
- /**
260
- * Start HTTP server
261
- */
262
- export function startHTTPServer(port: number = 3000): void {
263
- const server = createServer(async (req, res) => {
264
- try {
265
- await handleRequest(req, res);
266
- } catch (error: any) {
267
- console.error('[APIClaw HTTP] Error:', error);
268
- sendJSON(res, 500, { error: 'Internal server error', message: error.message });
269
- }
270
- });
271
-
272
- server.listen(port, () => {
273
- console.log(`\nšŸ¦ž APIClaw HTTP API running on http://localhost:${port}`);
274
- console.log(` GET /api/discover?query=...&agentId=...`);
275
- console.log(` POST /api/call_api`);
276
- console.log(` GET /health\n`);
277
- });
278
-
279
- server.on('error', (error: any) => {
280
- if (error.code === 'EADDRINUSE') {
281
- console.error(`[APIClaw HTTP] Port ${port} is already in use`);
282
- } else {
283
- console.error('[APIClaw HTTP] Server error:', error);
284
- }
285
- });
286
- }