diff --git a/nginx/nginx.conf b/nginx/nginx.conf index e09eec2c..85192be6 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -15,11 +15,18 @@ server { gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json; + # index.html — never cache (so new deploys take effect immediately) + location = /index.html { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + add_header Expires 0; + } + # Frontend routes (SPA) with static asset caching location / { try_files $uri $uri/ /index.html; - # Cache static assets + # Cache hashed static assets (js/css have content hashes in filenames) location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; diff --git a/web/public/icons/claw402.png b/web/public/icons/claw402.png new file mode 100644 index 00000000..613eaae0 Binary files /dev/null and b/web/public/icons/claw402.png differ diff --git a/web/public/icons/claw402.svg b/web/public/icons/claw402.svg new file mode 100644 index 00000000..6cb43ac7 --- /dev/null +++ b/web/public/icons/claw402.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + $ + diff --git a/web/src/components/AITradersPage.tsx b/web/src/components/AITradersPage.tsx index 3ced9453..422f88d1 100644 --- a/web/src/components/AITradersPage.tsx +++ b/web/src/components/AITradersPage.tsx @@ -67,6 +67,22 @@ const BLOCKRUN_MODELS = [ { id: 'minimax-m2.5', name: 'MiniMax M2.5', desc: 'MiniMax · Flagship' }, ] +// Models available through Claw402 (x402 USDC payment protocol) +const CLAW402_MODELS = [ + { id: 'gpt-5.4', name: 'GPT-5.4', provider: 'OpenAI', desc: 'Flagship · Fast', icon: '⚡' }, + { id: 'gpt-5.4-pro', name: 'GPT-5.4 Pro', provider: 'OpenAI', desc: 'Reasoning · Pro', icon: '🧠' }, + { id: 'gpt-5.3', name: 'GPT-5.3', provider: 'OpenAI', desc: 'Balanced', icon: '💡' }, + { id: 'gpt-5-mini', name: 'GPT-5 Mini', provider: 'OpenAI', desc: 'Fast · Cheap', icon: '🚀' }, + { id: 'claude-opus', name: 'Claude Opus', provider: 'Anthropic', desc: 'Flagship · Deep', icon: '🎯' }, + { id: 'deepseek', name: 'DeepSeek V3', provider: 'DeepSeek', desc: 'Best Value', icon: '🔥' }, + { id: 'deepseek-reasoner', name: 'DeepSeek R1', provider: 'DeepSeek', desc: 'Reasoning', icon: '🤔' }, + { id: 'qwen-max', name: 'Qwen Max', provider: 'Alibaba', desc: 'Flagship', icon: '🌟' }, + { id: 'qwen-plus', name: 'Qwen Plus', provider: 'Alibaba', desc: 'Balanced', icon: '✨' }, + { id: 'grok-4.1', name: 'Grok 4.1', provider: 'xAI', desc: 'Flagship', icon: '⚡' }, + { id: 'gemini-3.1-pro', name: 'Gemini 3.1 Pro', provider: 'Google', desc: 'Flagship', icon: '💎' }, + { id: 'kimi-k2.5', name: 'Kimi K2.5', provider: 'Moonshot', desc: 'Balanced', icon: '🌙' }, +] + // AI Provider configuration - default models and API links const AI_PROVIDER_CONFIG: Record m.id === selectedModelId) - : allModels?.find((m) => m.id === selectedModelId) + // Always prefer allModels (supportedModels) for provider/id lookup; + // fall back to configuredModels for edit mode details (apiKey etc.) + const selectedModel = + allModels?.find((m) => m.id === selectedModelId) || + configuredModels?.find((m) => m.id === selectedModelId) useEffect(() => { if (editingModelId && selectedModel) { @@ -1640,8 +1663,53 @@ export function ModelConfigModal({
{language === 'zh' ? '选择 AI 模型提供商' : 'Choose Your AI Provider'}
+ + {/* Claw402 Featured Card — always first, always prominent */} + {availableModels.some(m => m.provider === 'claw402') && ( + + )} +
- {availableModels.filter(m => !m.provider?.startsWith('blockrun')).map((model) => ( + {availableModels.filter(m => !m.provider?.startsWith('blockrun') && m.provider !== 'claw402').map((model) => ( )} - {/* Step 1: Configure */} - {(currentStep === 1 || editingModelId) && selectedModel && ( + {/* Step 1: Configure — Claw402 Dedicated UI */} + {(currentStep === 1 || editingModelId) && selectedModel && (selectedModel.provider === 'claw402' || selectedModel.id === 'claw402') && ( +
+ {/* Claw402 Hero Header */} +
+
+ Claw402 +
+
+ Claw402 +
+
+ {language === 'zh' + ? '用 USDC 按次付费,支持所有主流 AI 模型' + : 'Pay-per-call with USDC — supports all major AI models'} +
+
+ {['GPT', 'Claude', 'DeepSeek', 'Gemini', 'Grok', 'Qwen', 'Kimi'].map(name => ( + + {name} + + ))} +
+
+ + {/* Step 1: Select AI Model */} +
+ +
+ {language === 'zh' + ? '所有模型通过 Claw402 统一调用,创建后可随时切换' + : 'All models unified via Claw402. Switch anytime after setup.'} +
+
+ {CLAW402_MODELS.map((m) => { + const isSelected = (modelName || 'deepseek') === m.id + return ( + + ) + })} +
+
+ + {/* Step 2: Wallet Setup */} +
+ + +
+
+ {language === 'zh' + ? '💡 Claw402 使用 Base 链上的 USDC 付费,你需要一个 EVM 钱包' + : '💡 Claw402 uses USDC on Base chain. You need an EVM wallet.'} +
+
+
+ + {language === 'zh' + ? '可以用 MetaMask、Rabby 等钱包导出私钥' + : 'Export private key from MetaMask, Rabby, etc.'} +
+
+ + {language === 'zh' + ? '建议新建一个专用钱包,充入少量 USDC 即可' + : 'Recommended: create a dedicated wallet with a small USDC balance'} +
+
+
+ +
+
+ {language === 'zh' ? '钱包私钥(Base 链 EVM)' : 'Wallet Private Key (Base Chain EVM)'} +
+ setApiKey(e.target.value)} + placeholder="0x..." + className="w-full px-4 py-3 rounded-xl font-mono text-sm" + style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF' }} + required + /> +
+ 🔒 + + {language === 'zh' + ? '私钥仅在本地签名使用,不会上传或发送交易。无需 ETH,无 Gas 费用。' + : 'Private key is only used locally for signing. Never uploaded. No ETH or gas needed.'} + +
+
+
+ + {/* USDC Recharge Guide */} +
+
+ 💰 {language === 'zh' ? '如何充值 USDC' : 'How to Fund USDC'} +
+
+
+ 1. + {language === 'zh' ? '从交易所(Binance / OKX / Coinbase)提 USDC 到你的钱包地址' : 'Withdraw USDC from exchange (Binance/OKX/Coinbase) to your wallet'} +
+
+ 2. + {language === 'zh' ? '选择 Base 网络(手续费极低)' : 'Select Base network (very low fees)'} +
+
+ 3. + {language === 'zh' ? '充入 $5-10 USDC 即可使用很长时间(约 $0.003/次调用)' : '$5-10 USDC lasts a long time (~$0.003/call)'} +
+
+
+ + {/* Buttons */} +
+ + +
+
+ )} + + {/* Step 1: Configure — Standard Providers (non-claw402) */} + {(currentStep === 1 || editingModelId) && selectedModel && selectedModel.provider !== 'claw402' && selectedModel.id !== 'claw402' && (
{/* Selected Model Header */}
diff --git a/web/src/components/ModelIcons.tsx b/web/src/components/ModelIcons.tsx index 42721810..e51a3e7e 100644 --- a/web/src/components/ModelIcons.tsx +++ b/web/src/components/ModelIcons.tsx @@ -16,6 +16,7 @@ const MODEL_COLORS: Record = { minimax: '#E45735', 'blockrun-base': '#2563EB', 'blockrun-sol': '#9945FF', + claw402: '#7C3AED', } // 获取AI模型图标的函数 @@ -54,6 +55,9 @@ export const getModelIcon = (modelType: string, props: IconProps = {}) => { case 'blockrun-sol': iconPath = '/icons/blockrun.svg' break + case 'claw402': + iconPath = '/icons/claw402.png' + break default: return null }