claudmax 2.0.0 → 2.0.1

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.
Files changed (124) hide show
  1. package/claudmax-1.0.16.tgz +0 -0
  2. package/{packages/cli/index.js → index.js} +2 -0
  3. package/package.json +27 -55
  4. package/.claude/settings.local.json +0 -7
  5. package/.env.example +0 -24
  6. package/.github/workflows/publish.yml +0 -31
  7. package/README.md +0 -178
  8. package/claudmax-mcp-1.0.2.tgz +0 -0
  9. package/help +0 -0
  10. package/help-wal +0 -0
  11. package/next-env.d.ts +0 -6
  12. package/next.config.mjs +0 -43
  13. package/packages/cli/claudmax-1.0.16.tgz +0 -0
  14. package/packages/cli/package.json +0 -33
  15. package/packages/mcp/claudmax-mcp-1.0.0.tgz +0 -0
  16. package/packages/mcp/claudmax-mcp-1.0.1.tgz +0 -0
  17. package/packages/mcp/claudmax-mcp-1.0.2.tgz +0 -0
  18. package/packages/mcp/claudmax-mcp-1.0.3.tgz +0 -0
  19. package/packages/mcp/index.js +0 -129
  20. package/packages/mcp/package-lock.json +0 -1146
  21. package/packages/mcp/package.json +0 -32
  22. package/postcss.config.mjs +0 -6
  23. package/prisma/schema.prisma +0 -130
  24. package/prisma/seed.ts +0 -27
  25. package/public/favicon.svg +0 -10
  26. package/public/robots.txt +0 -10
  27. package/run_build.sh +0 -4
  28. package/scripts/migrate-plans.js +0 -98
  29. package/scripts/seed-blog.ts +0 -1014
  30. package/src/app/admin/dashboard/AdminDashboardClient.tsx +0 -1546
  31. package/src/app/admin/dashboard/page.tsx +0 -13
  32. package/src/app/admin/page.tsx +0 -132
  33. package/src/app/api/admin/auth/me/route.ts +0 -34
  34. package/src/app/api/admin/health/route.ts +0 -110
  35. package/src/app/api/admin/keys/[id]/route.ts +0 -116
  36. package/src/app/api/admin/keys/route.ts +0 -192
  37. package/src/app/api/admin/keys-list/route.ts +0 -81
  38. package/src/app/api/admin/login/route.ts +0 -72
  39. package/src/app/api/admin/logout/route.ts +0 -8
  40. package/src/app/api/admin/migrate/route.ts +0 -133
  41. package/src/app/api/admin/plans/[id]/route.ts +0 -65
  42. package/src/app/api/admin/plans/route.ts +0 -66
  43. package/src/app/api/admin/posts/[id]/route.ts +0 -81
  44. package/src/app/api/admin/posts/route.ts +0 -83
  45. package/src/app/api/admin/seed/route.ts +0 -145
  46. package/src/app/api/admin/settings/route.ts +0 -44
  47. package/src/app/api/admin/stats/route.ts +0 -74
  48. package/src/app/api/admin/users/[id]/route.ts +0 -166
  49. package/src/app/api/admin/users/plans/route.ts +0 -45
  50. package/src/app/api/admin/users/route.ts +0 -202
  51. package/src/app/api/blog/[slug]/route.ts +0 -22
  52. package/src/app/api/blog/route.ts +0 -40
  53. package/src/app/api/cron/daily-status/route.ts +0 -208
  54. package/src/app/api/support/chat/route.ts +0 -55
  55. package/src/app/api/support/chat/session/route.ts +0 -62
  56. package/src/app/api/support/chat/stream/route.ts +0 -44
  57. package/src/app/api/support/email/route.ts +0 -63
  58. package/src/app/api/tools/understand_image/route.ts +0 -113
  59. package/src/app/api/tools/upload/route.ts +0 -179
  60. package/src/app/api/tools/web_search/route.ts +0 -99
  61. package/src/app/api/v1/audio/route.ts +0 -67
  62. package/src/app/api/v1/audio/speech/route.ts +0 -73
  63. package/src/app/api/v1/chat/completions/route.ts +0 -3
  64. package/src/app/api/v1/chat/route.ts +0 -1079
  65. package/src/app/api/v1/images/generations/route.ts +0 -93
  66. package/src/app/api/v1/info/route.ts +0 -30
  67. package/src/app/api/v1/key-status/route.ts +0 -109
  68. package/src/app/api/v1/key-status/stream/route.ts +0 -135
  69. package/src/app/api/v1/messages/count_tokens/route.ts +0 -22
  70. package/src/app/api/v1/messages/route.ts +0 -807
  71. package/src/app/api/v1/models/route.ts +0 -14
  72. package/src/app/api/v1/route.ts +0 -18
  73. package/src/app/blog/BlogClient.tsx +0 -193
  74. package/src/app/blog/[slug]/page.tsx +0 -117
  75. package/src/app/blog/page.tsx +0 -20
  76. package/src/app/check-usage/CheckUsageClient.tsx +0 -186
  77. package/src/app/check-usage/layout.tsx +0 -11
  78. package/src/app/check-usage/page.tsx +0 -15
  79. package/src/app/docs/layout.tsx +0 -16
  80. package/src/app/docs/page.tsx +0 -1055
  81. package/src/app/faq/FAQClient.tsx +0 -227
  82. package/src/app/faq/page.tsx +0 -21
  83. package/src/app/globals.css +0 -75
  84. package/src/app/layout.tsx +0 -80
  85. package/src/app/page.tsx +0 -256
  86. package/src/app/reseller/ResellerClient.tsx +0 -435
  87. package/src/app/reseller/page.tsx +0 -15
  88. package/src/app/setup.ps1/route.ts +0 -79
  89. package/src/app/setup.sh/route.ts +0 -113
  90. package/src/app/sitemap.ts +0 -50
  91. package/src/app/status/StatusClient.tsx +0 -103
  92. package/src/app/status/layout.tsx +0 -11
  93. package/src/app/status/page.tsx +0 -15
  94. package/src/app/support/SupportClient.tsx +0 -411
  95. package/src/app/support/page.tsx +0 -25
  96. package/src/app/v1/chat/completions/route.ts +0 -3
  97. package/src/app/v1/chat/route.ts +0 -4
  98. package/src/app/v1/messages/route.ts +0 -3
  99. package/src/components/Footer.tsx +0 -120
  100. package/src/components/Header.tsx +0 -131
  101. package/src/components/landing/features.tsx +0 -99
  102. package/src/components/ui/badge.tsx +0 -32
  103. package/src/components/ui/button.tsx +0 -46
  104. package/src/components/ui/card.tsx +0 -50
  105. package/src/components/ui/dialog.tsx +0 -97
  106. package/src/components/ui/dropdown-menu.tsx +0 -156
  107. package/src/components/ui/input.tsx +0 -21
  108. package/src/components/ui/label.tsx +0 -15
  109. package/src/components/ui/separator.tsx +0 -22
  110. package/src/components/ui/switch.tsx +0 -27
  111. package/src/components/ui/tabs.tsx +0 -51
  112. package/src/components/ui/toast.tsx +0 -103
  113. package/src/lib/auth.ts +0 -45
  114. package/src/lib/prisma.ts +0 -20
  115. package/src/lib/providers.ts +0 -158
  116. package/src/lib/security.ts +0 -165
  117. package/src/lib/utils.ts +0 -14
  118. package/src/middleware.ts +0 -30
  119. package/tailwind.config.ts +0 -53
  120. package/tsconfig.json +0 -41
  121. package/tsconfig.tsbuildinfo +0 -1
  122. package/vercel.json +0 -8
  123. /package/{packages/cli/bin → bin}/claudmax.js +0 -0
  124. /package/{packages/cli/claudmax-1.0.17.tgz → claudmax-1.0.17.tgz} +0 -0
