@nordsym/apiclaw 1.7.7 → 1.7.9
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/convex/workspaces.ts +2 -2
- package/package.json +1 -1
- package/src/capability-router.ts +1 -1
- package/src/cli.ts +1 -1
- package/src/hivr-whitelist.ts +1 -1
- package/src/index.ts +56 -28
- package/src/product-whitelist.ts +1 -1
- package/src/proxy.ts +1 -1
- package/src/telemetry.ts +1 -1
package/convex/workspaces.ts
CHANGED
|
@@ -95,9 +95,9 @@ export const verifyMagicLink = mutation({
|
|
|
95
95
|
status: "active",
|
|
96
96
|
tier: "free",
|
|
97
97
|
usageCount: 0,
|
|
98
|
-
usageLimit: 50, //
|
|
98
|
+
usageLimit: 50, // 50 calls/month for free tier
|
|
99
99
|
weeklyUsageCount: 0,
|
|
100
|
-
weeklyUsageLimit: 50, //
|
|
100
|
+
weeklyUsageLimit: 50, // Monthly limit (field name is legacy)
|
|
101
101
|
hourlyUsageCount: 0,
|
|
102
102
|
referralCode: newReferralCode!,
|
|
103
103
|
createdAt: Date.now(),
|
package/package.json
CHANGED
package/src/capability-router.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { executeAPICall } from './execute.js';
|
|
|
7
7
|
import { logAPICall } from './mcp-analytics.js';
|
|
8
8
|
|
|
9
9
|
// Convex HTTP API for capability queries
|
|
10
|
-
const CONVEX_URL = process.env.NEXT_PUBLIC_CONVEX_URL || 'https://
|
|
10
|
+
const CONVEX_URL = process.env.NEXT_PUBLIC_CONVEX_URL || 'https://brilliant-puffin-712.eu-west-1.convex.cloud';
|
|
11
11
|
|
|
12
12
|
interface ProviderMapping {
|
|
13
13
|
providerId: string;
|
package/src/cli.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { discoverAPIs, getAPIDetails, getCategories } from './discovery.js';
|
|
|
10
10
|
import { executeAPICall, getConnectedProviders } from './execute.js';
|
|
11
11
|
import { readSession, writeSession, clearSession, getMachineFingerprint } from './session.js';
|
|
12
12
|
|
|
13
|
-
const CONVEX_URL = process.env.CONVEX_URL || 'https://
|
|
13
|
+
const CONVEX_URL = process.env.CONVEX_URL || 'https://brilliant-puffin-712.eu-west-1.convex.cloud';
|
|
14
14
|
const convex = new ConvexHttpClient(CONVEX_URL);
|
|
15
15
|
|
|
16
16
|
// Colors for terminal
|
package/src/hivr-whitelist.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// Hivr PROD Convex deployment
|
|
8
|
-
const HIVR_CONVEX_URL = "https://
|
|
8
|
+
const HIVR_CONVEX_URL = "https://brilliant-puffin-712.eu-west-1.convex.cloud";
|
|
9
9
|
|
|
10
10
|
// Fallback static whitelist (in case Convex is down)
|
|
11
11
|
const STATIC_WHITELIST = [
|
package/src/index.ts
CHANGED
|
@@ -77,6 +77,7 @@ interface WorkspaceContext {
|
|
|
77
77
|
email: string;
|
|
78
78
|
tier: string;
|
|
79
79
|
usageRemaining: number;
|
|
80
|
+
usageCount: number;
|
|
80
81
|
status: string;
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -96,7 +97,7 @@ const anonymousRateLimits = new Map<string, AnonymousRateLimitState>();
|
|
|
96
97
|
// Rate limit constants
|
|
97
98
|
const ANONYMOUS_HOURLY_LIMIT = 5;
|
|
98
99
|
const ANONYMOUS_WEEKLY_LIMIT = 10;
|
|
99
|
-
const
|
|
100
|
+
const FREE_MONTHLY_LIMIT = 50;
|
|
100
101
|
|
|
101
102
|
/**
|
|
102
103
|
* Calculate minutes until next hour
|
|
@@ -111,14 +112,10 @@ function calculateMinutesUntilNextHour(): number {
|
|
|
111
112
|
/**
|
|
112
113
|
* Get next Monday 00:00 UTC as ISO string
|
|
113
114
|
*/
|
|
114
|
-
function
|
|
115
|
+
function getNextMonthUTC(): string {
|
|
115
116
|
const now = new Date();
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
const nextMonday = new Date(now);
|
|
119
|
-
nextMonday.setUTCDate(now.getUTCDate() + daysUntilMonday);
|
|
120
|
-
nextMonday.setUTCHours(0, 0, 0, 0);
|
|
121
|
-
return nextMonday.toISOString().replace('T', ' ').slice(0, 16) + ' UTC';
|
|
117
|
+
const nextMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1));
|
|
118
|
+
return nextMonth.toISOString().replace('T', ' ').slice(0, 16) + ' UTC';
|
|
122
119
|
}
|
|
123
120
|
|
|
124
121
|
/**
|
|
@@ -173,10 +170,10 @@ function checkAnonymousRateLimit(fingerprint: string): { allowed: boolean; error
|
|
|
173
170
|
allowed: false,
|
|
174
171
|
error: JSON.stringify({
|
|
175
172
|
success: false,
|
|
176
|
-
error: `
|
|
177
|
-
hint: "Register to get 50 calls/
|
|
173
|
+
error: `Monthly limit reached (${ANONYMOUS_WEEKLY_LIMIT} calls)`,
|
|
174
|
+
hint: "Register to get 50 calls/month",
|
|
178
175
|
action: "Run: register_owner({ email: 'you@example.com' })",
|
|
179
|
-
retry_after:
|
|
176
|
+
retry_after: getNextMonthUTC()
|
|
180
177
|
}, null, 2)
|
|
181
178
|
};
|
|
182
179
|
}
|
|
@@ -221,6 +218,7 @@ async function validateSession(): Promise<boolean> {
|
|
|
221
218
|
email: result.email ?? '',
|
|
222
219
|
tier: result.tier ?? 'free',
|
|
223
220
|
usageRemaining: result.usageRemaining ?? 0,
|
|
221
|
+
usageCount: result.usageCount ?? 0,
|
|
224
222
|
status: result.status ?? 'unknown',
|
|
225
223
|
};
|
|
226
224
|
|
|
@@ -265,7 +263,7 @@ async function trackEarnProgress(workspaceId: string, provider: string, action:
|
|
|
265
263
|
/**
|
|
266
264
|
* Rate limiting for anonymous proxy usage
|
|
267
265
|
* Limits: 10 calls/week, 5 calls/hour (anonymous)
|
|
268
|
-
* 50 calls/
|
|
266
|
+
* 50 calls/month, 10 calls/hour (authenticated)
|
|
269
267
|
*/
|
|
270
268
|
interface RateLimitState {
|
|
271
269
|
hourly: { count: number; resetAt: number };
|
|
@@ -274,6 +272,9 @@ interface RateLimitState {
|
|
|
274
272
|
|
|
275
273
|
const rateLimitStore = new Map<string, RateLimitState>();
|
|
276
274
|
|
|
275
|
+
// Unregistered (auto-provisioned, no email) users get this many calls before signup required
|
|
276
|
+
const UNREGISTERED_CALL_LIMIT = 5;
|
|
277
|
+
|
|
277
278
|
/**
|
|
278
279
|
* For proxy providers, allow anonymous usage with rate limiting
|
|
279
280
|
*/
|
|
@@ -308,12 +309,25 @@ function checkWorkspaceAccess(providerId?: string): { allowed: boolean; error?:
|
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
if (workspaceContext.status !== 'active') {
|
|
311
|
-
return {
|
|
312
|
-
allowed: false,
|
|
313
|
-
error: `Workspace status: ${workspaceContext.status}. Please verify your email.`
|
|
312
|
+
return {
|
|
313
|
+
allowed: false,
|
|
314
|
+
error: `Workspace status: ${workspaceContext.status}. Please verify your email.`
|
|
314
315
|
};
|
|
315
316
|
}
|
|
316
|
-
|
|
317
|
+
|
|
318
|
+
// Unregistered workspaces (auto-provisioned, no email) get limited calls then must register
|
|
319
|
+
if (!workspaceContext.email && workspaceContext.usageCount >= UNREGISTERED_CALL_LIMIT) {
|
|
320
|
+
return {
|
|
321
|
+
allowed: false,
|
|
322
|
+
error: JSON.stringify({
|
|
323
|
+
success: false,
|
|
324
|
+
error: `Register to continue. You've used ${UNREGISTERED_CALL_LIMIT} free calls.`,
|
|
325
|
+
hint: "Run register_owner with your email to unlock 50 calls/month.",
|
|
326
|
+
action: "register_owner"
|
|
327
|
+
}, null, 2)
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
317
331
|
if (workspaceContext.usageRemaining === 0) {
|
|
318
332
|
// Free tier hit weekly limit
|
|
319
333
|
if (workspaceContext.tier === 'free') {
|
|
@@ -321,10 +335,10 @@ function checkWorkspaceAccess(providerId?: string): { allowed: boolean; error?:
|
|
|
321
335
|
allowed: false,
|
|
322
336
|
error: JSON.stringify({
|
|
323
337
|
success: false,
|
|
324
|
-
error: `
|
|
338
|
+
error: `Monthly limit reached (${FREE_MONTHLY_LIMIT} calls)`,
|
|
325
339
|
hint: "Upgrade to Backer for unlimited calls",
|
|
326
340
|
upgrade_url: "https://apiclaw.nordsym.com/upgrade",
|
|
327
|
-
retry_after:
|
|
341
|
+
retry_after: getNextMonthUTC()
|
|
328
342
|
}, null, 2)
|
|
329
343
|
};
|
|
330
344
|
}
|
|
@@ -1532,6 +1546,7 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1532
1546
|
}) as { success: boolean; remaining?: number };
|
|
1533
1547
|
if (usageResult.success) {
|
|
1534
1548
|
workspaceContext.usageRemaining = usageResult.remaining ?? -1;
|
|
1549
|
+
workspaceContext.usageCount = (workspaceContext.usageCount || 0) + 1;
|
|
1535
1550
|
}
|
|
1536
1551
|
|
|
1537
1552
|
// Track earn progress (first direct call + unique APIs)
|
|
@@ -1541,18 +1556,29 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1541
1556
|
}
|
|
1542
1557
|
}
|
|
1543
1558
|
|
|
1559
|
+
// Build response with signup nudge for unregistered users
|
|
1560
|
+
const responseData: Record<string, unknown> = {
|
|
1561
|
+
status: result.success ? 'success' : 'error',
|
|
1562
|
+
provider: result.provider,
|
|
1563
|
+
action: result.action,
|
|
1564
|
+
type: apiType,
|
|
1565
|
+
...(result.success ? { data: result.data } : { error: result.error }),
|
|
1566
|
+
...(result.cost !== undefined ? { cost_sek: result.cost } : {})
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1569
|
+
// Nudge unregistered users
|
|
1570
|
+
if (result.success && workspaceContext && !workspaceContext.email) {
|
|
1571
|
+
const remaining = UNREGISTERED_CALL_LIMIT - (workspaceContext.usageCount || 0);
|
|
1572
|
+
if (remaining > 0 && remaining <= 3) {
|
|
1573
|
+
responseData._notice = `${remaining} free calls remaining. Run register_owner to unlock 50/month.`;
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1544
1577
|
return {
|
|
1545
1578
|
content: [
|
|
1546
1579
|
{
|
|
1547
1580
|
type: 'text',
|
|
1548
|
-
text: JSON.stringify(
|
|
1549
|
-
status: result.success ? 'success' : 'error',
|
|
1550
|
-
provider: result.provider,
|
|
1551
|
-
action: result.action,
|
|
1552
|
-
type: apiType,
|
|
1553
|
-
...(result.success ? { data: result.data } : { error: result.error }),
|
|
1554
|
-
...(result.cost !== undefined ? { cost_sek: result.cost } : {})
|
|
1555
|
-
}, null, 2)
|
|
1581
|
+
text: JSON.stringify(responseData, null, 2)
|
|
1556
1582
|
}
|
|
1557
1583
|
],
|
|
1558
1584
|
isError: !result.success
|
|
@@ -1729,6 +1755,7 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1729
1755
|
email,
|
|
1730
1756
|
tier: existing.tier,
|
|
1731
1757
|
usageRemaining: existing.usageLimit - existing.usageCount,
|
|
1758
|
+
usageCount: existing.usageCount,
|
|
1732
1759
|
status: existing.status,
|
|
1733
1760
|
};
|
|
1734
1761
|
|
|
@@ -1770,7 +1797,7 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1770
1797
|
}) as { token: string; expiresAt: number };
|
|
1771
1798
|
|
|
1772
1799
|
// Send magic link via email
|
|
1773
|
-
const verifyUrl = `https://apiclaw.nordsym.com/verify?token=${magicLinkResult.token}`;
|
|
1800
|
+
const verifyUrl = `https://apiclaw.nordsym.com/auth/verify?token=${magicLinkResult.token}`;
|
|
1774
1801
|
|
|
1775
1802
|
const emailResponse = await fetch('https://api.resend.com/emails', {
|
|
1776
1803
|
method: 'POST',
|
|
@@ -1860,6 +1887,7 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1860
1887
|
email: result.email ?? '',
|
|
1861
1888
|
tier: result.tier ?? 'free',
|
|
1862
1889
|
usageRemaining: result.usageRemaining ?? 0,
|
|
1890
|
+
usageCount: result.usageCount ?? 0,
|
|
1863
1891
|
status: result.status ?? 'unknown',
|
|
1864
1892
|
};
|
|
1865
1893
|
|
|
@@ -1940,7 +1968,7 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1940
1968
|
}) as { token: string; expiresAt: number };
|
|
1941
1969
|
|
|
1942
1970
|
// TODO: Agent 2 will implement actual email sending
|
|
1943
|
-
const verifyUrl = `https://apiclaw.nordsym.com/verify?token=${magicLinkResult.token}`;
|
|
1971
|
+
const verifyUrl = `https://apiclaw.nordsym.com/auth/verify?token=${magicLinkResult.token}`;
|
|
1944
1972
|
|
|
1945
1973
|
return {
|
|
1946
1974
|
content: [{
|
package/src/product-whitelist.ts
CHANGED
|
@@ -18,7 +18,7 @@ interface ProductSource {
|
|
|
18
18
|
const PRODUCT_SOURCES: ProductSource[] = [
|
|
19
19
|
{
|
|
20
20
|
name: 'hivr',
|
|
21
|
-
convexUrl: 'https://
|
|
21
|
+
convexUrl: 'https://brilliant-puffin-712.eu-west-1.convex.cloud',
|
|
22
22
|
queryPath: 'agents:list',
|
|
23
23
|
agentIdField: 'handle', // ✅ Fixed: Hivr agents use 'handle', not 'agentId'
|
|
24
24
|
},
|
package/src/proxy.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { readSession, getMachineFingerprint } from './session.js';
|
|
6
6
|
|
|
7
|
-
const PROXY_BASE = "https://
|
|
7
|
+
const PROXY_BASE = "https://brilliant-puffin-712.eu-west-1.convex.site/proxy";
|
|
8
8
|
|
|
9
9
|
export async function callProxy(provider: string, params: any): Promise<any> {
|
|
10
10
|
const url = `${PROXY_BASE}/${provider}`;
|
package/src/telemetry.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Disable with APICLAW_TELEMETRY=false
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
const TELEMETRY_ENDPOINT = 'https://
|
|
13
|
+
const TELEMETRY_ENDPOINT = 'https://brilliant-puffin-712.eu-west-1.convex.cloud/api/mutation';
|
|
14
14
|
|
|
15
15
|
interface TelemetryEvent {
|
|
16
16
|
type: 'startup' | 'search' | 'execute' | 'discovery';
|