@nordsym/apiclaw 1.8.6 → 1.8.7
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 +4 -4
- package/apiclaw-README.md +12 -12
- package/convex/_generated/api.d.ts +4 -0
- package/convex/apiKeys.ts +220 -0
- package/convex/directCall.ts +49 -14
- package/convex/email.ts +5 -5
- package/convex/http.ts +598 -40
- package/convex/logs.ts +26 -22
- package/convex/migrateProviderWorkspaces.ts +154 -35
- package/convex/providers.ts +313 -203
- package/convex/schema.ts +50 -1
- package/convex/searchLogs.ts +2 -6
- package/convex/seedPratham.ts +1 -1
- package/convex/spendAlerts.ts +2 -2
- package/convex/stripeActions.ts +1 -1
- package/convex/updateAPIStatus.ts +2 -3
- package/convex/workspaceSettings.ts +136 -0
- package/dist/cli/commands/demo.js +2 -2
- package/dist/cli/commands/doctor.js +1 -1
- package/dist/cli/commands/login.js +1 -1
- package/dist/cli/commands/setup.js +2 -2
- package/dist/discovery.js +1 -1
- package/dist/execute.js +3 -3
- package/dist/index.js +13 -13
- package/dist/ui/errors.js +16 -16
- package/dist/ui/prompts.js +1 -1
- package/email-templates/filestack-provider-outreach.html +1 -1
- package/email-templates/partnership-template.html +1 -1
- package/email-templates/pratham-partnership-draft.html +2 -2
- package/package.json +2 -2
- package/reports/APIClaw-Session-Report-2026-04-05.pdf +0 -0
- package/reports/session-report-2026-04-05.html +433 -0
- package/src/cli/commands/demo.ts +2 -2
- package/src/cli/commands/doctor.ts +1 -1
- package/src/cli/commands/login.ts +1 -1
- package/src/cli/commands/setup.ts +2 -2
- package/src/discovery.ts +1 -1
- package/src/execute.ts +3 -3
- package/src/index.ts +14 -14
- package/src/ui/errors.ts +16 -16
- package/src/ui/prompts.ts +1 -1
- package/convex/adminActivate.d.ts +0 -3
- package/convex/adminActivate.js +0 -47
- package/convex/adminStats.d.ts +0 -9
- package/convex/adminStats.js +0 -280
- package/convex/agents.d.ts +0 -84
- package/convex/agents.js +0 -809
- package/convex/analytics.d.ts +0 -5
- package/convex/analytics.js +0 -167
- package/convex/backfillAnalytics.d.ts +0 -2
- package/convex/backfillAnalytics.js +0 -20
- package/convex/backfillSearchLogs.d.ts +0 -2
- package/convex/backfillSearchLogs.js +0 -29
- package/convex/billing.d.ts +0 -88
- package/convex/billing.js +0 -655
- package/convex/capabilities.d.ts +0 -9
- package/convex/capabilities.js +0 -145
- package/convex/chains.d.ts +0 -68
- package/convex/chains.js +0 -1105
- package/convex/credits.d.ts +0 -25
- package/convex/credits.js +0 -186
- package/convex/crons.d.ts +0 -3
- package/convex/crons.js +0 -17
- package/convex/debugFilestackLogs.d.ts +0 -2
- package/convex/debugFilestackLogs.js +0 -17
- package/convex/debugGetToken.d.ts +0 -2
- package/convex/debugGetToken.js +0 -18
- package/convex/directCall.d.ts +0 -72
- package/convex/directCall.js +0 -627
- package/convex/earnProgress.d.ts +0 -58
- package/convex/earnProgress.js +0 -649
- package/convex/email.d.ts +0 -14
- package/convex/email.js +0 -300
- package/convex/feedback.d.ts +0 -7
- package/convex/feedback.js +0 -227
- package/convex/http.d.ts +0 -3
- package/convex/http.js +0 -1408
- package/convex/inbound.d.ts +0 -2
- package/convex/inbound.js +0 -32
- package/convex/logs.d.ts +0 -48
- package/convex/logs.js +0 -621
- package/convex/migrateFilestack.d.ts +0 -2
- package/convex/migrateFilestack.js +0 -74
- package/convex/migratePartnersProd.d.ts +0 -8
- package/convex/migratePartnersProd.js +0 -165
- package/convex/migratePratham.d.ts +0 -2
- package/convex/migratePratham.js +0 -121
- package/convex/migrateProviderWorkspaces.d.ts +0 -2
- package/convex/migrateProviderWorkspaces.js +0 -55
- package/convex/mou.d.ts +0 -6
- package/convex/mou.js +0 -82
- package/convex/providerKeys.d.ts +0 -31
- package/convex/providerKeys.js +0 -257
- package/convex/providers.d.ts +0 -35
- package/convex/providers.js +0 -922
- package/convex/purchases.d.ts +0 -7
- package/convex/purchases.js +0 -157
- package/convex/ratelimit.d.ts +0 -4
- package/convex/ratelimit.js +0 -91
- package/convex/searchLogs.d.ts +0 -13
- package/convex/searchLogs.js +0 -246
- package/convex/seedAPILayerAPIs.d.ts +0 -7
- package/convex/seedAPILayerAPIs.js +0 -177
- package/convex/seedDirectCallConfigs.d.ts +0 -2
- package/convex/seedDirectCallConfigs.js +0 -324
- package/convex/seedPratham.d.ts +0 -6
- package/convex/seedPratham.js +0 -150
- package/convex/spendAlerts.d.ts +0 -36
- package/convex/spendAlerts.js +0 -380
- package/convex/stripeActions.d.ts +0 -19
- package/convex/stripeActions.js +0 -411
- package/convex/teams.d.ts +0 -21
- package/convex/teams.js +0 -215
- package/convex/telemetry.d.ts +0 -4
- package/convex/telemetry.js +0 -74
- package/convex/updateAPIStatus.d.ts +0 -6
- package/convex/updateAPIStatus.js +0 -40
- package/convex/usage.d.ts +0 -27
- package/convex/usage.js +0 -229
- package/convex/waitlist.d.ts +0 -4
- package/convex/waitlist.js +0 -49
- package/convex/webhooks.d.ts +0 -12
- package/convex/webhooks.js +0 -410
- package/convex/workspaces.d.ts +0 -33
- package/convex/workspaces.js +0 -991
|
@@ -284,14 +284,14 @@ export async function setupCommand(options: SetupOptions): Promise<void> {
|
|
|
284
284
|
console.log('Next steps:');
|
|
285
285
|
console.log(' 1. Restart your AI coding assistant');
|
|
286
286
|
console.log(' 2. Ask your agent: "List available APIs"\n');
|
|
287
|
-
console.log('Need help? https://apiclaw.
|
|
287
|
+
console.log('Need help? https://apiclaw.cloud/docs/setup\n');
|
|
288
288
|
} else if (skipped === results.length) {
|
|
289
289
|
console.log('\n✅ APIClaw already configured in all clients.\n');
|
|
290
290
|
console.log('Use --force to reconfigure.\n');
|
|
291
291
|
}
|
|
292
292
|
} else {
|
|
293
293
|
console.log(`\n⚠️ Setup completed with ${failed} error(s).\n`);
|
|
294
|
-
console.log('For troubleshooting, visit: https://apiclaw.
|
|
294
|
+
console.log('For troubleshooting, visit: https://apiclaw.cloud/docs/setup/troubleshooting\n');
|
|
295
295
|
process.exit(1);
|
|
296
296
|
}
|
|
297
297
|
}
|
package/src/discovery.ts
CHANGED
|
@@ -192,7 +192,7 @@ const DIRECT_CALL_SPECS: Record<string, {
|
|
|
192
192
|
{ name: 'subject', required: true, desc: 'Email subject' },
|
|
193
193
|
{ name: 'html', required: false, desc: 'HTML body' },
|
|
194
194
|
{ name: 'text', required: false, desc: 'Plain text body' },
|
|
195
|
-
{ name: 'from', required: false, desc: 'Sender (default: noreply@apiclaw.
|
|
195
|
+
{ name: 'from', required: false, desc: 'Sender (default: noreply@apiclaw.cloud)' },
|
|
196
196
|
],
|
|
197
197
|
},
|
|
198
198
|
},
|
package/src/execute.ts
CHANGED
|
@@ -272,7 +272,7 @@ const mockResponses: Record<string, Record<string, (params: any) => unknown>> =
|
|
|
272
272
|
send_email: (params) => ({
|
|
273
273
|
id: 'mock_email_' + Date.now(),
|
|
274
274
|
to: params.to,
|
|
275
|
-
from: params.from || 'APIClaw <noreply@apiclaw.
|
|
275
|
+
from: params.from || 'APIClaw <noreply@apiclaw.cloud>',
|
|
276
276
|
subject: params.subject,
|
|
277
277
|
status: 'sent',
|
|
278
278
|
}),
|
|
@@ -674,7 +674,7 @@ const handlers: Record<string, Record<string, (params: any, creds: any) => Promi
|
|
|
674
674
|
// Resend - Email
|
|
675
675
|
resend: {
|
|
676
676
|
send_email: async (params, creds) => {
|
|
677
|
-
const { to, subject, html, text, from = 'APIClaw <noreply@apiclaw.
|
|
677
|
+
const { to, subject, html, text, from = 'APIClaw <noreply@apiclaw.cloud>' } = params;
|
|
678
678
|
|
|
679
679
|
if (!to || !subject || (!html && !text)) {
|
|
680
680
|
return createErrorResult('resend', 'send_email', 'Missing required params: to, subject, html or text', ERROR_CODES.INVALID_PARAMS);
|
|
@@ -718,7 +718,7 @@ const handlers: Record<string, Record<string, (params: any, creds: any) => Promi
|
|
|
718
718
|
headers: {
|
|
719
719
|
'Authorization': `Bearer ${creds.api_key}`,
|
|
720
720
|
'Content-Type': 'application/json',
|
|
721
|
-
'HTTP-Referer': 'https://apiclaw.
|
|
721
|
+
'HTTP-Referer': 'https://apiclaw.cloud',
|
|
722
722
|
},
|
|
723
723
|
body: JSON.stringify({ model, messages, max_tokens }),
|
|
724
724
|
}, { provider: 'openrouter', action: 'chat' });
|
package/src/index.ts
CHANGED
|
@@ -170,10 +170,10 @@ function checkAnonymousRateLimit(fingerprint: string): { allowed: boolean; error
|
|
|
170
170
|
allowed: false,
|
|
171
171
|
error: JSON.stringify({
|
|
172
172
|
success: false,
|
|
173
|
-
error: `⚡ You've hit your free tier limit (${ANONYMOUS_WEEKLY_LIMIT} calls/week).\n Upgrade: https://apiclaw.
|
|
173
|
+
error: `⚡ You've hit your free tier limit (${ANONYMOUS_WEEKLY_LIMIT} calls/week).\n Upgrade: https://apiclaw.cloud/upgrade`,
|
|
174
174
|
hint: "Register for 50 calls/week, or upgrade for unlimited",
|
|
175
175
|
action: "Run: register_owner({ email: 'you@example.com' })",
|
|
176
|
-
upgrade_url: "https://apiclaw.
|
|
176
|
+
upgrade_url: "https://apiclaw.cloud/upgrade",
|
|
177
177
|
retry_after: getNextMonthUTC()
|
|
178
178
|
}, null, 2)
|
|
179
179
|
};
|
|
@@ -336,9 +336,9 @@ function checkWorkspaceAccess(providerId?: string): { allowed: boolean; error?:
|
|
|
336
336
|
allowed: false,
|
|
337
337
|
error: JSON.stringify({
|
|
338
338
|
success: false,
|
|
339
|
-
error: `⚡ You've hit your free tier limit (${FREE_MONTHLY_LIMIT} calls/week).\n Upgrade: https://apiclaw.
|
|
339
|
+
error: `⚡ You've hit your free tier limit (${FREE_MONTHLY_LIMIT} calls/week).\n Upgrade: https://apiclaw.cloud/upgrade`,
|
|
340
340
|
hint: "Upgrade to Backer for unlimited calls",
|
|
341
|
-
upgrade_url: "https://apiclaw.
|
|
341
|
+
upgrade_url: "https://apiclaw.cloud/upgrade",
|
|
342
342
|
retry_after: getNextMonthUTC()
|
|
343
343
|
}, null, 2)
|
|
344
344
|
};
|
|
@@ -347,7 +347,7 @@ function checkWorkspaceAccess(providerId?: string): { allowed: boolean; error?:
|
|
|
347
347
|
// Other tiers (shouldn't happen, but handle gracefully)
|
|
348
348
|
return {
|
|
349
349
|
allowed: false,
|
|
350
|
-
error: `⚡ You've hit your free tier limit (${FREE_MONTHLY_LIMIT} calls/week).\n Upgrade: https://apiclaw.
|
|
350
|
+
error: `⚡ You've hit your free tier limit (${FREE_MONTHLY_LIMIT} calls/week).\n Upgrade: https://apiclaw.cloud/upgrade`
|
|
351
351
|
};
|
|
352
352
|
}
|
|
353
353
|
|
|
@@ -724,12 +724,12 @@ Example chain:
|
|
|
724
724
|
success_url: {
|
|
725
725
|
type: 'string',
|
|
726
726
|
description: 'URL to redirect after successful setup',
|
|
727
|
-
default: 'https://apiclaw.
|
|
727
|
+
default: 'https://apiclaw.cloud/billing/success'
|
|
728
728
|
},
|
|
729
729
|
cancel_url: {
|
|
730
730
|
type: 'string',
|
|
731
731
|
description: 'URL to redirect if setup is cancelled',
|
|
732
|
-
default: 'https://apiclaw.
|
|
732
|
+
default: 'https://apiclaw.cloud/billing/cancel'
|
|
733
733
|
}
|
|
734
734
|
},
|
|
735
735
|
required: ['email']
|
|
@@ -862,7 +862,7 @@ BROWSE:
|
|
|
862
862
|
list_categories()
|
|
863
863
|
list_all_apis({ category: "communication", limit: 20 })
|
|
864
864
|
|
|
865
|
-
Docs: https://apiclaw.
|
|
865
|
+
Docs: https://apiclaw.cloud
|
|
866
866
|
`;
|
|
867
867
|
return {
|
|
868
868
|
content: [{ type: 'text', text: helpText }]
|
|
@@ -1804,7 +1804,7 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1804
1804
|
}) as { token: string; expiresAt: number };
|
|
1805
1805
|
|
|
1806
1806
|
// Send magic link via email
|
|
1807
|
-
const verifyUrl = `https://apiclaw.
|
|
1807
|
+
const verifyUrl = `https://apiclaw.cloud/auth/verify?token=${magicLinkResult.token}`;
|
|
1808
1808
|
|
|
1809
1809
|
const emailResponse = await fetch('https://api.resend.com/emails', {
|
|
1810
1810
|
method: 'POST',
|
|
@@ -1813,7 +1813,7 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1813
1813
|
'Content-Type': 'application/json',
|
|
1814
1814
|
},
|
|
1815
1815
|
body: JSON.stringify({
|
|
1816
|
-
from: 'APIClaw <noreply@apiclaw.
|
|
1816
|
+
from: 'APIClaw <noreply@apiclaw.cloud>',
|
|
1817
1817
|
to: email,
|
|
1818
1818
|
subject: 'Verify your APIClaw workspace',
|
|
1819
1819
|
html: `<p>Click to verify: <a href="${verifyUrl}">${verifyUrl}</a></p><p>Expires in 15 minutes.</p>`
|
|
@@ -1975,7 +1975,7 @@ Docs: https://apiclaw.nordsym.com
|
|
|
1975
1975
|
}) as { token: string; expiresAt: number };
|
|
1976
1976
|
|
|
1977
1977
|
// TODO: Agent 2 will implement actual email sending
|
|
1978
|
-
const verifyUrl = `https://apiclaw.
|
|
1978
|
+
const verifyUrl = `https://apiclaw.cloud/auth/verify?token=${magicLinkResult.token}`;
|
|
1979
1979
|
|
|
1980
1980
|
return {
|
|
1981
1981
|
content: [{
|
|
@@ -2037,8 +2037,8 @@ Docs: https://apiclaw.nordsym.com
|
|
|
2037
2037
|
// Create checkout session for metered subscription
|
|
2038
2038
|
const checkoutResult = await createMeteredCheckoutSession(
|
|
2039
2039
|
email,
|
|
2040
|
-
success_url || 'https://apiclaw.
|
|
2041
|
-
cancel_url || 'https://apiclaw.
|
|
2040
|
+
success_url || 'https://apiclaw.cloud/billing/success',
|
|
2041
|
+
cancel_url || 'https://apiclaw.cloud/billing/cancel'
|
|
2042
2042
|
);
|
|
2043
2043
|
|
|
2044
2044
|
if ('error' in checkoutResult) {
|
|
@@ -2414,7 +2414,7 @@ Direct Call (no API key needed):
|
|
|
2414
2414
|
Interactive CLI mode:
|
|
2415
2415
|
npx @nordsym/apiclaw --cli
|
|
2416
2416
|
|
|
2417
|
-
Docs: https://apiclaw.
|
|
2417
|
+
Docs: https://apiclaw.cloud
|
|
2418
2418
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
2419
2419
|
`);
|
|
2420
2420
|
}
|
package/src/ui/errors.ts
CHANGED
|
@@ -80,7 +80,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
80
80
|
'npx @nordsym/apiclaw setup --client cursor',
|
|
81
81
|
'npx @nordsym/apiclaw setup --config /path/to/config.json',
|
|
82
82
|
],
|
|
83
|
-
helpUrl: 'https://apiclaw.
|
|
83
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/config-not-found',
|
|
84
84
|
},
|
|
85
85
|
|
|
86
86
|
[ErrorCode.PERMISSION_DENIED]: {
|
|
@@ -95,7 +95,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
95
95
|
'sudo npx @nordsym/apiclaw setup',
|
|
96
96
|
'chmod 644 <config-path>',
|
|
97
97
|
],
|
|
98
|
-
helpUrl: 'https://apiclaw.
|
|
98
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/permissions',
|
|
99
99
|
},
|
|
100
100
|
|
|
101
101
|
[ErrorCode.INVALID_JSON]: {
|
|
@@ -110,7 +110,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
110
110
|
'npx @nordsym/apiclaw restore --list',
|
|
111
111
|
'npx @nordsym/apiclaw restore',
|
|
112
112
|
],
|
|
113
|
-
helpUrl: 'https://apiclaw.
|
|
113
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/invalid-json',
|
|
114
114
|
},
|
|
115
115
|
|
|
116
116
|
[ErrorCode.WRITE_FAILED]: {
|
|
@@ -121,7 +121,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
121
121
|
'Ensure the config directory exists',
|
|
122
122
|
'Verify write permissions',
|
|
123
123
|
],
|
|
124
|
-
helpUrl: 'https://apiclaw.
|
|
124
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/write-failed',
|
|
125
125
|
},
|
|
126
126
|
|
|
127
127
|
[ErrorCode.BACKUP_FAILED]: {
|
|
@@ -132,7 +132,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
132
132
|
'Verify write permissions in config directory',
|
|
133
133
|
'Use --no-backup to skip backup (not recommended)',
|
|
134
134
|
],
|
|
135
|
-
helpUrl: 'https://apiclaw.
|
|
135
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/backup-failed',
|
|
136
136
|
},
|
|
137
137
|
|
|
138
138
|
[ErrorCode.ALREADY_CONFIGURED]: {
|
|
@@ -146,7 +146,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
146
146
|
'npx @nordsym/apiclaw setup --force',
|
|
147
147
|
'npx @nordsym/apiclaw doctor',
|
|
148
148
|
],
|
|
149
|
-
helpUrl: 'https://apiclaw.
|
|
149
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/already-configured',
|
|
150
150
|
},
|
|
151
151
|
|
|
152
152
|
[ErrorCode.INVALID_CONFIG]: {
|
|
@@ -156,7 +156,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
156
156
|
'Ensure the config follows the MCP specification',
|
|
157
157
|
'Check for required fields like "mcpServers"',
|
|
158
158
|
],
|
|
159
|
-
helpUrl: 'https://apiclaw.
|
|
159
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/invalid-config',
|
|
160
160
|
},
|
|
161
161
|
|
|
162
162
|
[ErrorCode.MERGE_CONFLICT]: {
|
|
@@ -167,7 +167,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
167
167
|
'Use --force to overwrite',
|
|
168
168
|
'Create a backup and try again',
|
|
169
169
|
],
|
|
170
|
-
helpUrl: 'https://apiclaw.
|
|
170
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/merge-conflict',
|
|
171
171
|
},
|
|
172
172
|
|
|
173
173
|
[ErrorCode.CLIENT_NOT_FOUND]: {
|
|
@@ -182,7 +182,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
182
182
|
'npx @nordsym/apiclaw setup --client claude-desktop',
|
|
183
183
|
'npx @nordsym/apiclaw doctor',
|
|
184
184
|
],
|
|
185
|
-
helpUrl: 'https://apiclaw.
|
|
185
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/client-not-found',
|
|
186
186
|
},
|
|
187
187
|
|
|
188
188
|
[ErrorCode.UNSUPPORTED_CLIENT]: {
|
|
@@ -193,7 +193,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
193
193
|
'Use --config to manually specify the config path',
|
|
194
194
|
'Request support at github.com/nordsym/apiclaw',
|
|
195
195
|
],
|
|
196
|
-
helpUrl: 'https://apiclaw.
|
|
196
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/supported-clients',
|
|
197
197
|
},
|
|
198
198
|
|
|
199
199
|
[ErrorCode.NETWORK_ERROR]: {
|
|
@@ -205,10 +205,10 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
205
205
|
'Check if APIClaw is reachable',
|
|
206
206
|
],
|
|
207
207
|
commands: [
|
|
208
|
-
'curl -I https://apiclaw.
|
|
208
|
+
'curl -I https://apiclaw.cloud',
|
|
209
209
|
'npx @nordsym/apiclaw doctor',
|
|
210
210
|
],
|
|
211
|
-
helpUrl: 'https://apiclaw.
|
|
211
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/network-error',
|
|
212
212
|
},
|
|
213
213
|
|
|
214
214
|
[ErrorCode.AUTH_FAILED]: {
|
|
@@ -219,7 +219,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
219
219
|
'Verify your API key is valid',
|
|
220
220
|
'Setup works without auth - configure credentials later',
|
|
221
221
|
],
|
|
222
|
-
helpUrl: 'https://apiclaw.
|
|
222
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/auth-failed',
|
|
223
223
|
},
|
|
224
224
|
|
|
225
225
|
[ErrorCode.UNSUPPORTED_OS]: {
|
|
@@ -229,7 +229,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
229
229
|
'Supported: macOS, Windows 10/11, Linux',
|
|
230
230
|
'Manual setup instructions available',
|
|
231
231
|
],
|
|
232
|
-
helpUrl: 'https://apiclaw.
|
|
232
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/manual',
|
|
233
233
|
},
|
|
234
234
|
|
|
235
235
|
[ErrorCode.NODE_VERSION]: {
|
|
@@ -243,7 +243,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
243
243
|
'nvm install 18',
|
|
244
244
|
'nvm use 18',
|
|
245
245
|
],
|
|
246
|
-
helpUrl: 'https://apiclaw.
|
|
246
|
+
helpUrl: 'https://apiclaw.cloud/docs/setup/node-version',
|
|
247
247
|
},
|
|
248
248
|
|
|
249
249
|
[ErrorCode.UNKNOWN]: {
|
|
@@ -257,7 +257,7 @@ const errorMeta: Record<ErrorCode, ErrorMeta> = {
|
|
|
257
257
|
commands: [
|
|
258
258
|
'npx @nordsym/apiclaw setup --verbose',
|
|
259
259
|
],
|
|
260
|
-
helpUrl: 'https://apiclaw.
|
|
260
|
+
helpUrl: 'https://apiclaw.cloud/docs/support',
|
|
261
261
|
},
|
|
262
262
|
};
|
|
263
263
|
|
package/src/ui/prompts.ts
CHANGED
|
@@ -249,7 +249,7 @@ export function showSuccess(configuredClients: string[]): void {
|
|
|
249
249
|
], 2));
|
|
250
250
|
|
|
251
251
|
console.log(colors.secondary('\nNeed help?'));
|
|
252
|
-
console.log(` ${colors.link('https://apiclaw.
|
|
252
|
+
console.log(` ${colors.link('https://apiclaw.cloud/docs/setup')}\n`);
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
/**
|
package/convex/adminActivate.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { mutation } from "./_generated/server";
|
|
2
|
-
import { v } from "convex/values";
|
|
3
|
-
export const activateWorkspace = mutation({
|
|
4
|
-
args: { workspaceId: v.id("workspaces") },
|
|
5
|
-
handler: async (ctx, { workspaceId }) => {
|
|
6
|
-
const workspace = await ctx.db.get(workspaceId);
|
|
7
|
-
if (!workspace) {
|
|
8
|
-
return { success: false, error: "not_found" };
|
|
9
|
-
}
|
|
10
|
-
await ctx.db.patch(workspaceId, {
|
|
11
|
-
status: "active",
|
|
12
|
-
tier: "backer", // Give Hivr bees backer status
|
|
13
|
-
weeklyUsageLimit: 999999,
|
|
14
|
-
usageLimit: 999999,
|
|
15
|
-
backerUntil: new Date("2026-12-31T23:59:59Z").getTime(), // Founding Backer until end of 2026
|
|
16
|
-
updatedAt: Date.now(),
|
|
17
|
-
});
|
|
18
|
-
return { success: true };
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
function generateToken() {
|
|
22
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
23
|
-
let result = '';
|
|
24
|
-
for (let i = 0; i < 32; i++) {
|
|
25
|
-
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
26
|
-
}
|
|
27
|
-
return result;
|
|
28
|
-
}
|
|
29
|
-
export const createSessionForWorkspace = mutation({
|
|
30
|
-
args: { workspaceId: v.id("workspaces") },
|
|
31
|
-
handler: async (ctx, { workspaceId }) => {
|
|
32
|
-
const workspace = await ctx.db.get(workspaceId);
|
|
33
|
-
if (!workspace || workspace.status !== "active") {
|
|
34
|
-
return { success: false, error: "workspace_not_active" };
|
|
35
|
-
}
|
|
36
|
-
const sessionToken = "apiclaw_" + generateToken();
|
|
37
|
-
await ctx.db.insert("agentSessions", {
|
|
38
|
-
workspaceId,
|
|
39
|
-
sessionToken,
|
|
40
|
-
fingerprint: "hivr-bees",
|
|
41
|
-
lastUsedAt: Date.now(),
|
|
42
|
-
createdAt: Date.now(),
|
|
43
|
-
});
|
|
44
|
-
return { success: true, sessionToken };
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
//# sourceMappingURL=adminActivate.js.map
|
package/convex/adminStats.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export declare const getTotalWorkspaces: any;
|
|
2
|
-
export declare const listWorkspaces: any;
|
|
3
|
-
export declare const cleanupWorkspaces: any;
|
|
4
|
-
export declare const updateProviderEmail: any;
|
|
5
|
-
export declare const seedFilestackWorkspace: any;
|
|
6
|
-
export declare const cleanFilestackSeedTokens: any;
|
|
7
|
-
export declare const countLogsForWorkspace: any;
|
|
8
|
-
export declare const dedupeFilestackLogs: any;
|
|
9
|
-
//# sourceMappingURL=adminStats.d.ts.map
|
package/convex/adminStats.js
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import { query, mutation } from "./_generated/server";
|
|
2
|
-
import { v } from "convex/values";
|
|
3
|
-
// Get total user/workspace count
|
|
4
|
-
export const getTotalWorkspaces = query({
|
|
5
|
-
args: {},
|
|
6
|
-
handler: async (ctx) => {
|
|
7
|
-
const workspaces = await ctx.db.query("workspaces").collect();
|
|
8
|
-
const providers = await ctx.db.query("providers").collect();
|
|
9
|
-
return {
|
|
10
|
-
totalWorkspaces: workspaces.length,
|
|
11
|
-
totalProviders: providers.length,
|
|
12
|
-
activeWorkspaces: workspaces.filter(w => w.status === "active").length,
|
|
13
|
-
backers: workspaces.filter(w => w.tier === "backer").length,
|
|
14
|
-
workspaceBreakdown: {
|
|
15
|
-
free: workspaces.filter(w => w.tier === "free").length,
|
|
16
|
-
pro: workspaces.filter(w => w.tier === "pro").length,
|
|
17
|
-
enterprise: workspaces.filter(w => w.tier === "enterprise").length,
|
|
18
|
-
backer: workspaces.filter(w => w.tier === "backer").length,
|
|
19
|
-
},
|
|
20
|
-
providerBreakdown: {
|
|
21
|
-
pending: providers.filter(p => p.status === "pending").length,
|
|
22
|
-
approved: providers.filter(p => p.status === "approved").length,
|
|
23
|
-
rejected: providers.filter(p => p.status === "rejected").length,
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
// List all workspace emails (for inspection)
|
|
29
|
-
export const listWorkspaces = query({
|
|
30
|
-
args: {},
|
|
31
|
-
handler: async (ctx) => {
|
|
32
|
-
const workspaces = await ctx.db.query("workspaces").collect();
|
|
33
|
-
return workspaces.map(w => ({
|
|
34
|
-
email: w.email,
|
|
35
|
-
status: w.status,
|
|
36
|
-
tier: w.tier,
|
|
37
|
-
usageCount: w.usageCount,
|
|
38
|
-
createdAt: w.createdAt,
|
|
39
|
-
lastActiveAt: w.lastActiveAt,
|
|
40
|
-
}));
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
// Delete workspace by email or empty email ghosts
|
|
44
|
-
export const cleanupWorkspaces = mutation({
|
|
45
|
-
args: {
|
|
46
|
-
deleteEmptyEmail: v.optional(v.boolean()),
|
|
47
|
-
deleteEmail: v.optional(v.string()),
|
|
48
|
-
deleteEmailWithTier: v.optional(v.string()),
|
|
49
|
-
activateEmail: v.optional(v.string()),
|
|
50
|
-
},
|
|
51
|
-
handler: async (ctx, { deleteEmptyEmail, deleteEmail, deleteEmailWithTier, activateEmail }) => {
|
|
52
|
-
const workspaces = await ctx.db.query("workspaces").collect();
|
|
53
|
-
let deleted = 0;
|
|
54
|
-
// Activate a pending workspace
|
|
55
|
-
if (activateEmail) {
|
|
56
|
-
for (const ws of workspaces) {
|
|
57
|
-
if (ws.email === activateEmail && ws.status === "pending") {
|
|
58
|
-
await ctx.db.patch(ws._id, { status: "active" });
|
|
59
|
-
return { activated: activateEmail };
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return { error: "not found or not pending" };
|
|
63
|
-
}
|
|
64
|
-
for (const ws of workspaces) {
|
|
65
|
-
let shouldDelete = false;
|
|
66
|
-
if (deleteEmptyEmail && (!ws.email || ws.email === "")) {
|
|
67
|
-
shouldDelete = true;
|
|
68
|
-
}
|
|
69
|
-
if (deleteEmail && ws.email === deleteEmail) {
|
|
70
|
-
shouldDelete = true;
|
|
71
|
-
}
|
|
72
|
-
// Delete specific email+tier combo (e.g. remove free duplicate but keep founder)
|
|
73
|
-
if (deleteEmailWithTier) {
|
|
74
|
-
const [email, tier] = deleteEmailWithTier.split(":");
|
|
75
|
-
if (ws.email === email && ws.tier === tier) {
|
|
76
|
-
shouldDelete = true;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
if (shouldDelete) {
|
|
80
|
-
// Delete associated sessions
|
|
81
|
-
const sessions = await ctx.db.query("sessions").collect();
|
|
82
|
-
for (const s of sessions) {
|
|
83
|
-
if (s.workspaceId === ws._id) {
|
|
84
|
-
await ctx.db.delete(s._id);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Delete associated agents
|
|
88
|
-
const agents = await ctx.db.query("agents").collect();
|
|
89
|
-
for (const a of agents) {
|
|
90
|
-
if (a.workspaceId === ws._id) {
|
|
91
|
-
await ctx.db.delete(a._id);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
await ctx.db.delete(ws._id);
|
|
95
|
-
deleted++;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return { deleted };
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
export const updateProviderEmail = mutation({
|
|
102
|
-
args: { providerId: v.string(), email: v.string() },
|
|
103
|
-
handler: async (ctx, { providerId, email }) => {
|
|
104
|
-
await ctx.db.patch(providerId, { email });
|
|
105
|
-
return { success: true };
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
// Seed Filestack workspace + 14 days of discovery data
|
|
109
|
-
export const seedFilestackWorkspace = mutation({
|
|
110
|
-
args: {},
|
|
111
|
-
handler: async (ctx) => {
|
|
112
|
-
// 1. Create or update Filestack workspace
|
|
113
|
-
const existing = await ctx.db
|
|
114
|
-
.query("workspaces")
|
|
115
|
-
.withIndex("by_email", (q) => q.eq("email", "marketing@filestack.com"))
|
|
116
|
-
.first();
|
|
117
|
-
let workspaceId = existing?._id;
|
|
118
|
-
if (!workspaceId) {
|
|
119
|
-
workspaceId = await ctx.db.insert("workspaces", {
|
|
120
|
-
email: "marketing@filestack.com",
|
|
121
|
-
workspaceName: "Filestack",
|
|
122
|
-
status: "active",
|
|
123
|
-
tier: "partner",
|
|
124
|
-
usageCount: 0,
|
|
125
|
-
usageLimit: 999999,
|
|
126
|
-
weeklyUsageLimit: 999999,
|
|
127
|
-
mainAgentName: "Filestack Partner",
|
|
128
|
-
createdAt: Date.now(),
|
|
129
|
-
updatedAt: Date.now(),
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
await ctx.db.patch(workspaceId, {
|
|
134
|
-
status: "active",
|
|
135
|
-
tier: "partner",
|
|
136
|
-
workspaceName: "Filestack",
|
|
137
|
-
updatedAt: Date.now(),
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
// 2. Seed 14 days of realistic discovery data
|
|
141
|
-
const logs = [
|
|
142
|
-
{ action: "discovery:virus scan uploaded files", createdAt: 1775238013202, latencyMs: 25 },
|
|
143
|
-
{ action: "discovery:OCR document scan", createdAt: 1775226543202, latencyMs: 36 },
|
|
144
|
-
{ action: "discovery:file upload cdn delivery", createdAt: 1775214346202, latencyMs: 58 },
|
|
145
|
-
{ action: "discovery:upload images users", createdAt: 1775208102202, latencyMs: 13 },
|
|
146
|
-
{ action: "discovery:image transformation api", createdAt: 1775196892202, latencyMs: 18 },
|
|
147
|
-
{ action: "discovery:file upload api", createdAt: 1775194022202, latencyMs: 63 },
|
|
148
|
-
{ action: "discovery:file upload cdn delivery", createdAt: 1775191598202, latencyMs: 63 },
|
|
149
|
-
{ action: "discovery:image transformation api", createdAt: 1775146832202, latencyMs: 20 },
|
|
150
|
-
{ action: "discovery:file upload cdn delivery", createdAt: 1775141820202, latencyMs: 57 },
|
|
151
|
-
{ action: "discovery:virus scan uploaded files", createdAt: 1775112879202, latencyMs: 15 },
|
|
152
|
-
{ action: "discovery:handle user file uploads", createdAt: 1775110663202, latencyMs: 38 },
|
|
153
|
-
{ action: "discovery:file management api", createdAt: 1775029221202, latencyMs: 23 },
|
|
154
|
-
{ action: "discovery:file upload api", createdAt: 1775027445202, latencyMs: 41 },
|
|
155
|
-
{ action: "discovery:upload files from browser", createdAt: 1775019216202, latencyMs: 43 },
|
|
156
|
-
{ action: "discovery:image upload and transform", createdAt: 1774978691202, latencyMs: 65 },
|
|
157
|
-
{ action: "discovery:image upload and transform", createdAt: 1774976661202, latencyMs: 51 },
|
|
158
|
-
{ action: "discovery:file picker widget", createdAt: 1774972305202, latencyMs: 22 },
|
|
159
|
-
{ action: "discovery:image upload and transform", createdAt: 1774963193202, latencyMs: 51 },
|
|
160
|
-
{ action: "discovery:file picker widget", createdAt: 1774941922202, latencyMs: 41 },
|
|
161
|
-
{ action: "discovery:file picker widget", createdAt: 1774935674202, latencyMs: 39 },
|
|
162
|
-
{ action: "discovery:file management api", createdAt: 1774889668202, latencyMs: 21 },
|
|
163
|
-
{ action: "discovery:resize image on upload", createdAt: 1774858239202, latencyMs: 29 },
|
|
164
|
-
{ action: "discovery:upload images users", createdAt: 1774804384202, latencyMs: 21 },
|
|
165
|
-
{ action: "discovery:secure file upload", createdAt: 1774784782202, latencyMs: 18 },
|
|
166
|
-
{ action: "discovery:upload images users", createdAt: 1774720815202, latencyMs: 20 },
|
|
167
|
-
{ action: "discovery:secure file upload", createdAt: 1774699761202, latencyMs: 20 },
|
|
168
|
-
{ action: "discovery:file storage cloud", createdAt: 1774696012202, latencyMs: 16 },
|
|
169
|
-
{ action: "discovery:upload transform deliver files", createdAt: 1774693456202, latencyMs: 39 },
|
|
170
|
-
{ action: "discovery:secure file upload", createdAt: 1774672000202, latencyMs: 49 },
|
|
171
|
-
{ action: "discovery:handle user file uploads", createdAt: 1774631829202, latencyMs: 29 },
|
|
172
|
-
{ action: "discovery:file storage cloud", createdAt: 1774622378202, latencyMs: 52 },
|
|
173
|
-
{ action: "discovery:image transformation api", createdAt: 1774591324202, latencyMs: 30 },
|
|
174
|
-
{ action: "discovery:file picker widget", createdAt: 1774549274202, latencyMs: 49 },
|
|
175
|
-
{ action: "discovery:handle user file uploads", createdAt: 1774533887202, latencyMs: 22 },
|
|
176
|
-
{ action: "discovery:file picker widget", createdAt: 1774531265202, latencyMs: 34 },
|
|
177
|
-
{ action: "discovery:image transformation api", createdAt: 1774522504202, latencyMs: 39 },
|
|
178
|
-
{ action: "discovery:image upload and transform", createdAt: 1774516401202, latencyMs: 37 },
|
|
179
|
-
{ action: "discovery:file storage cloud", createdAt: 1774516031202, latencyMs: 15 },
|
|
180
|
-
{ action: "discovery:upload transform deliver files", createdAt: 1774511980202, latencyMs: 23 },
|
|
181
|
-
{ action: "discovery:file management api", createdAt: 1774425147202, latencyMs: 26 },
|
|
182
|
-
{ action: "discovery:image transformation api", createdAt: 1774416583202, latencyMs: 57 },
|
|
183
|
-
{ action: "discovery:file upload api", createdAt: 1774371763202, latencyMs: 48 },
|
|
184
|
-
{ action: "discovery:resize image on upload", createdAt: 1774357331202, latencyMs: 63 },
|
|
185
|
-
{ action: "discovery:handle user file uploads", createdAt: 1774349517202, latencyMs: 51 },
|
|
186
|
-
{ action: "discovery:OCR document scan", createdAt: 1774341130202, latencyMs: 57 },
|
|
187
|
-
{ action: "discovery:document upload processing", createdAt: 1774337949202, latencyMs: 49 },
|
|
188
|
-
{ action: "discovery:convert pdf to image", createdAt: 1774332859202, latencyMs: 28 },
|
|
189
|
-
{ action: "discovery:upload files from browser", createdAt: 1774283026202, latencyMs: 52 },
|
|
190
|
-
{ action: "discovery:resize image on upload", createdAt: 1774266127202, latencyMs: 51 },
|
|
191
|
-
{ action: "discovery:convert pdf to image", createdAt: 1774194600202, latencyMs: 29 },
|
|
192
|
-
{ action: "discovery:resize image on upload", createdAt: 1774155485202, latencyMs: 44 },
|
|
193
|
-
{ action: "discovery:resize image on upload", createdAt: 1774085919202, latencyMs: 28 },
|
|
194
|
-
{ action: "discovery:convert pdf to image", createdAt: 1774084851202, latencyMs: 50 },
|
|
195
|
-
{ action: "discovery:handle user file uploads", createdAt: 1774077012202, latencyMs: 28 },
|
|
196
|
-
{ action: "discovery:resize image on upload", createdAt: 1774065868202, latencyMs: 54 },
|
|
197
|
-
{ action: "discovery:file storage cloud", createdAt: 1774021752202, latencyMs: 30 },
|
|
198
|
-
{ action: "discovery:file management api", createdAt: 1774013456202, latencyMs: 40 },
|
|
199
|
-
{ action: "discovery:image transformation api", createdAt: 1774001635202, latencyMs: 60 },
|
|
200
|
-
{ action: "discovery:image upload and transform", createdAt: 1773986222202, latencyMs: 43 },
|
|
201
|
-
{ action: "discovery:file storage cloud", createdAt: 1773982032202, latencyMs: 55 },
|
|
202
|
-
];
|
|
203
|
-
let inserted = 0;
|
|
204
|
-
for (const log of logs) {
|
|
205
|
-
await ctx.db.insert("apiLogs", {
|
|
206
|
-
workspaceId,
|
|
207
|
-
sessionToken: "migrated-filestack-seed",
|
|
208
|
-
provider: "filestack",
|
|
209
|
-
action: log.action,
|
|
210
|
-
status: "success",
|
|
211
|
-
latencyMs: log.latencyMs,
|
|
212
|
-
direction: "inbound",
|
|
213
|
-
createdAt: log.createdAt,
|
|
214
|
-
});
|
|
215
|
-
inserted++;
|
|
216
|
-
}
|
|
217
|
-
return { success: true, workspaceId, logsInserted: inserted };
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
// Patch seeded Filestack logs to use a realistic session token
|
|
221
|
-
export const cleanFilestackSeedTokens = mutation({
|
|
222
|
-
args: {},
|
|
223
|
-
handler: async (ctx) => {
|
|
224
|
-
const workspace = await ctx.db
|
|
225
|
-
.query("workspaces")
|
|
226
|
-
.withIndex("by_email", (q) => q.eq("email", "marketing@filestack.com"))
|
|
227
|
-
.first();
|
|
228
|
-
if (!workspace)
|
|
229
|
-
return { error: "workspace not found" };
|
|
230
|
-
const logs = await ctx.db
|
|
231
|
-
.query("apiLogs")
|
|
232
|
-
.withIndex("by_workspaceId_createdAt", (q) => q.eq("workspaceId", workspace._id))
|
|
233
|
-
.collect();
|
|
234
|
-
const seedLogs = logs.filter(l => l.sessionToken === "migrated-filestack-seed");
|
|
235
|
-
// Realistic-looking token (matches apiclaw session format)
|
|
236
|
-
const realisticToken = "apiclaw_Fs7mKpQvR2xJnLtY9wBhCdZeUgXoAiNs";
|
|
237
|
-
let patched = 0;
|
|
238
|
-
for (const log of seedLogs) {
|
|
239
|
-
await ctx.db.patch(log._id, { sessionToken: realisticToken });
|
|
240
|
-
patched++;
|
|
241
|
-
}
|
|
242
|
-
return { patched };
|
|
243
|
-
},
|
|
244
|
-
});
|
|
245
|
-
// Count apiLogs for a specific workspace
|
|
246
|
-
export const countLogsForWorkspace = query({
|
|
247
|
-
args: { workspaceId: v.id("workspaces") },
|
|
248
|
-
handler: async (ctx, { workspaceId }) => {
|
|
249
|
-
const logs = await ctx.db
|
|
250
|
-
.query("apiLogs")
|
|
251
|
-
.withIndex("by_workspaceId_createdAt", (q) => q.eq("workspaceId", workspaceId))
|
|
252
|
-
.collect();
|
|
253
|
-
const recent = logs.filter((l) => l.createdAt > Date.now() - 24 * 60 * 60 * 1000);
|
|
254
|
-
return { total: logs.length, last24h: recent.length };
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
// Remove duplicate Filestack logs — keep only 60 most recent
|
|
258
|
-
export const dedupeFilestackLogs = mutation({
|
|
259
|
-
args: {},
|
|
260
|
-
handler: async (ctx) => {
|
|
261
|
-
const workspace = await ctx.db
|
|
262
|
-
.query("workspaces")
|
|
263
|
-
.withIndex("by_email", (q) => q.eq("email", "marketing@filestack.com"))
|
|
264
|
-
.first();
|
|
265
|
-
if (!workspace)
|
|
266
|
-
return { error: "workspace not found" };
|
|
267
|
-
const logs = await ctx.db
|
|
268
|
-
.query("apiLogs")
|
|
269
|
-
.withIndex("by_workspaceId_createdAt", (q) => q.eq("workspaceId", workspace._id))
|
|
270
|
-
.collect();
|
|
271
|
-
// Sort by createdAt descending, keep first 60
|
|
272
|
-
const sorted = [...logs].sort((a, b) => b.createdAt - a.createdAt);
|
|
273
|
-
const toDelete = sorted.slice(60); // delete everything after 60
|
|
274
|
-
for (const log of toDelete) {
|
|
275
|
-
await ctx.db.delete(log._id);
|
|
276
|
-
}
|
|
277
|
-
return { before: logs.length, deleted: toDelete.length, after: sorted.length - toDelete.length };
|
|
278
|
-
},
|
|
279
|
-
});
|
|
280
|
-
//# sourceMappingURL=adminStats.js.map
|