@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.
@@ -95,9 +95,9 @@ export const verifyMagicLink = mutation({
95
95
  status: "active",
96
96
  tier: "free",
97
97
  usageCount: 0,
98
- usageLimit: 50, // Legacy field, now using weekly limits
98
+ usageLimit: 50, // 50 calls/month for free tier
99
99
  weeklyUsageCount: 0,
100
- weeklyUsageLimit: 50, // 50 calls/week for free tier
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nordsym/apiclaw",
3
- "version": "1.7.7",
3
+ "version": "1.7.9",
4
4
  "description": "The API layer for AI agents. Dashboard + 22K APIs + 18 Direct Call providers. MCP native.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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://adventurous-avocet-799.convex.cloud';
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://adventurous-avocet-799.convex.cloud';
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
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  // Hivr PROD Convex deployment
8
- const HIVR_CONVEX_URL = "https://sensible-quail-275.convex.cloud";
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 FREE_WEEKLY_LIMIT = 50;
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 getNextMondayUTC(): string {
115
+ function getNextMonthUTC(): string {
115
116
  const now = new Date();
116
- const dayOfWeek = now.getUTCDay(); // 0 = Sunday, 1 = Monday, etc.
117
- const daysUntilMonday = dayOfWeek === 0 ? 1 : 8 - dayOfWeek;
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: `Weekly limit reached (${ANONYMOUS_WEEKLY_LIMIT} calls)`,
177
- hint: "Register to get 50 calls/week",
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: getNextMondayUTC()
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/week, 10 calls/hour (authenticated)
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: `Weekly limit reached (${FREE_WEEKLY_LIMIT} calls)`,
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: getNextMondayUTC()
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: [{
@@ -18,7 +18,7 @@ interface ProductSource {
18
18
  const PRODUCT_SOURCES: ProductSource[] = [
19
19
  {
20
20
  name: 'hivr',
21
- convexUrl: 'https://sensible-quail-275.convex.cloud',
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://adventurous-avocet-799.convex.site/proxy";
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://agile-crane-840.convex.cloud/api/mutation';
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';