@@ -1,93 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { prisma } from '@/lib/prisma';
3
-
4
- const TIER_LIMITS: Record<string, { requestsPerWindow: number; tokensPerWindow: number }> = {
5
- free: { requestsPerWindow: 100, tokensPerWindow: 500_000 },
6
- '5x': { requestsPerWindow: 500, tokensPerWindow: 5_000_000 },
7
- '20x': { requestsPerWindow: 2000, tokensPerWindow: 20_000_000 },
8
- unlimited: { requestsPerWindow: 999_999_999, tokensPerWindow: 999_999_999_999 },
9
- };
10
-
11
- export async function POST(req: Request) {
12
- try {
13
- const apiKey = req.headers.get('x-api-key') ?? req.headers.get('authorization')?.replace('Bearer ', '');
14
- if (!apiKey) return NextResponse.json({ error: { message: 'Missing API key', type: 'authentication_error' } }, { status: 401 });
15
- if (!apiKey.startsWith('sk-cmx_')) return NextResponse.json({ error: { message: 'Invalid API key format', type: 'authentication_error' } }, { status: 401 });
16
-
17
- const key = await prisma.apiKey.findUnique({ where: { key: apiKey } });
18
- if (!key) return NextResponse.json({ error: { message: 'Invalid API key', type: 'authentication_error' } }, { status: 401 });
19
- if (!key.isActive) return NextResponse.json({ error: { message: 'API key has been revoked', type: 'authentication_error' } }, { status: 401 });
20
-
21
- const tier = key.tier ?? 'free';
22
- const limits = TIER_LIMITS[tier] ?? TIER_LIMITS.free;
23
- if (key.windowRequestsUsed >= limits.requestsPerWindow) {
24
- return NextResponse.json({ error: { message: 'Rate limit exceeded', type: 'rate_limit_exceeded' } }, { status: 429 });
25
- }
26
-
27
- const body = await req.json();
28
- const { prompt, n = 1, size = '1024x1024', model = 'claude-image-4' } = body;
29
-
30
- if (!prompt || typeof prompt !== 'string') {
31
- return NextResponse.json({ error: { message: 'prompt is required', type: 'invalid_request_error' } }, { status: 400 });
32
- }
33
-
34
- // Parse size (e.g. "1024x1024" or "512x512")
35
- const [width, height] = (size || '1024x1024').split('x').map(Number);
36
- const w = Math.min(Math.max(width || 1024, 256), 1440);
37
- const h = Math.min(Math.max(height || 1024, 256), 1440);
38
-
39
- const results = [];
40
- const numImages = Math.min(Math.max(n || 1, 1), 4);
41
-
42
- for (let i = 0; i < numImages; i++) {
43
- const encodedPrompt = encodeURIComponent(prompt.slice(0, 1000));
44
- const imageUrl = `https://image.pollinations.ai/prompt/${encodedPrompt}?width=${w}&height=${h}&seed=${Math.floor(Math.random() * 999999)}&nologo=true`;
45
-
46
- try {
47
- const imageRes = await fetch(imageUrl, {
48
- headers: { 'Accept': 'image/*' },
49
- signal: AbortSignal.timeout(30000),
50
- });
51
-
52
- if (!imageRes.ok) {
53
- results.push({
54
- url: null,
55
- revised_prompt: prompt,
56
- error: `Image generation failed: ${imageRes.status}`,
57
- });
58
- continue;
59
- }
60
-
61
- const imageBuffer = await imageRes.arrayBuffer();
62
- const base64 = Buffer.from(imageBuffer).toString('base64');
63
- const contentType = imageRes.headers.get('content-type') ?? 'image/jpeg';
64
-
65
- results.push({
66
- url: `data:${contentType};base64,${base64}`,
67
- revised_prompt: prompt,
68
- });
69
- } catch (err) {
70
- results.push({
71
- url: null,
72
- revised_prompt: prompt,
73
- error: 'Image generation timed out',
74
- });
75
- }
76
- }
77
-
78
- // Count as 1 request
79
- await prisma.apiKey.update({
80
- where: { id: key.id },
81
- data: { lastUsedAt: new Date(), windowRequestsUsed: { increment: 1 } },
82
- }).catch(() => {});
83
-
84
- return NextResponse.json({
85
- created: Math.floor(Date.now() / 1000),
86
- model: model || 'claude-image-4',
87
- data: results,
88
- });
89
- } catch (err) {
90
- console.error('[ClaudMax Images error]:', err);
91
- return NextResponse.json({ error: { message: 'Internal server error', type: 'internal_error' } }, { status: 500 });
92
- }
93
- }
@@ -1,30 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
-
3
- export async function GET() {
4
- return NextResponse.json({
5
- name: 'ClaudMax',
6
- description: 'High-performance Claude API gateway powered by Claude Enterprise. Access Opus 4.6, Sonnet 4.6, and Haiku 4.5 through a unified endpoint.',
7
- provider: 'ClaudMax',
8
- version: '1.0.0',
9
- status: 'operational',
10
- timestamp: new Date().toISOString(),
11
- endpoints: {
12
- messages: '/v1/messages',
13
- chat: '/v1/chat/completions',
14
- models: '/v1/models',
15
- keyStatus: '/v1/key-status',
16
- },
17
- models: [
18
- { id: 'claude-opus-4-6', name: 'Opus 4.6', context_length: 200000 },
19
- { id: 'claude-sonnet-4-6', name: 'Sonnet 4.6', context_length: 200000 },
20
- { id: 'claude-haiku-4-5-20251001', name: 'Haiku 4.5', context_length: 200000 },
21
- { id: 'claude-sonnet-4-vision', name: 'Sonnet 4 Vision', context_length: 200000 },
22
- { id: 'claude-image-4', name: 'Image 4', context_length: 200000 },
23
- { id: 'claude-audio-4', name: 'Audio 4', context_length: 200000 },
24
- ],
25
- });
26
- }
27
-
28
- export async function HEAD() {
29
- return new NextResponse(null, { status: 200 });
30
- }
@@ -1,109 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { prisma } from '@/lib/prisma';
3
-
4
- const WINDOW_MS = 5 * 60 * 60 * 1000;
5
-
6
- const TIER_LIMITS: Record<string, { requestsPerWindow: number; tokensPerWindow: number }> = {
7
- '5x': { requestsPerWindow: 18000, tokensPerWindow: 5_000_000 },
8
- '20x': { requestsPerWindow: 18000, tokensPerWindow: 20_000_000 },
9
- unlimited: { requestsPerWindow: 999_999_999, tokensPerWindow: 999_999_999_999 },
10
- };
11
-
12
- export async function POST(req: Request) {
13
- try {
14
- const { apiKey } = await req.json();
15
- if (!apiKey) {
16
- return NextResponse.json({ error: 'API key is required' }, { status: 400 });
17
- }
18
-
19
- const key = await prisma.apiKey.findUnique({
20
- where: { key: apiKey },
21
- select: {
22
- id: true,
23
- name: true,
24
- prefix: true,
25
- tier: true,
26
- isActive: true,
27
- displayMultiplier: true,
28
- lastUsedAt: true,
29
- windowStartAt: true,
30
- windowTokensUsed: true,
31
- windowRequestsUsed: true,
32
- totalTokensUsed: true,
33
- createdAt: true,
34
- },
35
- });
36
-
37
- if (!key) {
38
- return NextResponse.json({ error: 'Invalid API key' }, { status: 401 });
39
- }
40
-
41
- const tier = key.tier ?? '5x';
42
- const limits = TIER_LIMITS[tier] ?? TIER_LIMITS['5x'];
43
- const multiplier = key.displayMultiplier ?? 3.0;
44
- const effectiveTokenLimit = Math.floor(limits.tokensPerWindow * multiplier);
45
-
46
- const now = Date.now();
47
- let windowStart = key.windowStartAt?.getTime() ?? now;
48
- let windowTokens = Number(key.windowTokensUsed);
49
- let windowRequests = key.windowRequestsUsed;
50
-
51
- // Window reset check
52
- if (now - windowStart > WINDOW_MS) {
53
- windowStart = now;
54
- windowTokens = 0;
55
- windowRequests = 0;
56
- }
57
-
58
- const windowResetAt = windowStart + WINDOW_MS;
59
- const tokensUsedDisplay = Math.floor(windowTokens * multiplier);
60
- const tokensLeftDisplay = Math.max(0, effectiveTokenLimit - tokensUsedDisplay);
61
- const requestsLeft = Math.max(0, limits.requestsPerWindow - windowRequests);
62
- const windowResetMs = Math.max(0, windowResetAt - now);
63
-
64
- return NextResponse.json({
65
- key: key.id,
66
- name: key.name,
67
- prefix: key.prefix,
68
- tier,
69
- isActive: key.isActive,
70
- displayMultiplier: multiplier,
71
-
72
- // Window state
73
- windowStartAt: new Date(windowStart).toISOString(),
74
- windowResetAt: new Date(windowResetAt).toISOString(),
75
- windowResetMs,
76
-
77
- // Requests
78
- requestsUsed: windowRequests,
79
- requestsLimit: limits.requestsPerWindow,
80
- requestsLeft,
81
- requestsPerMinute: 60,
82
-
83
- // Tokens — displayed to customer (multiplied)
84
- tokensUsed: tokensUsedDisplay,
85
- tokensLimit: effectiveTokenLimit,
86
- tokensLeft: tokensLeftDisplay,
87
-
88
- // Tokens — actual backend usage (hidden from customer, for admin)
89
- tokensUsedActual: windowTokens,
90
- tokensLimitActual: limits.tokensPerWindow,
91
-
92
- // Lifetime
93
- totalTokensUsed: Number(key.totalTokensUsed),
94
-
95
- createdAt: key.createdAt.toISOString(),
96
- lastUsedAt: key.lastUsedAt?.toISOString() ?? null,
97
- });
98
- } catch (err) {
99
- // If DB is unavailable, return a graceful fallback response
100
- console.error('[ClaudMax] Key status error:', err);
101
- try {
102
- const { apiKey } = await req.clone().json();
103
- if (apiKey) {
104
- return NextResponse.json({ error: 'Service temporarily unavailable. Please try again.', code: 'DB_UNAVAILABLE' }, { status: 503 });
105
- }
106
- } catch { /* ignore */ }
107
- return NextResponse.json({ error: 'Invalid API key' }, { status: 401 });
108
- }
109
- }
@@ -1,135 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { prisma } from '@/lib/prisma';
3
-
4
- const WINDOW_MS = 5 * 60 * 60 * 1000;
5
-
6
- const TIER_LIMITS: Record<string, { requestsPerWindow: number; tokensPerWindow: number }> = {
7
- '5x': { requestsPerWindow: 18000, tokensPerWindow: 5_000_000 },
8
- '20x': { requestsPerWindow: 18000, tokensPerWindow: 20_000_000 },
9
- unlimited: { requestsPerWindow: 999_999_999, tokensPerWindow: 999_999_999_999 },
10
- };
11
-
12
- async function getKeyStatus(apiKeyId: string) {
13
- const key = await prisma.apiKey.findUnique({
14
- where: { id: apiKeyId },
15
- select: {
16
- id: true, name: true, prefix: true, tier: true, isActive: true,
17
- displayMultiplier: true, lastUsedAt: true, windowStartAt: true,
18
- windowTokensUsed: true, windowRequestsUsed: true, totalTokensUsed: true,
19
- },
20
- });
21
- if (!key) return null;
22
-
23
- const tier = key.tier ?? '5x';
24
- const limits = TIER_LIMITS[tier] ?? TIER_LIMITS['5x'];
25
- const multiplier = key.displayMultiplier ?? 3.0;
26
- const effectiveTokenLimit = Math.floor(limits.tokensPerWindow * multiplier);
27
-
28
- const now = Date.now();
29
- let windowStart = key.windowStartAt?.getTime() ?? now;
30
- let windowTokens = Number(key.windowTokensUsed);
31
- let windowRequests = key.windowRequestsUsed;
32
-
33
- if (now - windowStart > WINDOW_MS) {
34
- windowStart = now;
35
- windowTokens = 0;
36
- windowRequests = 0;
37
- }
38
-
39
- const windowResetAt = windowStart + WINDOW_MS;
40
- const tokensUsedDisplay = Math.floor(windowTokens * multiplier);
41
- const tokensLeftDisplay = Math.max(0, effectiveTokenLimit - tokensUsedDisplay);
42
- const requestsLeft = Math.max(0, limits.requestsPerWindow - windowRequests);
43
- const windowResetMs = Math.max(0, windowResetAt - now);
44
-
45
- return {
46
- windowStartAt: new Date(windowStart).toISOString(),
47
- windowResetAt: new Date(windowResetAt).toISOString(),
48
- windowResetMs,
49
- requestsUsed: windowRequests,
50
- requestsLimit: limits.requestsPerWindow,
51
- requestsLeft,
52
- tokensUsed: tokensUsedDisplay,
53
- tokensLimit: effectiveTokenLimit,
54
- tokensLeft: tokensLeftDisplay,
55
- tokensUsedActual: windowTokens,
56
- lastUsedAt: key.lastUsedAt?.toISOString() ?? null,
57
- displayMultiplier: multiplier,
58
- isActive: key.isActive,
59
- };
60
- }
61
-
62
- export async function POST(req: Request) {
63
- try {
64
- const { apiKey } = await req.json();
65
- if (!apiKey || !apiKey.startsWith('sk-cmx_')) {
66
- return NextResponse.json({ error: 'Invalid API key' }, { status: 401 });
67
- }
68
-
69
- const key = await prisma.apiKey.findUnique({ where: { key: apiKey }, select: { id: true } });
70
- if (!key) return NextResponse.json({ error: 'Invalid API key' }, { status: 401 });
71
-
72
- let closed = false;
73
- let lastStatus = '';
74
-
75
- const stream = new ReadableStream({
76
- async start(controller) {
77
- const send = (data: object) => {
78
- if (closed) return;
79
- const line = `data: ${JSON.stringify(data)}\n\n`;
80
- controller.enqueue(new TextEncoder().encode(line));
81
- };
82
-
83
- // Send initial state immediately
84
- const initial = await getKeyStatus(key.id);
85
- if (initial) send({ type: 'update', ...initial });
86
- lastStatus = initial ? JSON.stringify(initial) : '';
87
-
88
- // Poll every 2 seconds for up to 60 seconds
89
- let count = 0;
90
- const interval = setInterval(async () => {
91
- if (closed || count >= 30) {
92
- clearInterval(interval);
93
- if (!closed) {
94
- controller.enqueue(new TextEncoder().encode('data: {"type":"done"}\n\n'));
95
- controller.close();
96
- }
97
- return;
98
- }
99
-
100
- const status = await getKeyStatus(key.id);
101
- if (!status) {
102
- clearInterval(interval);
103
- if (!closed) {
104
- controller.enqueue(new TextEncoder().encode('data: {"type":"error","message":"Key not found"}\n\n'));
105
- controller.close();
106
- }
107
- return;
108
- }
109
-
110
- const statusStr = JSON.stringify(status);
111
- if (statusStr !== lastStatus) {
112
- lastStatus = statusStr;
113
- send({ type: 'update', ...status });
114
- }
115
- count++;
116
- }, 2000);
117
- },
118
- cancel() {
119
- closed = true;
120
- },
121
- });
122
-
123
- return new Response(stream, {
124
- headers: {
125
- 'Content-Type': 'text/event-stream',
126
- 'Cache-Control': 'no-cache',
127
- 'Connection': 'keep-alive',
128
- 'X-Accel-Buffering': 'no',
129
- },
130
- });
131
- } catch (err) {
132
- console.error('[ClaudMax] Key status stream error:', err);
133
- return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
134
- }
135
- }
@@ -1,22 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
-
3
- export async function POST(req: Request) {
4
- try {
5
- const { model, messages } = await req.json();
6
-
7
- // Simple token estimation (≈ 4 chars per token for English)
8
- let totalChars = 0;
9
- for (const msg of messages ?? []) {
10
- totalChars += (msg.content?.toString() ?? '').length;
11
- }
12
- const estimatedTokens = Math.ceil(totalChars / 4);
13
-
14
- return NextResponse.json({
15
- tokens: estimatedTokens,
16
- model,
17
- cached_tokens: 0,
18
- });
19
- } catch {
20
- return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
21
- }
22
- }