@nordsym/apiclaw 2.0.0 → 2.1.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/convex/schema.ts +13 -0
- package/convex/workspaces.ts +185 -0
- package/dist/index.js +210 -79
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +245 -100
package/src/index.ts
CHANGED
|
@@ -84,6 +84,7 @@ interface WorkspaceContext {
|
|
|
84
84
|
|
|
85
85
|
let workspaceContext: WorkspaceContext | null = null;
|
|
86
86
|
let currentAgentId: string | null = null; // Agent ID from agents table (set on startup)
|
|
87
|
+
let pendingRegistrationEmail: string | null = null; // Email waiting for OTP verification
|
|
87
88
|
|
|
88
89
|
// Anonymous rate limit tracking (in-memory, per machine fingerprint)
|
|
89
90
|
interface AnonymousRateLimitState {
|
|
@@ -278,35 +279,21 @@ const rateLimitStore = new Map<string, RateLimitState>();
|
|
|
278
279
|
const UNREGISTERED_CALL_LIMIT = 5;
|
|
279
280
|
|
|
280
281
|
/**
|
|
281
|
-
*
|
|
282
|
+
* Check workspace access -- registration required for all API calls
|
|
282
283
|
*/
|
|
283
284
|
function checkWorkspaceAccess(providerId?: string): { allowed: boolean; error?: string; isAnonymous?: boolean } {
|
|
284
|
-
//
|
|
285
|
-
if (providerId && PROXY_PROVIDERS.includes(providerId)) {
|
|
286
|
-
if (!workspaceContext) {
|
|
287
|
-
// Anonymous user - check rate limits
|
|
288
|
-
const fingerprint = getMachineFingerprint();
|
|
289
|
-
const rateLimitCheck = checkAnonymousRateLimit(fingerprint);
|
|
290
|
-
|
|
291
|
-
if (!rateLimitCheck.allowed) {
|
|
292
|
-
return {
|
|
293
|
-
allowed: false,
|
|
294
|
-
error: rateLimitCheck.error,
|
|
295
|
-
isAnonymous: true
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return { allowed: true, isAnonymous: true };
|
|
300
|
-
}
|
|
301
|
-
// Authenticated user using proxy provider - allow with higher limits
|
|
302
|
-
return { allowed: true, isAnonymous: false };
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Non-proxy providers require authentication
|
|
285
|
+
// All API calls require registration now
|
|
306
286
|
if (!workspaceContext) {
|
|
307
|
-
return {
|
|
308
|
-
allowed: false,
|
|
309
|
-
error:
|
|
287
|
+
return {
|
|
288
|
+
allowed: false,
|
|
289
|
+
error: JSON.stringify({
|
|
290
|
+
status: 'registration_required',
|
|
291
|
+
error: 'Registration required to call APIs.',
|
|
292
|
+
message: '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.',
|
|
293
|
+
action: 'register_owner',
|
|
294
|
+
free_tier: '50 API calls/month -- completely free.',
|
|
295
|
+
}, null, 2),
|
|
296
|
+
isAnonymous: true,
|
|
310
297
|
};
|
|
311
298
|
}
|
|
312
299
|
|
|
@@ -511,7 +498,7 @@ const tools: Tool[] = [
|
|
|
511
498
|
},
|
|
512
499
|
{
|
|
513
500
|
name: 'call_api',
|
|
514
|
-
description: `Execute an API call through APIClaw.
|
|
501
|
+
description: `Execute an API call through APIClaw. Requires registration (free). If not registered, call register_owner first.
|
|
515
502
|
|
|
516
503
|
SINGLE CALL: Provide provider + action + params
|
|
517
504
|
CHAIN: Provide chain array to execute multiple APIs in sequence/parallel with cross-step references.
|
|
@@ -683,7 +670,7 @@ Example chain:
|
|
|
683
670
|
// ============================================
|
|
684
671
|
{
|
|
685
672
|
name: 'register_owner',
|
|
686
|
-
description: 'Register your email to create a workspace.
|
|
673
|
+
description: 'REQUIRED before using any API. Register your email to create a workspace. A 6-digit verification code will be sent to your email. After calling this, ask the user for the code and call verify_code.',
|
|
687
674
|
inputSchema: {
|
|
688
675
|
type: 'object',
|
|
689
676
|
properties: {
|
|
@@ -695,6 +682,24 @@ Example chain:
|
|
|
695
682
|
required: ['email']
|
|
696
683
|
}
|
|
697
684
|
},
|
|
685
|
+
{
|
|
686
|
+
name: 'verify_code',
|
|
687
|
+
description: 'Verify the 6-digit code sent to your email after register_owner. This completes registration and activates your workspace. Ask the user to check their email and paste the code.',
|
|
688
|
+
inputSchema: {
|
|
689
|
+
type: 'object',
|
|
690
|
+
properties: {
|
|
691
|
+
email: {
|
|
692
|
+
type: 'string',
|
|
693
|
+
description: 'The email address used in register_owner'
|
|
694
|
+
},
|
|
695
|
+
code: {
|
|
696
|
+
type: 'string',
|
|
697
|
+
description: 'The 6-digit verification code from the email'
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
required: ['email', 'code']
|
|
701
|
+
}
|
|
702
|
+
},
|
|
698
703
|
{
|
|
699
704
|
name: 'check_workspace_status',
|
|
700
705
|
description: 'Check your workspace status, tier, and usage remaining.',
|
|
@@ -831,40 +836,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
831
836
|
try {
|
|
832
837
|
switch (name) {
|
|
833
838
|
case 'apiclaw_help': {
|
|
839
|
+
const isAuthenticated = !!workspaceContext;
|
|
834
840
|
const helpText = `
|
|
835
|
-
🦞 APIClaw
|
|
841
|
+
🦞 APIClaw -- The API Layer for AI Agents
|
|
836
842
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
837
|
-
|
|
838
|
-
|
|
843
|
+
${!isAuthenticated ? `
|
|
844
|
+
GET STARTED (free):
|
|
845
|
+
1. register_owner({ email: "you@example.com" }) — sends 6-digit code
|
|
846
|
+
2. verify_code({ email: "you@example.com", code: "123456" }) — activates workspace
|
|
847
|
+
` : `
|
|
848
|
+
STATUS: Authenticated as ${workspaceContext!.email} (${workspaceContext!.tier} tier)
|
|
849
|
+
`}
|
|
850
|
+
DISCOVER APIs (free, no registration needed):
|
|
839
851
|
discover_apis({ query: "send SMS to Sweden" })
|
|
840
|
-
discover_apis({ query: "search the web", max_results: 10 })
|
|
841
852
|
discover_apis({ query: "text to speech", category: "ai" })
|
|
842
853
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
• resend — Email
|
|
855
|
-
• openrouter — LLM routing (100+ models)
|
|
856
|
-
• elevenlabs — Text-to-speech
|
|
857
|
-
• replicate — AI models (images, video, audio)
|
|
858
|
-
• firecrawl — Web scraping & crawling
|
|
859
|
-
• github — Code repos & developer data
|
|
860
|
-
• e2b — Code sandbox for AI agents
|
|
861
|
-
|
|
862
|
-
BROWSE:
|
|
863
|
-
list_categories()
|
|
864
|
-
list_all_apis({ category: "communication", limit: 20 })
|
|
854
|
+
CALL APIs (requires free registration):
|
|
855
|
+
call_api({ provider: "brave_search", action: "search", params: { q: "AI agents" } })
|
|
856
|
+
call_api({ provider: "elevenlabs", action: "tts", params: { text: "Hello" } })
|
|
857
|
+
|
|
858
|
+
23 MANAGED PROVIDERS:
|
|
859
|
+
OpenAI, Anthropic, xAI/Grok, Groq, Mistral, OpenRouter, Together AI,
|
|
860
|
+
Replicate, ElevenLabs, Deepgram, AssemblyAI, Brave Search, Firecrawl,
|
|
861
|
+
Serper, Resend, 46elks, Twilio, E2B, Stability AI, Cohere, Voyage AI,
|
|
862
|
+
GitHub, APILayer (27 sub-APIs)
|
|
863
|
+
|
|
864
|
+
26,700+ DISCOVERABLE | 1,654 CALLABLE | Free tier: 50 calls/month
|
|
865
865
|
|
|
866
866
|
Docs: https://apiclaw.cloud
|
|
867
867
|
`;
|
|
868
|
+
|
|
868
869
|
return {
|
|
869
870
|
content: [{ type: 'text', text: helpText }]
|
|
870
871
|
};
|
|
@@ -1180,6 +1181,25 @@ Docs: https://apiclaw.cloud
|
|
|
1180
1181
|
}
|
|
1181
1182
|
|
|
1182
1183
|
case 'call_api': {
|
|
1184
|
+
// ============================================
|
|
1185
|
+
// REGISTRATION GATE: require email before any API call
|
|
1186
|
+
// ============================================
|
|
1187
|
+
if (!workspaceContext) {
|
|
1188
|
+
return {
|
|
1189
|
+
content: [{
|
|
1190
|
+
type: 'text',
|
|
1191
|
+
text: JSON.stringify({
|
|
1192
|
+
status: 'registration_required',
|
|
1193
|
+
error: 'You need to register before making API calls.',
|
|
1194
|
+
message: 'APIClaw requires a free account to use APIs. Ask the user for their email address, then call register_owner({ email: "user@example.com" }). A 6-digit verification code will be sent to their email. Then call verify_code with the code.',
|
|
1195
|
+
action: 'register_owner',
|
|
1196
|
+
free_tier: '50 API calls/month, unlimited discovery -- completely free.',
|
|
1197
|
+
}, null, 2)
|
|
1198
|
+
}],
|
|
1199
|
+
isError: true
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1183
1203
|
const provider = args?.provider as string;
|
|
1184
1204
|
const action = args?.action as string;
|
|
1185
1205
|
const params = (args?.params as Record<string, any>) || {};
|
|
@@ -1188,7 +1208,7 @@ Docs: https://apiclaw.cloud
|
|
|
1188
1208
|
const chain = args?.chain as ChainStepUnion[] | undefined;
|
|
1189
1209
|
const subagentId = args?.subagent_id as string | undefined;
|
|
1190
1210
|
const aiBackend = args?.ai_backend as string | undefined;
|
|
1191
|
-
|
|
1211
|
+
|
|
1192
1212
|
// Track AI backend if provided
|
|
1193
1213
|
if (aiBackend && workspaceContext?.sessionToken) {
|
|
1194
1214
|
fetch('https://adventurous-avocet-799.convex.cloud/api/mutation', {
|
|
@@ -1663,13 +1683,29 @@ Docs: https://apiclaw.cloud
|
|
|
1663
1683
|
}
|
|
1664
1684
|
|
|
1665
1685
|
case 'capability': {
|
|
1686
|
+
// Registration gate
|
|
1687
|
+
if (!workspaceContext) {
|
|
1688
|
+
return {
|
|
1689
|
+
content: [{
|
|
1690
|
+
type: 'text',
|
|
1691
|
+
text: JSON.stringify({
|
|
1692
|
+
status: 'registration_required',
|
|
1693
|
+
error: 'You need to register before making API calls.',
|
|
1694
|
+
message: '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.',
|
|
1695
|
+
action: 'register_owner',
|
|
1696
|
+
}, null, 2)
|
|
1697
|
+
}],
|
|
1698
|
+
isError: true
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1666
1702
|
const capabilityId = args?.capability as string;
|
|
1667
1703
|
const action = args?.action as string;
|
|
1668
1704
|
const params = (args?.params as Record<string, any>) || {};
|
|
1669
1705
|
const preferences = (args?.preferences as Record<string, any>) || {};
|
|
1670
1706
|
const subagentId = args?.subagent_id as string | undefined;
|
|
1671
1707
|
const aiBackend = args?.ai_backend as string | undefined;
|
|
1672
|
-
|
|
1708
|
+
|
|
1673
1709
|
// Track AI backend if provided
|
|
1674
1710
|
if (aiBackend && workspaceContext?.sessionToken) {
|
|
1675
1711
|
fetch('https://adventurous-avocet-799.convex.cloud/api/mutation', {
|
|
@@ -1755,7 +1791,7 @@ Docs: https://apiclaw.cloud
|
|
|
1755
1791
|
|
|
1756
1792
|
case 'register_owner': {
|
|
1757
1793
|
const email = args?.email as string;
|
|
1758
|
-
|
|
1794
|
+
|
|
1759
1795
|
if (!email || !email.includes('@')) {
|
|
1760
1796
|
return {
|
|
1761
1797
|
content: [{
|
|
@@ -1768,13 +1804,12 @@ Docs: https://apiclaw.cloud
|
|
|
1768
1804
|
isError: true
|
|
1769
1805
|
};
|
|
1770
1806
|
}
|
|
1771
|
-
|
|
1807
|
+
|
|
1772
1808
|
try {
|
|
1773
|
-
// Check if workspace already exists
|
|
1809
|
+
// Check if workspace already exists and is active -- auto-login
|
|
1774
1810
|
const existing = await convex.query("workspaces:getByEmail" as any, { email }) as { id: string; status: string; tier: string; usageCount: number; usageLimit: number } | null;
|
|
1775
1811
|
|
|
1776
1812
|
if (existing && existing.status === 'active') {
|
|
1777
|
-
// Workspace exists and is active - create session directly
|
|
1778
1813
|
const fingerprint = getMachineFingerprint();
|
|
1779
1814
|
const sessionResult = await convex.mutation("workspaces:createAgentSession" as any, {
|
|
1780
1815
|
workspaceId: existing.id,
|
|
@@ -1783,23 +1818,17 @@ Docs: https://apiclaw.cloud
|
|
|
1783
1818
|
|
|
1784
1819
|
if (sessionResult.success) {
|
|
1785
1820
|
writeSession(sessionResult.sessionToken!, existing.id, email);
|
|
1786
|
-
|
|
1787
|
-
// Claim anonymous usage history
|
|
1821
|
+
|
|
1788
1822
|
try {
|
|
1789
1823
|
const claimResult = await convex.mutation("workspaces:claimAnonymousUsage" as any, {
|
|
1790
1824
|
workspaceId: existing.id,
|
|
1791
1825
|
machineFingerprint: fingerprint,
|
|
1792
|
-
}) as { success: boolean; claimedCount?: number
|
|
1793
|
-
|
|
1826
|
+
}) as { success: boolean; claimedCount?: number };
|
|
1794
1827
|
if (claimResult.success && claimResult.claimedCount) {
|
|
1795
|
-
console.error(`[APIClaw]
|
|
1828
|
+
console.error(`[APIClaw] Claimed ${claimResult.claimedCount} anonymous usage records`);
|
|
1796
1829
|
}
|
|
1797
|
-
} catch (
|
|
1798
|
-
|
|
1799
|
-
console.error('[APIClaw] Warning: Failed to claim anonymous usage:', err);
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
// Update global context
|
|
1830
|
+
} catch (_) {}
|
|
1831
|
+
|
|
1803
1832
|
workspaceContext = {
|
|
1804
1833
|
sessionToken: sessionResult.sessionToken!,
|
|
1805
1834
|
workspaceId: existing.id,
|
|
@@ -1809,7 +1838,7 @@ Docs: https://apiclaw.cloud
|
|
|
1809
1838
|
usageCount: existing.usageCount,
|
|
1810
1839
|
status: existing.status,
|
|
1811
1840
|
};
|
|
1812
|
-
|
|
1841
|
+
|
|
1813
1842
|
return {
|
|
1814
1843
|
content: [{
|
|
1815
1844
|
type: 'text',
|
|
@@ -1827,29 +1856,15 @@ Docs: https://apiclaw.cloud
|
|
|
1827
1856
|
};
|
|
1828
1857
|
}
|
|
1829
1858
|
}
|
|
1830
|
-
|
|
1831
|
-
//
|
|
1832
|
-
const createResult = await convex.mutation("workspaces:createWorkspace" as any, { email }) as { success: boolean; workspaceId?: string; error?: string };
|
|
1833
|
-
|
|
1834
|
-
let workspaceId: string;
|
|
1835
|
-
if (createResult.success) {
|
|
1836
|
-
workspaceId = createResult.workspaceId!;
|
|
1837
|
-
} else if (createResult.error === 'workspace_exists') {
|
|
1838
|
-
workspaceId = createResult.workspaceId!;
|
|
1839
|
-
} else {
|
|
1840
|
-
throw new Error(createResult.error);
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
// Create magic link
|
|
1859
|
+
|
|
1860
|
+
// New user or pending workspace -- send OTP
|
|
1844
1861
|
const fingerprint = getMachineFingerprint();
|
|
1845
|
-
const
|
|
1862
|
+
const otpResult = await convex.mutation("workspaces:createOTP" as any, {
|
|
1846
1863
|
email,
|
|
1847
1864
|
fingerprint,
|
|
1848
|
-
}) as {
|
|
1849
|
-
|
|
1850
|
-
// Send
|
|
1851
|
-
const verifyUrl = `https://apiclaw.cloud/auth/verify?token=${magicLinkResult.token}`;
|
|
1852
|
-
|
|
1865
|
+
}) as { code: string; expiresAt: number };
|
|
1866
|
+
|
|
1867
|
+
// Send OTP email
|
|
1853
1868
|
const emailResponse = await fetch('https://api.resend.com/emails', {
|
|
1854
1869
|
method: 'POST',
|
|
1855
1870
|
headers: {
|
|
@@ -1859,25 +1874,42 @@ Docs: https://apiclaw.cloud
|
|
|
1859
1874
|
body: JSON.stringify({
|
|
1860
1875
|
from: 'APIClaw <noreply@apiclaw.cloud>',
|
|
1861
1876
|
to: email,
|
|
1862
|
-
subject:
|
|
1863
|
-
html:
|
|
1877
|
+
subject: `Your APIClaw verification code: ${otpResult.code}`,
|
|
1878
|
+
html: `
|
|
1879
|
+
<div style="font-family: Inter, sans-serif; max-width: 480px; margin: 0 auto; padding: 40px 24px;">
|
|
1880
|
+
<div style="text-align: center; margin-bottom: 32px;">
|
|
1881
|
+
<span style="font-size: 48px;">🦞</span>
|
|
1882
|
+
</div>
|
|
1883
|
+
<h1 style="font-size: 24px; font-weight: 700; color: #0A0A0A; text-align: center; margin-bottom: 8px;">Your verification code</h1>
|
|
1884
|
+
<p style="font-size: 16px; color: #525252; text-align: center; margin-bottom: 32px;">Paste this code in your terminal to activate APIClaw.</p>
|
|
1885
|
+
<div style="background: #F5F5F5; border: 1px solid #E5E5E5; border-radius: 12px; padding: 24px; text-align: center; margin-bottom: 24px;">
|
|
1886
|
+
<code style="font-size: 36px; font-weight: 700; letter-spacing: 0.3em; color: #EF4444; font-family: 'JetBrains Mono', monospace;">${otpResult.code}</code>
|
|
1887
|
+
</div>
|
|
1888
|
+
<p style="font-size: 13px; color: #737373; text-align: center;">This code expires in 10 minutes. If you didn't request this, ignore this email.</p>
|
|
1889
|
+
<hr style="border: none; border-top: 1px solid #E5E5E5; margin: 32px 0 16px;" />
|
|
1890
|
+
<p style="font-size: 12px; color: #A3A3A3; text-align: center;">APIClaw -- The API Layer For AI Agents</p>
|
|
1891
|
+
</div>
|
|
1892
|
+
`
|
|
1864
1893
|
})
|
|
1865
1894
|
});
|
|
1866
|
-
|
|
1895
|
+
|
|
1867
1896
|
if (!emailResponse.ok) {
|
|
1868
1897
|
const errorData = await emailResponse.text();
|
|
1869
1898
|
throw new Error(`Failed to send verification email: ${errorData}`);
|
|
1870
1899
|
}
|
|
1871
|
-
|
|
1900
|
+
|
|
1901
|
+
// Store pending email for verify_code
|
|
1902
|
+
pendingRegistrationEmail = email;
|
|
1903
|
+
|
|
1872
1904
|
return {
|
|
1873
1905
|
content: [{
|
|
1874
1906
|
type: 'text',
|
|
1875
1907
|
text: JSON.stringify({
|
|
1876
|
-
status: '
|
|
1877
|
-
message:
|
|
1908
|
+
status: 'code_sent',
|
|
1909
|
+
message: `Verification code sent to ${email}`,
|
|
1910
|
+
next_step: 'Ask the user to check their email for a 6-digit code, then call verify_code with the email and code.',
|
|
1878
1911
|
email,
|
|
1879
|
-
expires_in_minutes:
|
|
1880
|
-
next_step: 'Check your email, click the verification link, then run check_workspace_status',
|
|
1912
|
+
expires_in_minutes: 10,
|
|
1881
1913
|
}, null, 2)
|
|
1882
1914
|
}]
|
|
1883
1915
|
};
|
|
@@ -1894,6 +1926,119 @@ Docs: https://apiclaw.cloud
|
|
|
1894
1926
|
};
|
|
1895
1927
|
}
|
|
1896
1928
|
}
|
|
1929
|
+
|
|
1930
|
+
case 'verify_code': {
|
|
1931
|
+
const email = (args?.email as string) || pendingRegistrationEmail;
|
|
1932
|
+
const code = args?.code as string;
|
|
1933
|
+
|
|
1934
|
+
if (!email || !code) {
|
|
1935
|
+
return {
|
|
1936
|
+
content: [{
|
|
1937
|
+
type: 'text',
|
|
1938
|
+
text: JSON.stringify({
|
|
1939
|
+
status: 'error',
|
|
1940
|
+
error: 'Both email and code are required.',
|
|
1941
|
+
hint: 'Call register_owner first to receive a verification code.',
|
|
1942
|
+
}, null, 2)
|
|
1943
|
+
}],
|
|
1944
|
+
isError: true
|
|
1945
|
+
};
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
try {
|
|
1949
|
+
const fingerprint = getMachineFingerprint();
|
|
1950
|
+
const result = await convex.mutation("workspaces:verifyOTP" as any, {
|
|
1951
|
+
email,
|
|
1952
|
+
code: code.trim(),
|
|
1953
|
+
fingerprint,
|
|
1954
|
+
}) as {
|
|
1955
|
+
success: boolean;
|
|
1956
|
+
error?: string;
|
|
1957
|
+
message?: string;
|
|
1958
|
+
isNewUser?: boolean;
|
|
1959
|
+
sessionToken?: string;
|
|
1960
|
+
workspace?: { id: string; email: string; tier: string; status: string; usageCount: number; usageLimit: number }
|
|
1961
|
+
};
|
|
1962
|
+
|
|
1963
|
+
if (!result.success) {
|
|
1964
|
+
// Increment attempt counter
|
|
1965
|
+
try {
|
|
1966
|
+
await convex.mutation("workspaces:incrementOTPAttempt" as any, { email, code: code.trim() });
|
|
1967
|
+
} catch (_) {}
|
|
1968
|
+
|
|
1969
|
+
return {
|
|
1970
|
+
content: [{
|
|
1971
|
+
type: 'text',
|
|
1972
|
+
text: JSON.stringify({
|
|
1973
|
+
status: 'error',
|
|
1974
|
+
error: result.message || 'Verification failed',
|
|
1975
|
+
hint: result.error === 'code_expired'
|
|
1976
|
+
? 'Run register_owner again to get a new code.'
|
|
1977
|
+
: 'Check the code and try again.',
|
|
1978
|
+
}, null, 2)
|
|
1979
|
+
}],
|
|
1980
|
+
isError: true
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
// Success! Save session
|
|
1985
|
+
writeSession(result.sessionToken!, result.workspace!.id, result.workspace!.email);
|
|
1986
|
+
|
|
1987
|
+
// Claim anonymous usage
|
|
1988
|
+
try {
|
|
1989
|
+
const claimResult = await convex.mutation("workspaces:claimAnonymousUsage" as any, {
|
|
1990
|
+
workspaceId: result.workspace!.id,
|
|
1991
|
+
machineFingerprint: fingerprint,
|
|
1992
|
+
}) as { success: boolean; claimedCount?: number };
|
|
1993
|
+
if (claimResult.success && claimResult.claimedCount) {
|
|
1994
|
+
console.error(`[APIClaw] Claimed ${claimResult.claimedCount} anonymous usage records`);
|
|
1995
|
+
}
|
|
1996
|
+
} catch (_) {}
|
|
1997
|
+
|
|
1998
|
+
// Update global context
|
|
1999
|
+
workspaceContext = {
|
|
2000
|
+
sessionToken: result.sessionToken!,
|
|
2001
|
+
workspaceId: result.workspace!.id,
|
|
2002
|
+
email: result.workspace!.email,
|
|
2003
|
+
tier: result.workspace!.tier,
|
|
2004
|
+
usageRemaining: result.workspace!.usageLimit - result.workspace!.usageCount,
|
|
2005
|
+
usageCount: result.workspace!.usageCount,
|
|
2006
|
+
status: result.workspace!.status,
|
|
2007
|
+
};
|
|
2008
|
+
|
|
2009
|
+
pendingRegistrationEmail = null;
|
|
2010
|
+
|
|
2011
|
+
return {
|
|
2012
|
+
content: [{
|
|
2013
|
+
type: 'text',
|
|
2014
|
+
text: JSON.stringify({
|
|
2015
|
+
status: 'success',
|
|
2016
|
+
message: result.isNewUser
|
|
2017
|
+
? `Welcome to APIClaw! Workspace activated for ${result.workspace!.email}`
|
|
2018
|
+
: `Welcome back! Authenticated as ${result.workspace!.email}`,
|
|
2019
|
+
workspace: {
|
|
2020
|
+
email: result.workspace!.email,
|
|
2021
|
+
tier: result.workspace!.tier,
|
|
2022
|
+
usageCount: result.workspace!.usageCount,
|
|
2023
|
+
usageLimit: result.workspace!.usageLimit,
|
|
2024
|
+
},
|
|
2025
|
+
ready: 'You can now use discover_apis and call_api.',
|
|
2026
|
+
}, null, 2)
|
|
2027
|
+
}]
|
|
2028
|
+
};
|
|
2029
|
+
} catch (error) {
|
|
2030
|
+
return {
|
|
2031
|
+
content: [{
|
|
2032
|
+
type: 'text',
|
|
2033
|
+
text: JSON.stringify({
|
|
2034
|
+
status: 'error',
|
|
2035
|
+
error: error instanceof Error ? error.message : 'Verification failed',
|
|
2036
|
+
}, null, 2)
|
|
2037
|
+
}],
|
|
2038
|
+
isError: true
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
1897
2042
|
|
|
1898
2043
|
case 'check_workspace_status': {
|
|
1899
2044
|
// Check if we have a local session
|