@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.
- package/README.md +15 -2
- package/dist/bin-http.js +0 -0
- package/dist/bin.bundled.js +79288 -0
- package/dist/gateway-client.d.ts.map +1 -1
- package/dist/gateway-client.js +24 -2
- package/dist/gateway-client.js.map +1 -1
- package/dist/index.bundled.js +61263 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/.claude/settings.local.json +0 -13
- package/.env.prod +0 -1
- package/apiclaw-README.md +0 -494
- package/convex/_generated/api.d.ts +0 -145
- package/convex/_generated/api.js +0 -23
- package/convex/_generated/dataModel.d.ts +0 -60
- package/convex/_generated/server.d.ts +0 -143
- package/convex/_generated/server.js +0 -93
- package/convex/_listWorkspaces.ts +0 -13
- package/convex/adminActivate.ts +0 -53
- package/convex/adminStats.ts +0 -306
- package/convex/agents.ts +0 -939
- package/convex/analytics.ts +0 -187
- package/convex/apiKeys.ts +0 -220
- package/convex/backfillAnalytics.ts +0 -272
- package/convex/backfillSearchLogs.ts +0 -35
- package/convex/billing.ts +0 -834
- package/convex/capabilities.ts +0 -157
- package/convex/chains.ts +0 -1318
- package/convex/credits.ts +0 -211
- package/convex/crons.ts +0 -65
- package/convex/debugFilestackLogs.ts +0 -16
- package/convex/debugGetToken.ts +0 -18
- package/convex/directCall.ts +0 -713
- package/convex/earnProgress.ts +0 -753
- package/convex/email.ts +0 -329
- package/convex/feedback.ts +0 -265
- package/convex/funnel.ts +0 -431
- package/convex/guards.ts +0 -174
- package/convex/http.ts +0 -3756
- package/convex/inbound.ts +0 -32
- package/convex/logs.ts +0 -701
- package/convex/migrateFilestack.ts +0 -81
- package/convex/migratePartnersProd.ts +0 -174
- package/convex/migratePratham.ts +0 -126
- package/convex/migrateProviderWorkspaces.ts +0 -175
- package/convex/mou.ts +0 -91
- package/convex/nurture.ts +0 -355
- package/convex/providerKeys.ts +0 -289
- package/convex/providers.ts +0 -1135
- package/convex/purchases.ts +0 -183
- package/convex/ratelimit.ts +0 -104
- package/convex/schema.ts +0 -926
- package/convex/searchLogs.ts +0 -265
- package/convex/seedAPILayerAPIs.ts +0 -191
- package/convex/seedDirectCallConfigs.ts +0 -336
- package/convex/seedPratham.ts +0 -149
- package/convex/spendAlerts.ts +0 -442
- package/convex/stripeActions.ts +0 -607
- package/convex/teams.ts +0 -243
- package/convex/telemetry.ts +0 -81
- package/convex/tsconfig.json +0 -25
- package/convex/updateAPIStatus.ts +0 -44
- package/convex/usage.ts +0 -260
- package/convex/usageReports.ts +0 -357
- package/convex/waitlist.ts +0 -55
- package/convex/webhooks.ts +0 -494
- package/convex/workspaceSettings.ts +0 -143
- package/convex/workspaces.ts +0 -1331
- package/convex.json +0 -3
- package/direct-test.mjs +0 -51
- package/email-templates/filestack-provider-outreach.html +0 -162
- package/email-templates/partnership-template.html +0 -116
- package/email-templates/pratham-draft-preview.txt +0 -57
- package/email-templates/pratham-partnership-draft.html +0 -141
- package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
- package/reports/pipeline/PIPELINE-REPORT.json +0 -153
- package/reports/pipeline/acquire_apisguru.json +0 -17
- package/reports/pipeline/capabilities.json +0 -38
- package/reports/pipeline/discover_azure_recursive.json +0 -1551
- package/reports/pipeline/discover_github.json +0 -25
- package/reports/pipeline/discover_github_repos.json +0 -49
- package/reports/pipeline/discover_swaggerhub.json +0 -24
- package/reports/pipeline/discover_well_known.json +0 -23
- package/reports/pipeline/fetch_specs.json +0 -19
- package/reports/pipeline/generate_providers.json +0 -14
- package/reports/pipeline/match_registry.json +0 -11
- package/reports/pipeline/parse_specs.json +0 -17
- package/reports/pipeline/promote_candidates.json +0 -34
- package/reports/pipeline/validate.json +0 -30
- package/reports/pipeline/validate_smoke_details.json +0 -3835
- package/reports/session-report-2026-04-05.html +0 -433
- package/seed-apis-direct.mjs +0 -106
- package/src/access-control.ts +0 -174
- package/src/adapters/base.ts +0 -364
- package/src/adapters/claude-desktop.ts +0 -41
- package/src/adapters/cline.ts +0 -88
- package/src/adapters/continue.ts +0 -91
- package/src/adapters/cursor.ts +0 -43
- package/src/adapters/custom.ts +0 -188
- package/src/adapters/detect.ts +0 -202
- package/src/adapters/index.ts +0 -47
- package/src/adapters/windsurf.ts +0 -44
- package/src/bin-http.ts +0 -45
- package/src/bin.ts +0 -34
- package/src/capability-router.ts +0 -331
- package/src/chainExecutor.ts +0 -730
- package/src/chainResolver.test.ts +0 -246
- package/src/chainResolver.ts +0 -658
- package/src/cli/commands/demo.ts +0 -109
- package/src/cli/commands/doctor.ts +0 -435
- package/src/cli/commands/index.ts +0 -9
- package/src/cli/commands/login.ts +0 -203
- package/src/cli/commands/mcp-install.ts +0 -373
- package/src/cli/commands/restore.ts +0 -333
- package/src/cli/commands/setup.ts +0 -297
- package/src/cli/commands/uninstall.ts +0 -240
- package/src/cli/index.ts +0 -148
- package/src/cli.ts +0 -370
- package/src/confirmation.ts +0 -296
- package/src/credentials.ts +0 -455
- package/src/credits.ts +0 -329
- package/src/crypto.ts +0 -75
- package/src/discovery.ts +0 -568
- package/src/enterprise/env.ts +0 -156
- package/src/enterprise/index.ts +0 -7
- package/src/enterprise/script-generator.ts +0 -481
- package/src/execute-dynamic.ts +0 -617
- package/src/execute.ts +0 -2386
- package/src/funnel-client.ts +0 -168
- package/src/funnel.test.ts +0 -187
- package/src/gateway-client.ts +0 -192
- package/src/hivr-whitelist.ts +0 -110
- package/src/http-api.ts +0 -286
- package/src/http-server-minimal.ts +0 -154
- package/src/index.ts +0 -2702
- package/src/intelligent-gateway.ts +0 -339
- package/src/mcp-analytics.ts +0 -156
- package/src/metered.ts +0 -149
- package/src/open-apis-generated.ts +0 -157
- package/src/open-apis.ts +0 -558
- package/src/postinstall.ts +0 -40
- package/src/product-whitelist.ts +0 -246
- package/src/proxy.ts +0 -36
- package/src/registration-guard.ts +0 -117
- package/src/session.ts +0 -129
- package/src/stripe.ts +0 -497
- package/src/telemetry.ts +0 -71
- package/src/test.ts +0 -135
- package/src/types/convex-api.d.ts +0 -20
- package/src/types/convex-api.ts +0 -21
- package/src/types.ts +0 -109
- package/src/ui/colors.ts +0 -219
- package/src/ui/errors.ts +0 -394
- package/src/ui/index.ts +0 -17
- package/src/ui/prompts.ts +0 -390
- package/src/ui/spinner.ts +0 -325
- package/src/utils/backup.ts +0 -224
- package/src/utils/config.ts +0 -318
- package/src/utils/os.ts +0 -124
- package/src/utils/paths.ts +0 -203
- package/src/webhook.ts +0 -107
- package/test-10-working.cjs +0 -97
- package/test-14-final.cjs +0 -96
- package/test-actual-handlers.ts +0 -92
- package/test-apilayer-all-14.ts +0 -249
- package/test-apilayer-fixed.ts +0 -248
- package/test-direct-endpoints.ts +0 -174
- package/test-exact-endpoints.ts +0 -144
- package/test-final.ts +0 -83
- package/test-full-routing.ts +0 -100
- package/test-handlers-correct.ts +0 -217
- package/test-numverify-key.ts +0 -41
- package/test-via-handlers.ts +0 -92
- package/test-worldnews.mjs +0 -26
- package/tsconfig.json +0 -20
package/src/postinstall.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* APIClaw Postinstall Hook
|
|
4
|
-
* Prints a welcome message with links and fires ONE anonymous funnel
|
|
5
|
-
* event (install) so we can measure top-of-funnel truthfully.
|
|
6
|
-
*
|
|
7
|
-
* Disable telemetry: APICLAW_TELEMETRY=false
|
|
8
|
-
*/
|
|
9
|
-
import { emitFunnelEvent, hasLocalMarker, setLocalMarker } from './funnel-client.js';
|
|
10
|
-
import { getMachineFingerprint, detectMCPClient } from './session.js';
|
|
11
|
-
|
|
12
|
-
const CYAN = '\x1b[36m';
|
|
13
|
-
const RESET = '\x1b[0m';
|
|
14
|
-
const DIM = '\x1b[2m';
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
const fp = getMachineFingerprint();
|
|
18
|
-
const dedupeKey = `install:${fp}`;
|
|
19
|
-
if (!hasLocalMarker(dedupeKey)) {
|
|
20
|
-
emitFunnelEvent({
|
|
21
|
-
event: 'install',
|
|
22
|
-
fingerprint: fp,
|
|
23
|
-
mcpClient: detectMCPClient(),
|
|
24
|
-
platform: process.platform,
|
|
25
|
-
version: process.env.npm_package_version || 'unknown',
|
|
26
|
-
dedupeKey,
|
|
27
|
-
});
|
|
28
|
-
setLocalMarker(dedupeKey);
|
|
29
|
-
}
|
|
30
|
-
} catch {
|
|
31
|
-
/* never block install */
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
console.log('');
|
|
35
|
-
console.log(` 🦞 APIClaw installed successfully!`);
|
|
36
|
-
console.log('');
|
|
37
|
-
console.log(` → Sign in: ${CYAN}npx @nordsym/apiclaw login${RESET}`);
|
|
38
|
-
console.log(` → Full setup: ${CYAN}npx @nordsym/apiclaw setup${RESET} ${DIM}(auto-detects Claude, Cursor, Windsurf)${RESET}`);
|
|
39
|
-
console.log(` ⭐ Star us: ${CYAN}https://github.com/nordsym/apiclaw${RESET} ${DIM}(helps more devs find us)${RESET}`);
|
|
40
|
-
console.log('');
|
package/src/product-whitelist.ts
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Multi-Product Whitelist System
|
|
3
|
-
* Supports multiple products (Hivr, NordSym, partners) with namespaced agentIds
|
|
4
|
-
*
|
|
5
|
-
* Format: product:agentId
|
|
6
|
-
* Examples: hivr:bytebee, nordsym:mollebot, partner_x:agent1
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
interface ProductSource {
|
|
10
|
-
name: string;
|
|
11
|
-
convexUrl: string;
|
|
12
|
-
queryPath: string;
|
|
13
|
-
agentIdField: string;
|
|
14
|
-
authToken?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Product sources configuration
|
|
18
|
-
const PRODUCT_SOURCES: ProductSource[] = [
|
|
19
|
-
{
|
|
20
|
-
name: 'hivr',
|
|
21
|
-
convexUrl: 'https://brilliant-puffin-712.eu-west-1.convex.cloud',
|
|
22
|
-
queryPath: 'agents:list',
|
|
23
|
-
agentIdField: 'handle', // ✅ Fixed: Hivr agents use 'handle', not 'agentId'
|
|
24
|
-
},
|
|
25
|
-
// Add more products here as needed
|
|
26
|
-
// {
|
|
27
|
-
// name: 'nordsym',
|
|
28
|
-
// convexUrl: 'https://nordsym-deployment.convex.cloud',
|
|
29
|
-
// queryPath: 'team:listAgents',
|
|
30
|
-
// agentIdField: 'memberId',
|
|
31
|
-
// },
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
// Fallback static whitelist (emergency only)
|
|
35
|
-
const STATIC_WHITELIST = [
|
|
36
|
-
'hivr:bytebee',
|
|
37
|
-
'hivr:analyzerbee',
|
|
38
|
-
'hivr:buildbee',
|
|
39
|
-
'hivr:buzzwriter',
|
|
40
|
-
'hivr:hivemind',
|
|
41
|
-
'hivr:hivesage',
|
|
42
|
-
'hivr:symbot',
|
|
43
|
-
'hivr:hivrqueen',
|
|
44
|
-
'hivr:marketmaven',
|
|
45
|
-
'hivr:reconbee',
|
|
46
|
-
'hivr:sprintbee',
|
|
47
|
-
'hivr:quillbee',
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
// Cache per product (5 minutes TTL)
|
|
51
|
-
interface ProductCache {
|
|
52
|
-
agents: string[];
|
|
53
|
-
expiresAt: number;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const cache = new Map<string, ProductCache>();
|
|
57
|
-
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Fetch agents from a single product source
|
|
61
|
-
*/
|
|
62
|
-
async function fetchFromProduct(source: ProductSource): Promise<string[]> {
|
|
63
|
-
try {
|
|
64
|
-
const headers: Record<string, string> = {
|
|
65
|
-
'Content-Type': 'application/json',
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
if (source.authToken) {
|
|
69
|
-
headers['Authorization'] = `Bearer ${source.authToken}`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const response = await fetch(`${source.convexUrl}/api/query`, {
|
|
73
|
-
method: 'POST',
|
|
74
|
-
headers,
|
|
75
|
-
body: JSON.stringify({
|
|
76
|
-
path: source.queryPath,
|
|
77
|
-
args: {},
|
|
78
|
-
}),
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
if (!response.ok) {
|
|
82
|
-
console.warn(`[Whitelist] ${source.name}: HTTP ${response.status}`);
|
|
83
|
-
return [];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const result = await response.json() as any;
|
|
87
|
-
|
|
88
|
-
// Convex HTTP API returns { status: "success", value: [...] }
|
|
89
|
-
const data = result.value || result;
|
|
90
|
-
|
|
91
|
-
if (!Array.isArray(data)) {
|
|
92
|
-
console.warn(`[Whitelist] ${source.name}: Invalid response format`, typeof data);
|
|
93
|
-
return [];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Extract agentIds and add namespace
|
|
97
|
-
const agents = data
|
|
98
|
-
.map((item: any) => {
|
|
99
|
-
const agentId = item[source.agentIdField];
|
|
100
|
-
if (!agentId) return null;
|
|
101
|
-
return `${source.name}:${String(agentId).toLowerCase().trim()}`;
|
|
102
|
-
})
|
|
103
|
-
.filter((id): id is string => id !== null && id.length > 0);
|
|
104
|
-
|
|
105
|
-
console.log(`[Whitelist] ${source.name}: Fetched ${agents.length} agents`);
|
|
106
|
-
return agents;
|
|
107
|
-
|
|
108
|
-
} catch (error) {
|
|
109
|
-
console.error(`[Whitelist] ${source.name}: Fetch failed`, error);
|
|
110
|
-
return [];
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Fetch and merge agents from all product sources
|
|
116
|
-
*/
|
|
117
|
-
async function fetchAllProducts(): Promise<string[]> {
|
|
118
|
-
const results = await Promise.allSettled(
|
|
119
|
-
PRODUCT_SOURCES.map(source => fetchFromProduct(source))
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const allAgents: string[] = [];
|
|
123
|
-
|
|
124
|
-
for (const result of results) {
|
|
125
|
-
if (result.status === 'fulfilled') {
|
|
126
|
-
allAgents.push(...result.value);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// If no products returned data, use static fallback
|
|
131
|
-
if (allAgents.length === 0) {
|
|
132
|
-
console.warn('[Whitelist] All sources failed, using static fallback');
|
|
133
|
-
return STATIC_WHITELIST;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return allAgents;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Get current whitelist (cached or fresh)
|
|
141
|
-
*/
|
|
142
|
-
export async function getWhitelist(): Promise<string[]> {
|
|
143
|
-
const now = Date.now();
|
|
144
|
-
|
|
145
|
-
// Check if any cache entry is still valid
|
|
146
|
-
const validCaches: string[] = [];
|
|
147
|
-
for (const [product, cached] of cache.entries()) {
|
|
148
|
-
if (now < cached.expiresAt) {
|
|
149
|
-
validCaches.push(...cached.agents);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// If all caches valid, return merged
|
|
154
|
-
if (validCaches.length > 0 && cache.size === PRODUCT_SOURCES.length) {
|
|
155
|
-
return validCaches;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Fetch fresh data
|
|
159
|
-
const agents = await fetchAllProducts();
|
|
160
|
-
|
|
161
|
-
// Update cache per product
|
|
162
|
-
const agentsByProduct = new Map<string, string[]>();
|
|
163
|
-
for (const agent of agents) {
|
|
164
|
-
const [product] = agent.split(':');
|
|
165
|
-
if (!agentsByProduct.has(product)) {
|
|
166
|
-
agentsByProduct.set(product, []);
|
|
167
|
-
}
|
|
168
|
-
agentsByProduct.get(product)!.push(agent);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
for (const [product, productAgents] of agentsByProduct.entries()) {
|
|
172
|
-
cache.set(product, {
|
|
173
|
-
agents: productAgents,
|
|
174
|
-
expiresAt: now + CACHE_TTL,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return agents;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Check if agentId is authorized
|
|
183
|
-
* Supports both namespaced (product:agent) and legacy (agent) formats
|
|
184
|
-
*/
|
|
185
|
-
export async function isAuthorized(agentId: string | undefined): Promise<boolean> {
|
|
186
|
-
if (!agentId) return false;
|
|
187
|
-
|
|
188
|
-
const normalized = agentId.toLowerCase().trim();
|
|
189
|
-
const whitelist = await getWhitelist();
|
|
190
|
-
|
|
191
|
-
// Check exact match (namespaced)
|
|
192
|
-
if (whitelist.includes(normalized)) {
|
|
193
|
-
return true;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Legacy support: check if agentId matches any product's agent (without namespace)
|
|
197
|
-
// e.g., "bytebee" matches "hivr:bytebee"
|
|
198
|
-
if (!normalized.includes(':')) {
|
|
199
|
-
const legacyMatch = whitelist.some(entry => {
|
|
200
|
-
const [, agent] = entry.split(':');
|
|
201
|
-
return agent === normalized;
|
|
202
|
-
});
|
|
203
|
-
if (legacyMatch) {
|
|
204
|
-
console.log(`[Whitelist] Legacy match for ${normalized}`);
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Extract product name from agentId
|
|
214
|
-
*/
|
|
215
|
-
export function getProduct(agentId: string): string | null {
|
|
216
|
-
const [product] = agentId.split(':');
|
|
217
|
-
return product || null;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Force refresh whitelist (call after adding new agent)
|
|
222
|
-
*/
|
|
223
|
-
export function invalidateCache(product?: string): void {
|
|
224
|
-
if (product) {
|
|
225
|
-
cache.delete(product);
|
|
226
|
-
console.log(`[Whitelist] Cache invalidated for ${product}`);
|
|
227
|
-
} else {
|
|
228
|
-
cache.clear();
|
|
229
|
-
console.log('[Whitelist] All caches invalidated');
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Add new product source dynamically
|
|
235
|
-
*/
|
|
236
|
-
export function addProductSource(source: ProductSource): void {
|
|
237
|
-
const existing = PRODUCT_SOURCES.find(s => s.name === source.name);
|
|
238
|
-
if (existing) {
|
|
239
|
-
console.warn(`[Whitelist] Product ${source.name} already exists, updating`);
|
|
240
|
-
Object.assign(existing, source);
|
|
241
|
-
} else {
|
|
242
|
-
PRODUCT_SOURCES.push(source);
|
|
243
|
-
console.log(`[Whitelist] Added product source: ${source.name}`);
|
|
244
|
-
}
|
|
245
|
-
invalidateCache(source.name);
|
|
246
|
-
}
|
package/src/proxy.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* APIClaw Proxy - Fallback to hosted API when no local credentials
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { readSession, getMachineFingerprint } from './session.js';
|
|
6
|
-
|
|
7
|
-
const PROXY_BASE = "https://brilliant-puffin-712.eu-west-1.convex.site/proxy";
|
|
8
|
-
|
|
9
|
-
export async function callProxy(provider: string, params: any): Promise<any> {
|
|
10
|
-
const url = `${PROXY_BASE}/${provider}`;
|
|
11
|
-
|
|
12
|
-
// Get session and fingerprint for tracking
|
|
13
|
-
const session = readSession();
|
|
14
|
-
const fingerprint = getMachineFingerprint();
|
|
15
|
-
const identifier = session?.workspaceId || `anon:${fingerprint}`;
|
|
16
|
-
|
|
17
|
-
const response = await fetch(url, {
|
|
18
|
-
method: "POST",
|
|
19
|
-
headers: {
|
|
20
|
-
"Content-Type": "application/json",
|
|
21
|
-
"X-APIClaw-Identifier": identifier,
|
|
22
|
-
"X-APIClaw-Provider": provider,
|
|
23
|
-
"X-APIClaw-Action": params.action || "call",
|
|
24
|
-
},
|
|
25
|
-
body: JSON.stringify(params),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (!response.ok) {
|
|
29
|
-
const errorData = await response.json().catch(() => ({ error: "Proxy request failed" })) as { error?: string };
|
|
30
|
-
throw new Error(errorData.error || `Proxy error: ${response.status}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return response.json();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const PROXY_PROVIDERS = ["openrouter", "brave_search", "resend", "elevenlabs", "46elks", "twilio", "replicate", "firecrawl", "e2b", "groq", "deepgram", "serper", "mistral", "cohere", "together", "stability", "assemblyai", "github", "apilayer"];
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side registration guard — single source of truth for "does this
|
|
3
|
-
* workspaceContext pass the verified-owner check before an API call".
|
|
4
|
-
*
|
|
5
|
-
* Free paths (discover_apis, list_* , *_help) do NOT call this.
|
|
6
|
-
* Paying paths (call_api single + chain, capability, resume_chain) DO.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export interface WorkspaceContextLike {
|
|
10
|
-
sessionToken: string;
|
|
11
|
-
workspaceId: string;
|
|
12
|
-
email: string;
|
|
13
|
-
tier: string;
|
|
14
|
-
status: string;
|
|
15
|
-
usageRemaining: number;
|
|
16
|
-
usageCount: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type GuardResult =
|
|
20
|
-
| { ok: true; ctx: WorkspaceContextLike }
|
|
21
|
-
| { ok: false; reason: GuardReason; payload: Record<string, unknown> };
|
|
22
|
-
|
|
23
|
-
export type GuardReason =
|
|
24
|
-
| "no_session"
|
|
25
|
-
| "not_verified"
|
|
26
|
-
| "quota_exceeded"
|
|
27
|
-
| "pending_verification";
|
|
28
|
-
|
|
29
|
-
// Paths that are allowed without a verified owner.
|
|
30
|
-
export const FREE_CALL_PATHS = new Set<string>([
|
|
31
|
-
"discover_apis",
|
|
32
|
-
"list_categories",
|
|
33
|
-
"list_connected",
|
|
34
|
-
"list_capabilities",
|
|
35
|
-
"apiclaw_help",
|
|
36
|
-
"register_owner",
|
|
37
|
-
"verify_code",
|
|
38
|
-
"check_workspace_status",
|
|
39
|
-
"remind_owner",
|
|
40
|
-
"get_chain_status",
|
|
41
|
-
"setup_metered_billing",
|
|
42
|
-
"get_usage_summary",
|
|
43
|
-
"estimate_cost",
|
|
44
|
-
"check_balance",
|
|
45
|
-
"add_credits",
|
|
46
|
-
"get_api_details",
|
|
47
|
-
"purchase_access",
|
|
48
|
-
]);
|
|
49
|
-
|
|
50
|
-
// Paths that MUST go through requireVerifiedOwner.
|
|
51
|
-
export const ENFORCED_CALL_PATHS = new Set<string>([
|
|
52
|
-
"call_api",
|
|
53
|
-
"capability",
|
|
54
|
-
"resume_chain",
|
|
55
|
-
]);
|
|
56
|
-
|
|
57
|
-
export function requireVerifiedOwner(
|
|
58
|
-
workspaceContext: WorkspaceContextLike | null
|
|
59
|
-
): GuardResult {
|
|
60
|
-
if (!workspaceContext) {
|
|
61
|
-
return {
|
|
62
|
-
ok: false,
|
|
63
|
-
reason: "no_session",
|
|
64
|
-
payload: {
|
|
65
|
-
status: "registration_required",
|
|
66
|
-
error: "Registration required to call APIs.",
|
|
67
|
-
message:
|
|
68
|
-
"Ask the user for their email, then call register_owner({ email }). A 6-digit code will be sent. Then call verify_code with the code.",
|
|
69
|
-
action: "register_owner",
|
|
70
|
-
free_tier: "50 API calls/month -- completely free.",
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (!workspaceContext.email) {
|
|
76
|
-
return {
|
|
77
|
-
ok: false,
|
|
78
|
-
reason: "pending_verification",
|
|
79
|
-
payload: {
|
|
80
|
-
status: "registration_required",
|
|
81
|
-
error: "Workspace is not linked to a verified email yet.",
|
|
82
|
-
message: "Run register_owner({ email }) and verify_code to activate.",
|
|
83
|
-
action: "register_owner",
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (workspaceContext.status !== "active") {
|
|
89
|
-
return {
|
|
90
|
-
ok: false,
|
|
91
|
-
reason: "not_verified",
|
|
92
|
-
payload: {
|
|
93
|
-
status: "pending_verification",
|
|
94
|
-
error: `Workspace status: ${workspaceContext.status}. Please verify your email.`,
|
|
95
|
-
action: "verify_code",
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (workspaceContext.usageRemaining === 0) {
|
|
101
|
-
return {
|
|
102
|
-
ok: false,
|
|
103
|
-
reason: "quota_exceeded",
|
|
104
|
-
payload: {
|
|
105
|
-
status: "quota_exceeded",
|
|
106
|
-
error:
|
|
107
|
-
workspaceContext.tier === "free"
|
|
108
|
-
? "You've hit the free tier limit. Upgrade at https://apiclaw.cloud/upgrade."
|
|
109
|
-
: "Quota exceeded.",
|
|
110
|
-
upgrade_url: "https://apiclaw.cloud/upgrade",
|
|
111
|
-
action: "upgrade",
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return { ok: true, ctx: workspaceContext };
|
|
117
|
-
}
|
package/src/session.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session management for APIClaw MCP server
|
|
3
|
-
* Stores session token locally at ~/.apiclaw/session
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import * as os from 'os';
|
|
9
|
-
|
|
10
|
-
export interface SessionData {
|
|
11
|
-
sessionToken: string;
|
|
12
|
-
workspaceId: string;
|
|
13
|
-
email: string;
|
|
14
|
-
createdAt: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const SESSION_DIR = path.join(os.homedir(), '.apiclaw');
|
|
18
|
-
const SESSION_FILE = path.join(SESSION_DIR, 'session');
|
|
19
|
-
const SESSION_FILE_LEGACY = path.join(SESSION_DIR, 'session.json');
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Ensure the ~/.apiclaw directory exists
|
|
23
|
-
*/
|
|
24
|
-
function ensureSessionDir(): void {
|
|
25
|
-
if (!fs.existsSync(SESSION_DIR)) {
|
|
26
|
-
fs.mkdirSync(SESSION_DIR, { mode: 0o700 });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Read session from ~/.apiclaw/session
|
|
32
|
-
* Returns null if no session file exists or if it's invalid
|
|
33
|
-
*/
|
|
34
|
-
export function readSession(): SessionData | null {
|
|
35
|
-
try {
|
|
36
|
-
// Try primary session file first
|
|
37
|
-
if (fs.existsSync(SESSION_FILE)) {
|
|
38
|
-
const content = fs.readFileSync(SESSION_FILE, 'utf8');
|
|
39
|
-
const data = JSON.parse(content) as SessionData;
|
|
40
|
-
if (data.sessionToken && data.workspaceId && data.email) {
|
|
41
|
-
return data;
|
|
42
|
-
}
|
|
43
|
-
// Invalid (e.g. empty email from anonymous write) — clear and fall through
|
|
44
|
-
console.error('[APIClaw] Invalid session file, checking legacy...');
|
|
45
|
-
fs.unlinkSync(SESSION_FILE);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Fall back to session.json (written by CLI login in older versions)
|
|
49
|
-
if (fs.existsSync(SESSION_FILE_LEGACY)) {
|
|
50
|
-
const content = fs.readFileSync(SESSION_FILE_LEGACY, 'utf8');
|
|
51
|
-
const data = JSON.parse(content) as SessionData;
|
|
52
|
-
if (data.sessionToken && data.workspaceId && data.email) {
|
|
53
|
-
console.error(`[APIClaw] Migrating session.json → session for ${data.email}`);
|
|
54
|
-
// Migrate to canonical location
|
|
55
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify({ ...data, createdAt: data.createdAt || Date.now() }, null, 2), { mode: 0o600 });
|
|
56
|
-
return data;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return null;
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error('[APIClaw] Error reading session:', error);
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Write session to ~/.apiclaw/session
|
|
69
|
-
*/
|
|
70
|
-
export function writeSession(sessionToken: string, workspaceId: string, email: string): void {
|
|
71
|
-
try {
|
|
72
|
-
ensureSessionDir();
|
|
73
|
-
|
|
74
|
-
const data: SessionData = {
|
|
75
|
-
sessionToken,
|
|
76
|
-
workspaceId,
|
|
77
|
-
email,
|
|
78
|
-
createdAt: Date.now(),
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
fs.writeFileSync(SESSION_FILE, JSON.stringify(data, null, 2), {
|
|
82
|
-
mode: 0o600, // Read/write for owner only
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
console.error(`[APIClaw] Session saved for ${email}`);
|
|
86
|
-
} catch (error) {
|
|
87
|
-
console.error('[APIClaw] Error writing session:', error);
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Clear session file
|
|
94
|
-
*/
|
|
95
|
-
export function clearSession(): void {
|
|
96
|
-
try {
|
|
97
|
-
if (fs.existsSync(SESSION_FILE)) {
|
|
98
|
-
fs.unlinkSync(SESSION_FILE);
|
|
99
|
-
console.error('[APIClaw] Session cleared');
|
|
100
|
-
}
|
|
101
|
-
} catch (error) {
|
|
102
|
-
console.error('[APIClaw] Error clearing session:', error);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Get machine fingerprint (for session binding)
|
|
108
|
-
* Uses hostname + username as a simple fingerprint
|
|
109
|
-
*/
|
|
110
|
-
export function getMachineFingerprint(): string {
|
|
111
|
-
const hostname = os.hostname();
|
|
112
|
-
const username = os.userInfo().username;
|
|
113
|
-
return `${hostname}:${username}`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Detect which MCP client is running the server
|
|
118
|
-
* Priority: explicit env (set by mcp-install) → known env hints → fallback
|
|
119
|
-
*/
|
|
120
|
-
export function detectMCPClient(): string {
|
|
121
|
-
// 1. Explicit env (injected by mcp-install adapters)
|
|
122
|
-
if (process.env.APICLAW_MCP_CLIENT) return process.env.APICLAW_MCP_CLIENT;
|
|
123
|
-
// 2. Known environment variable hints
|
|
124
|
-
if (process.env.CURSOR_TRACE_DIR) return 'cursor';
|
|
125
|
-
if (process.env.CLAUDE_CODE) return 'claude-code';
|
|
126
|
-
if (process.env.WINDSURF_SESSION) return 'windsurf';
|
|
127
|
-
// 3. Fallback
|
|
128
|
-
return 'unknown';
|
|
129
|
-
}
|