Download index.html from abbasyk60/opencode: direct link, hf CLI and curl.
- Browser
- Download file 14.3 kB
-
https://huggingface.co/spaces/abbasyk60/opencode/resolve/main/index.html
- Command line
-
hf download hf://spaces/abbasyk60/opencode/index.html
-
curl -L -o index.html https://huggingface.co/spaces/abbasyk60/opencode/resolve/main/index.html
14.3 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>OpenCode AI Agent</title> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #1a1a2e; color: #fff; height: 100vh; display: flex; flex-direction: column; } | |
| .header { background: #16213e; padding: 15px 20px; border-bottom: 1px solid #0f3460; } | |
| .header h1 { font-size: 1.4em; color: #e94560; } | |
| .header p { font-size: 0.85em; color: #888; margin-top: 4px; } | |
| .model-select { display: flex; align-items: center; gap: 10px; margin-top: 10px; } | |
| .model-select label { font-size: 0.9em; color: #aaa; } | |
| .model-select select { background: #0f3460; color: #fff; border: 1px solid #e94560; padding: 6px 12px; border-radius: 6px; font-size: 0.9em; } | |
| .chat-container { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 12px; } | |
| .message { max-width: 80%; padding: 12px 16px; border-radius: 12px; line-height: 1.5; font-size: 0.95em; white-space: pre-wrap; word-wrap: break-word; } | |
| .message.user { align-self: flex-end; background: #e94560; color: #fff; border-bottom-right-radius: 4px; } | |
| .message.bot { align-self: flex-start; background: #16213e; color: #ddd; border-bottom-left-radius: 4px; border: 1px solid #0f3460; } | |
| .message.error { align-self: flex-start; background: #5c1a1a; color: #ff6b6b; } | |
| .input-area { padding: 15px 20px; background: #16213e; border-top: 1px solid #0f3460; display: flex; gap: 10px; } | |
| .input-area textarea { flex: 1; background: #0f3460; color: #fff; border: 1px solid #333; border-radius: 8px; padding: 12px; font-size: 0.95em; resize: none; font-family: inherit; } | |
| .input-area textarea:focus { outline: none; border-color: #e94560; } | |
| .input-area button { background: #e94560; color: #fff; border: none; border-radius: 8px; padding: 12px 24px; font-size: 0.95em; cursor: pointer; font-weight: 600; } | |
| .input-area button:hover { background: #c73650; } | |
| .input-area button:disabled { background: #555; cursor: not-allowed; } | |
| .api-key-area { padding: 10px 20px; background: #0d1117; border-top: 1px solid #333; } | |
| .api-key-area input { width: 100%; background: #161b22; color: #c9d1d9; border: 1px solid #30363d; border-radius: 6px; padding: 8px 12px; font-size: 0.85em; font-family: monospace; } | |
| .api-key-area input::placeholder { color: #484f58; } | |
| .loading { display: inline-block; } | |
| .loading::after { content: ''; animation: dots 1.5s infinite; } | |
| @keyframes dots { 0%, 20% { content: '.'; } 40% { content: '..'; } 60%, 100% { content: '...'; } } | |
| .free-tag { background: #2ea043; color: #fff; font-size: 0.7em; padding: 2px 6px; border-radius: 4px; margin-left: 6px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <h1>🤖 OpenCode AI Agent</h1> | |
| <p>Chat with AI models powered by OpenCode Zen + Local DeepSeek R1 7B</p> | |
| <div class="model-select"> | |
| <label>Model:</label> | |
| <select id="model-select"> | |
| <optgroup label="GPT Models"> | |
| <option value="gpt-5.6-sol">GPT 5.6 Sol</option> | |
| <option value="gpt-5.6-terra">GPT 5.6 Terra</option> | |
| <option value="gpt-5.6-luna">GPT 5.6 Luna</option> | |
| <option value="gpt-5.5" selected>GPT 5.5</option> | |
| <option value="gpt-5.5-pro">GPT 5.5 Pro</option> | |
| <option value="gpt-5.4">GPT 5.4</option> | |
| <option value="gpt-5.4-pro">GPT 5.4 Pro</option> | |
| <option value="gpt-5.4-mini">GPT 5.4 Mini</option> | |
| <option value="gpt-5.4-nano">GPT 5.4 Nano</option> | |
| <option value="gpt-5.3-codex">GPT 5.3 Codex</option> | |
| <option value="gpt-5.3-codex-spark">GPT 5.3 Codex Spark</option> | |
| <option value="gpt-5.2">GPT 5.2</option> | |
| <option value="gpt-5.1">GPT 5.1</option> | |
| <option value="gpt-5">GPT 5</option> | |
| <option value="gpt-5-nano">GPT 5 Nano</option> | |
| </optgroup> | |
| <optgroup label="Claude Models"> | |
| <option value="claude-fable-5">Claude Fable 5</option> | |
| <option value="claude-opus-5">Claude Opus 5</option> | |
| <option value="claude-opus-4-8">Claude Opus 4.8</option> | |
| <option value="claude-opus-4-7">Claude Opus 4.7</option> | |
| <option value="claude-opus-4-5">Claude Opus 4.5</option> | |
| <option value="claude-sonnet-5">Claude Sonnet 5</option> | |
| <option value="claude-sonnet-4-6">Claude Sonnet 4.6</option> | |
| <option value="claude-sonnet-4-5">Claude Sonnet 4.5</option> | |
| <option value="claude-haiku-4-5">Claude Haiku 4.5</option> | |
| </optgroup> | |
| <optgroup label="Gemini Models"> | |
| <option value="gemini-3.6-flash">Gemini 3.6 Flash</option> | |
| <option value="gemini-3.5-flash">Gemini 3.5 Flash</option> | |
| <option value="gemini-3.5-flash-lite">Gemini 3.5 Flash Lite</option> | |
| <option value="gemini-3.1-pro">Gemini 3.1 Pro</option> | |
| <option value="gemini-3-flash">Gemini 3 Flash</option> | |
| </optgroup> | |
| <optgroup label="Other Models"> | |
| <option value="grok-4.5">Grok 4.5</option> | |
| <option value="qwen3.7-max">Qwen3.7 Max</option> | |
| <option value="qwen3.7-plus">Qwen3.7 Plus</option> | |
| <option value="deepseek-v4-pro">DeepSeek V4 Pro</option> | |
| <option value="deepseek-v4-flash">DeepSeek V4 Flash</option> | |
| <option value="kimi-k2.7-code">Kimi K2.7 Code</option> | |
| <option value="minimax-m3">MiniMax M3</option> | |
| <option value="glm-5.2">GLM 5.2</option> | |
| </optgroup> | |
| <optgroup label="Local Models (Your Server)"> | |
| <option value="bartowski/Qwen2.5-Coder-3B-Instruct-GGUF">Qwen2.5-Coder-3B (Local) <span class="free-tag">FREE</span></option> | |
| </optgroup> | |
| <optgroup label="Free Models"> | |
| <option value="deepseek-v4-flash-free">DeepSeek V4 Flash Free <span class="free-tag">FREE</span></option> | |
| <option value="mimo-v2.5-free">MiMo-V2.5 Free <span class="free-tag">FREE</span></option> | |
| <option value="big-pickle">Big Pickle Free <span class="free-tag">FREE</span></option> | |
| <option value="laguna-s-2.1-free">Laguna S 2.1 Free <span class="free-tag">FREE</span></option> | |
| <option value="ling-3.0-flash-free">Ling-3.0 Flash Free <span class="free-tag">FREE</span></option> | |
| <option value="north-mini-code-free">North Mini Code Free <span class="free-tag">FREE</span></option> | |
| <option value="nemotron-3-ultra-free">Nemotron 3 Ultra Free <span class="free-tag">FREE</span></option> | |
| </optgroup> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="chat-container" id="chat"></div> | |
| <div class="api-key-area"> | |
| <input type="password" id="api-key" placeholder="Enter your OpenCode Zen API key (sk-...)" /> | |
| </div> | |
| <div class="input-area"> | |
| <textarea id="user-input" placeholder="Type your message..." rows="2"></textarea> | |
| <button id="send-btn" onclick="sendMessage()">Send</button> | |
| </div> | |
| <script> | |
| const chatEl = document.getElementById('chat'); | |
| const inputEl = document.getElementById('user-input'); | |
| const sendBtn = document.getElementById('send-btn'); | |
| const apiKeyEl = document.getElementById('api-key'); | |
| const modelSelect = document.getElementById('model-select'); | |
| let history = []; | |
| // Load saved API key | |
| const savedKey = localStorage.getItem('opencode_zen_key'); | |
| if (savedKey) apiKeyEl.value = savedKey; | |
| inputEl.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } | |
| }); | |
| function addMessage(role, content, isError = false) { | |
| const div = document.createElement('div'); | |
| div.className = `message ${role}${isError ? ' error' : ''}`; | |
| div.textContent = content; | |
| chatEl.appendChild(div); | |
| chatEl.scrollTop = chatEl.scrollHeight; | |
| } | |
| function addLoading() { | |
| const div = document.createElement('div'); | |
| div.className = 'message bot'; | |
| div.id = 'loading'; | |
| div.innerHTML = '<span class="loading">Thinking</span>'; | |
| chatEl.appendChild(div); | |
| chatEl.scrollTop = chatEl.scrollHeight; | |
| } | |
| function removeLoading() { | |
| const el = document.getElementById('loading'); | |
| if (el) el.remove(); | |
| } | |
| const LOCAL_API = 'https://abbasyk60-ai-telegram-bot.hf.space/v1/chat/completions'; | |
| const LOCAL_MODEL = 'bartowski/Qwen2.5-Coder-3B-Instruct-GGUF'; | |
| function getEndpoint(modelId) { | |
| if (modelId === LOCAL_MODEL) return 'local'; | |
| const openaiPrefixes = ['gpt-', 'deepseek-', 'grok-', 'minimax-', 'glm-', 'kimi-', 'big-pickle', 'mimo-', 'laguna-', 'ling-', 'north-', 'nemotron-']; | |
| if (openaiPrefixes.some(p => modelId.startsWith(p) || modelId.includes(p))) { | |
| return 'https://opencode.ai/zen/v1/chat/completions'; | |
| } | |
| if (modelId.startsWith('claude-') || modelId.startsWith('qwen3')) { | |
| return 'https://opencode.ai/zen/v1/messages'; | |
| } | |
| if (modelId.startsWith('gemini-')) { | |
| return 'https://opencode.ai/zen/v1/models/' + modelId; | |
| } | |
| return 'https://opencode.ai/zen/v1/chat/completions'; | |
| } | |
| async function sendMessage() { | |
| const message = inputEl.value.trim(); | |
| const apiKey = apiKeyEl.value.trim(); | |
| const modelId = modelSelect.value; | |
| const isLocal = modelId === LOCAL_MODEL; | |
| if (!isLocal && !apiKey) { addMessage('error', 'Please enter your OpenCode Zen API key first. (Not needed for local model)'); return; } | |
| if (!message) return; | |
| if (!isLocal) localStorage.setItem('opencode_zen_key', apiKey); | |
| addMessage('user', message); | |
| inputEl.value = ''; | |
| sendBtn.disabled = true; | |
| addLoading(); | |
| const endpoint = getEndpoint(modelId); | |
| const messages = [...history, { role: 'user', content: message }]; | |
| try { | |
| let response, data; | |
| if (isLocal) { | |
| response = await fetch(LOCAL_API, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ model: LOCAL_MODEL, messages: messages, max_tokens: 4096 }) | |
| }); | |
| } else if (endpoint.includes('/messages')) { | |
| response = await fetch(endpoint, { | |
| method: 'POST', | |
| headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'anthropic-version': '2023-06-01' }, | |
| body: JSON.stringify({ model: modelId, messages: messages, max_tokens: 4096 }) | |
| }); | |
| } else if (endpoint.includes('/models/')) { | |
| response = await fetch(endpoint, { | |
| method: 'POST', | |
| headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ contents: [{ parts: [{ text: message }] }] }) | |
| }); | |
| } else { | |
| response = await fetch(endpoint, { | |
| method: 'POST', | |
| headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ model: modelId, messages: messages, max_tokens: 4096 }) | |
| }); | |
| } | |
| removeLoading(); | |
| if (!response.ok) { | |
| const errText = await response.text(); | |
| addMessage('error', `API Error ${response.status}: ${errText.substring(0, 500)}`); | |
| sendBtn.disabled = false; | |
| return; | |
| } | |
| data = await response.json(); | |
| let botMessage = ''; | |
| if (data.choices && data.choices[0]) { | |
| const msg = data.choices[0].message; | |
| botMessage = msg.content || msg.reasoning_content || ''; | |
| } else if (endpoint.includes('/messages') && data.content) { | |
| botMessage = data.content.map(c => c.text || '').join(''); | |
| } else if (data.candidates && data.candidates[0]) { | |
| botMessage = data.candidates[0].content.parts.map(p => p.text).join(''); | |
| } else { | |
| botMessage = JSON.stringify(data, null, 2).substring(0, 2000); | |
| } | |
| if (!botMessage) botMessage = '(empty response - try again)'; | |
| addMessage('bot', botMessage); | |
| history.push({ role: 'user', content: message }); | |
| history.push({ role: 'assistant', content: botMessage }); | |
| // Keep history manageable | |
| if (history.length > 20) history = history.slice(-20); | |
| } catch (err) { | |
| removeLoading(); | |
| addMessage('error', `Error: ${err.message}`); | |
| } | |
| sendBtn.disabled = false; | |
| inputEl.focus(); | |
| } | |
| </script> | |
| </body> | |
| </html> | |