| <!DOCTYPE html> |
| <html lang="zh-Hant"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Windsurf API · 把 Windsurf 變成 OpenAI + Anthropic 相容端點</title> |
| <meta name="description" content="無頭運行 Windsurf 的 AI 模型,同時支援 /v1/chat/completions 與 /v1/messages 雙協定。Claude Code、Cursor、Cline 直接連。零 npm 依賴。"> |
| <meta name="theme-color" content="#FAF9F5"> |
| <meta property="og:title" content="Windsurf API"> |
| <meta property="og:description" content="雙協定 · 零依賴 · Docker 就緒"> |
| <meta property="og:type" content="website"> |
| <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ctext y='26' font-size='26'%3E%E3%80%B0%EF%B8%8F%3C/text%3E%3C/svg%3E"> |
| <style> |
| *{margin:0;padding:0;box-sizing:border-box} |
| html{scroll-behavior:smooth} |
| :root{ |
| --bg:#FAF9F5; |
| --bg-alt:#F5F2EB; |
| --paper:#FFFFFF; |
| --ink:#1A1612; |
| --ink-soft:#3D362C; |
| --muted:#6B6556; |
| --muted-soft:#A09888; |
| --rule:#E7E2D4; |
| --rule-soft:#EFEBDF; |
| --coral:#CC785C; |
| --coral-deep:#A05234; |
| --coral-soft:#F2DCD0; |
| --coral-wash:#FBF1EB; |
| --plum:#8B6FB0; |
| --sand:#D4A574; |
|
|
| --serif:'Charter','Iowan Old Style','Palatino Linotype','Palatino','URW Palladio L','P052','Noto Serif TC',Georgia,serif; |
| --sans:'Helvetica Neue','Segoe UI',-apple-system,BlinkMacSystemFont,'PingFang TC','Microsoft JhengHei',system-ui,sans-serif; |
| --mono:'SF Mono','JetBrains Mono',ui-monospace,Menlo,Consolas,monospace; |
| } |
| body{ |
| background:var(--bg);color:var(--ink); |
| font-family:var(--sans);font-size:16.5px;line-height:1.72; |
| -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility; |
| letter-spacing:.01em; |
| } |
| body::before{ |
| content:"";position:fixed;inset:0;pointer-events:none;z-index:0; |
| background: |
| radial-gradient(ellipse at 25% 0%,rgba(204,120,92,.03),transparent 55%), |
| radial-gradient(ellipse at 80% 100%,rgba(139,111,176,.02),transparent 50%); |
| } |
| body::after{ |
| content:"";position:fixed;inset:0;pointer-events:none;z-index:0; |
| background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.02'/%3E%3C/svg%3E"); |
| background-size:200px 200px; |
| } |
| a{color:var(--coral);text-decoration:none;transition:color .2s ease} |
| a:hover{color:var(--coral-deep)} |
| code{font-family:var(--mono);font-size:.88em;background:var(--rule-soft);padding:.1em .35em;border-radius:3px;color:var(--ink-soft)} |
| .container{max-width:1080px;margin:0 auto;padding:0 2rem;position:relative} |
| .narrow{max-width:780px;margin:0 auto;padding:0 2rem} |
| section{padding:5.5rem 0} |
| h1,h2,h3,h4{font-family:var(--serif);font-weight:500;letter-spacing:-.015em;color:var(--ink)} |
| .kicker{font-family:var(--sans);font-size:.74rem;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--coral);margin-bottom:.9rem;opacity:.85} |
| .section-title{font-size:2.4rem;line-height:1.14;margin-bottom:.9rem;font-weight:500} |
| .section-title em{font-style:italic;color:var(--coral);font-weight:400} |
| .section-sub{color:var(--muted);font-size:1.02rem;max-width:540px;margin-bottom:2.5rem;line-height:1.65} |
|
|
| /* ── Nav ── */ |
| nav{position:sticky;top:0;z-index:50;background:rgba(250,249,245,.92);border-bottom:1px solid var(--rule);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)} |
| nav .nav-inner{max-width:1080px;margin:0 auto;padding:.8rem 2rem;display:flex;align-items:center;justify-content:space-between;gap:1rem} |
| nav .logo{font-family:var(--serif);font-size:1.1rem;font-weight:500;color:var(--ink);display:flex;align-items:center;gap:.5rem} |
| nav .nav-links{display:flex;gap:1.4rem;font-size:.88rem} |
| nav .nav-links a{color:var(--muted);font-weight:500;position:relative} |
| nav .nav-links a::after{content:"";position:absolute;left:0;bottom:-2px;width:0;height:1.5px;background:var(--coral);transition:width .25s ease} |
| nav .nav-links a:hover{color:var(--ink)} |
| nav .nav-links a:hover::after{width:100%} |
| nav .nav-cta{font-size:.84rem;padding:.45rem .9rem;border-radius:6px;background:var(--ink);color:var(--bg);font-weight:500;display:inline-flex;align-items:center;gap:.35rem;transition:background .15s} |
| nav .nav-cta:hover{background:var(--coral-deep);color:#fff} |
| @media(max-width:720px){nav .nav-links{display:none}} |
|
|
| /* ── Hero ── */ |
| .hero{padding:6rem 0 5rem;border-bottom:1px solid var(--rule)} |
| .hero-inner{max-width:680px} |
| .hero-badge{display:inline-flex;align-items:center;gap:.4rem;padding:.3rem .7rem;border-radius:4px;background:var(--coral-wash);color:var(--coral-deep);font-size:.78rem;font-weight:500;margin-bottom:1.8rem;border:1px solid var(--coral-soft)} |
| .hero h1{font-size:clamp(2.4rem,5vw,3.6rem);line-height:1.06;margin-bottom:1.5rem;letter-spacing:-.025em;font-weight:500} |
| .hero h1 em{font-style:italic;color:var(--coral);font-weight:400} |
| .hero h1 .hl{background:linear-gradient(180deg,transparent 62%,var(--coral-soft) 62%,var(--coral-soft) 92%,transparent 92%);padding:0 .06em} |
| .hero .lede{font-size:1.12rem;color:var(--ink-soft);line-height:1.6;max-width:560px;margin-bottom:2rem} |
| .hero-cta{display:flex;gap:.7rem;flex-wrap:wrap;margin-bottom:1.8rem} |
| .btn{display:inline-flex;align-items:center;gap:.4rem;padding:.7rem 1.2rem;border-radius:6px;font-size:.92rem;font-weight:500;cursor:pointer;transition:all .15s;border:1px solid transparent;text-decoration:none} |
| .btn-primary{background:var(--ink);color:var(--bg)} |
| .btn-primary:hover{background:var(--coral-deep);color:#fff;transform:translateY(-1px);box-shadow:0 6px 20px -6px rgba(160,82,52,.35)} |
| .btn-ghost{background:transparent;color:var(--ink);border-color:var(--rule)} |
| .btn-ghost:hover{border-color:var(--muted-soft);transform:translateY(-1px)} |
| .btn svg{width:14px;height:14px} |
| .hero-meta{display:flex;gap:1.2rem;flex-wrap:wrap;font-size:.84rem;color:var(--muted)} |
| .hero-meta span{display:inline-flex;align-items:center;gap:.3rem} |
|
|
| /* ── Stats ── */ |
| .stats{background:var(--bg-alt);border-bottom:1px solid var(--rule);padding:3rem 0} |
| .stats-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--rule);border:1px solid var(--rule);border-radius:10px;overflow:hidden} |
| @media(max-width:760px){.stats-grid{grid-template-columns:repeat(2,1fr)}} |
| .stat{background:var(--paper);padding:1.8rem 1.2rem;text-align:center} |
| .stat .num{font-family:var(--serif);font-size:3.2rem;line-height:1;font-weight:500;color:var(--ink);letter-spacing:-.03em} |
| .stat .label{margin-top:.4rem;color:var(--muted);font-size:.84rem;font-weight:500} |
|
|
| /* ── Dual Protocol ── */ |
| .proto-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.2rem;margin-top:1.5rem} |
| @media(max-width:860px){.proto-grid{grid-template-columns:1fr}} |
| .proto-card{background:var(--paper);border:1px solid var(--rule);border-radius:10px;overflow:hidden;transition:transform .2s ease,box-shadow .2s ease} |
| .proto-card:hover{transform:translateY(-2px);box-shadow:0 8px 24px -8px rgba(26,22,18,.08)} |
| .proto-head{padding:.9rem 1.1rem;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--rule)} |
| .proto-head .proto-brand{display:flex;align-items:center;gap:.5rem} |
| .proto-head .logo-chip{width:24px;height:24px;border-radius:6px;display:inline-flex;align-items:center;justify-content:center;font-family:var(--serif);font-size:.72rem;font-weight:600;color:#fff} |
| .proto-head .logo-chip.openai{background:#10A37F} |
| .proto-head .logo-chip.anthropic{background:var(--coral)} |
| .proto-head h4{font-family:var(--sans);font-size:.92rem;font-weight:600} |
| .proto-head .proto-meta{font-size:.74rem;color:var(--muted);font-family:var(--mono)} |
| .proto-body{background:#1E1B17;padding:1rem 1.2rem;font-family:var(--mono);font-size:.76rem;line-height:1.7;color:#E8E2D4;overflow-x:auto;white-space:pre} |
| .proto-body .c-str{color:#B8C99A} |
| .proto-body .c-key{color:var(--sand)} |
| .proto-body .c-cmd{color:#E8E2D4;font-weight:500} |
| .proto-body .c-flag{color:#D4A574} |
| .proto-body .c-prompt{color:var(--coral);font-weight:600} |
| .proto-body .c-com{color:#706A5C;font-style:italic} |
| .note{background:var(--coral-wash);border-left:3px solid var(--coral);padding:.9rem 1.1rem;border-radius:0 6px 6px 0;margin-top:1.5rem;font-size:.88rem;color:var(--ink-soft);line-height:1.6} |
| .note strong{color:var(--coral-deep)} |
|
|
| /* ── Models ── */ |
| .models-section{background:var(--bg-alt)} |
| .model-filters{display:flex;flex-wrap:wrap;gap:.4rem;margin-bottom:1.5rem} |
| .filter-btn{padding:.35rem .85rem;border-radius:4px;background:var(--paper);border:1px solid var(--rule);font-size:.82rem;color:var(--muted);cursor:pointer;transition:all .15s;font-family:inherit;font-weight:500;display:inline-flex;align-items:center;gap:.3rem} |
| .filter-btn:hover{color:var(--ink);border-color:var(--muted-soft)} |
| .filter-btn.active{background:var(--ink);color:var(--bg);border-color:var(--ink)} |
| .filter-btn .count{opacity:.6;font-size:.76rem} |
| .filter-btn.active .count{opacity:.85} |
| .model-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(175px,1fr));gap:.4rem} |
| .model-card{background:var(--paper);border:1px solid var(--rule);border-radius:6px;padding:.65rem .8rem;position:relative;overflow:hidden;cursor:default;transition:border-color .2s ease,transform .2s ease,box-shadow .2s ease} |
| .model-card::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--provider-color,var(--muted-soft));transition:width .2s ease} |
| .model-card:hover{border-color:var(--muted-soft);transform:translateY(-1px);box-shadow:0 4px 12px -4px rgba(26,22,18,.06)} |
| .model-card:hover::before{width:4px} |
| .model-card.hide{display:none} |
| .model-name{font-family:var(--mono);font-size:.8rem;color:var(--ink);font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;margin-bottom:.15rem} |
| .model-meta{display:flex;align-items:center;gap:.35rem;font-size:.68rem;color:var(--muted);font-family:var(--sans)} |
| .model-provider{font-weight:600;color:var(--provider-color,var(--muted))} |
| .model-cost{margin-left:auto} |
| .model-badge{display:inline-block;padding:.08rem .35rem;border-radius:3px;font-size:.6rem;font-weight:600;letter-spacing:.03em;text-transform:uppercase} |
| .model-badge.free{background:#EBF4E6;color:#3D7A3D} |
| .model-badge.thinking{background:var(--coral-wash);color:var(--coral-deep)} |
| .model-card[data-provider="anthropic"]{--provider-color:#CC785C} |
| .model-card[data-provider="openai"]{--provider-color:#10A37F} |
| .model-card[data-provider="google"]{--provider-color:#4285F4} |
| .model-card[data-provider="deepseek"]{--provider-color:#5E6AD2} |
| .model-card[data-provider="xai"]{--provider-color:#1F1F1F} |
| .model-card[data-provider="qwen"]{--provider-color:#A855F7} |
| .model-card[data-provider="moonshot"]{--provider-color:#000} |
| .model-card[data-provider="zhipu"]{--provider-color:#3B82F6} |
| .model-card[data-provider="minimax"]{--provider-color:#FF6B35} |
| .model-card[data-provider="windsurf"]{--provider-color:#6B6556} |
|
|
| /* ── Architecture (dark immersive) ── */ |
| .arch-section{background:#151210;color:#E8E2D4;padding:5rem 0 4rem;position:relative;overflow:hidden} |
| .arch-section::before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse at 50% 40%,rgba(204,120,92,.08),transparent 65%),radial-gradient(ellipse at 20% 80%,rgba(139,111,176,.04),transparent 50%);pointer-events:none} |
| .arch-section .kicker{color:#CC785C;opacity:.9} |
| .arch-section .section-title{color:#E8E2D4} |
| .arch-section .section-title em{color:#CC785C} |
| .arch-section .section-sub{color:#8C8578} |
| .arch-canvas{position:relative;margin-top:2rem;min-height:520px} |
| @media(max-width:860px){.arch-canvas{min-height:auto}} |
| .arch-row{display:flex;align-items:center;justify-content:center;gap:0;position:relative;padding:0 1rem} |
| @media(max-width:860px){.arch-row{flex-direction:column;gap:2rem}} |
| .arch-nd{position:relative;text-align:center;z-index:2;flex-shrink:0} |
| .arch-nd-box{border:1px solid rgba(232,226,212,.12);border-radius:16px;padding:1.4rem 1.6rem;background:rgba(30,27,23,.8);backdrop-filter:blur(8px);transition:border-color .3s,box-shadow .3s} |
| .arch-nd:hover .arch-nd-box{border-color:rgba(204,120,92,.3);box-shadow:0 0 30px -10px rgba(204,120,92,.15)} |
| .arch-nd-icon{width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;margin:0 auto .7rem;font-size:1.2rem;border:1px solid rgba(232,226,212,.1)} |
| .arch-nd-label{font-family:var(--mono);font-size:.68rem;letter-spacing:.12em;text-transform:uppercase;color:#8C8578;margin-bottom:.3rem} |
| .arch-nd-title{font-family:var(--serif);font-size:1.05rem;font-weight:500;color:#E8E2D4;margin-bottom:.2rem} |
| .arch-nd-sub{font-size:.75rem;color:#6B6556;line-height:1.5} |
| .arch-nd.hero-node .arch-nd-box{border-color:rgba(204,120,92,.35);background:rgba(204,120,92,.06);padding:1.8rem 2rem} |
| .arch-nd.hero-node .arch-nd-icon{width:56px;height:56px;border-radius:14px;background:rgba(204,120,92,.12);border-color:rgba(204,120,92,.3);font-size:1.5rem} |
| .arch-nd.hero-node .arch-nd-title{font-size:1.3rem;color:#fff} |
| .arch-hero-glow{position:absolute;width:280px;height:280px;top:50%;left:50%;transform:translate(-50%,-50%);border-radius:50%;background:radial-gradient(circle,rgba(204,120,92,.12) 0%,transparent 70%);animation:a-pulse 4s ease-in-out infinite;pointer-events:none;z-index:0} |
| @keyframes a-pulse{0%,100%{transform:translate(-50%,-50%) scale(.9);opacity:.6}50%{transform:translate(-50%,-50%) scale(1.15);opacity:1}} |
| .arch-conn{flex:1;min-width:40px;height:40px;position:relative;display:flex;align-items:center;z-index:1} |
| @media(max-width:860px){.arch-conn{min-width:0;width:2px;min-height:40px;height:40px;flex:0;flex-direction:column;justify-content:center}} |
| .arch-conn-line{position:absolute;top:50%;left:0;right:0;height:2px;background:linear-gradient(90deg,rgba(204,120,92,.08),rgba(204,120,92,.25),rgba(204,120,92,.08))} |
| @media(max-width:860px){.arch-conn-line{top:0;bottom:0;left:50%;width:2px;height:100%;background:linear-gradient(180deg,rgba(204,120,92,.08),rgba(204,120,92,.25),rgba(204,120,92,.08))}} |
| .arch-conn-dot{position:absolute;width:6px;height:6px;border-radius:50%;background:#CC785C;box-shadow:0 0 8px 2px rgba(204,120,92,.4);animation:a-flow 2.5s ease-in-out infinite} |
| @media(max-width:860px){.arch-conn-dot{animation-name:a-flow-v}} |
| .arch-conn:nth-of-type(2) .arch-conn-dot:not(.ret){animation-delay:.6s} |
| .arch-conn:nth-of-type(3) .arch-conn-dot:not(.ret){animation-delay:1.2s} |
| .arch-conn:nth-of-type(1) .arch-conn-dot.ret{animation-delay:1.5s} |
| .arch-conn:nth-of-type(2) .arch-conn-dot.ret{animation-delay:.9s} |
| .arch-conn:nth-of-type(3) .arch-conn-dot.ret{animation-delay:.3s} |
| @keyframes a-flow{0%{left:0;opacity:0}10%{opacity:1}90%{opacity:1}100%{left:calc(100% - 6px);opacity:0}} |
| @keyframes a-flow-v{0%{top:0;opacity:0}10%{opacity:1}90%{opacity:1}100%{top:calc(100% - 6px);opacity:0}} |
| .arch-conn-proto{position:absolute;bottom:-20px;left:50%;transform:translateX(-50%);font-family:var(--mono);font-size:.65rem;color:#6B6556;letter-spacing:.06em;white-space:nowrap;background:rgba(21,18,16,.8);padding:.1rem .5rem;border-radius:3px} |
| @media(max-width:860px){.arch-conn-proto{bottom:auto;top:50%;left:calc(50% + 14px);transform:translateY(-50%) translateX(0)}} |
| .arch-conn-dot.ret{background:#8B6FB0;box-shadow:0 0 8px 2px rgba(139,111,176,.4);animation:a-flow-ret 3s ease-in-out infinite} |
| @keyframes a-flow-ret{0%{left:calc(100% - 6px);opacity:0}10%{opacity:1}90%{opacity:1}100%{left:0;opacity:0}} |
| .arch-features{display:grid;grid-template-columns:repeat(3,1fr);gap:.5rem;margin-top:2.5rem;max-width:680px;margin-left:auto;margin-right:auto} |
| @media(max-width:600px){.arch-features{grid-template-columns:repeat(2,1fr)}} |
| .arch-ft{display:flex;align-items:center;gap:.5rem;padding:.6rem .8rem;border-radius:10px;border:1px solid rgba(232,226,212,.07);background:rgba(30,27,23,.5);transition:all .25s;cursor:default} |
| .arch-ft:hover{border-color:rgba(204,120,92,.25);background:rgba(204,120,92,.05);transform:translateY(-1px)} |
| .arch-ft-dot{width:6px;height:6px;border-radius:50%;background:#CC785C;flex-shrink:0;box-shadow:0 0 6px rgba(204,120,92,.4)} |
| .arch-ft-text{font-size:.78rem;color:#A09888;font-family:var(--sans)} |
| .arch-ft:hover .arch-ft-text{color:#E8E2D4} |
| .arch-ft-desc{font-size:.65rem;color:#6B6556;margin-top:.1rem} |
| .arch-flow-label{text-align:center;margin-top:2rem;font-size:.72rem;color:#6B6556;font-family:var(--mono);letter-spacing:.08em;display:flex;align-items:center;justify-content:center;gap:.8rem} |
| .arch-flow-label .arr{display:inline-flex;align-items:center;gap:.3rem} |
| .arch-flow-label .arr::before{content:"";width:24px;height:1px} |
| .arch-flow-label .arr.req::before{background:linear-gradient(90deg,transparent,#CC785C)} |
| .arch-flow-label .arr.res::before{background:linear-gradient(90deg,transparent,#8B6FB0)} |
|
|
| /* ── Dashboard ── */ |
| .dashboard-section{background:var(--bg-alt)} |
| .panel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:.7rem;margin-top:1.5rem} |
| .panel-card{background:var(--paper);border:1px solid var(--rule);border-radius:8px;padding:1rem 1.1rem;transition:all .2s ease} |
| .panel-card:hover{border-color:var(--coral-soft);transform:translateY(-2px);box-shadow:0 6px 16px -6px rgba(26,22,18,.07)} |
| .panel-head{display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem} |
| .panel-icon{width:24px;height:24px;border-radius:5px;display:inline-flex;align-items:center;justify-content:center;background:var(--coral-wash);color:var(--coral-deep);font-family:var(--serif);font-size:.78rem;font-weight:600;flex-shrink:0} |
| .panel-head h4{font-family:var(--sans);font-size:.9rem;font-weight:600} |
| .panel-card p{font-size:.8rem;color:var(--muted);line-height:1.5} |
| .contrib-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:1rem;margin-top:1.5rem} |
| .contrib-card{background:var(--paper);border:1px solid var(--rule);border-radius:10px;padding:1.1rem 1.2rem;transition:all .2s ease;text-decoration:none;color:inherit;display:block} |
| .contrib-card:hover{border-color:var(--coral-soft);transform:translateY(-2px);box-shadow:0 8px 20px -8px rgba(26,22,18,.08)} |
| .contrib-head{display:flex;align-items:flex-start;gap:.8rem;margin-bottom:.7rem} |
| .contrib-avatar{width:48px;height:48px;border-radius:50%;border:1px solid var(--rule);background:var(--coral-wash);object-fit:cover;flex-shrink:0} |
| .contrib-meta{min-width:0;flex:1} |
| .contrib-name{font-family:var(--sans);font-weight:600;font-size:.95rem;color:var(--ink);display:flex;align-items:center;gap:.45rem;flex-wrap:wrap;line-height:1.3} |
| .contrib-prs{font-family:var(--mono,ui-monospace,monospace);font-size:.74rem;color:var(--muted);margin-top:.25rem;letter-spacing:.02em} |
| .contrib-weight{display:inline-flex;align-items:center;padding:1px 7px;border-radius:4px;font-family:var(--mono,ui-monospace,monospace);font-size:.68rem;font-weight:700;letter-spacing:.6px;line-height:1.55} |
| .contrib-weight-S-plus{background:linear-gradient(135deg,#f3c677,#d97706);color:#3a2806;box-shadow:0 0 0 1px rgba(217,119,6,.25)} |
| .contrib-weight-S{background:#c2410c;color:#fff7ed} |
| .contrib-weight-A-plus{background:#d97706;color:#fffbeb} |
| .contrib-weight-A{background:var(--coral-wash);color:var(--coral-deep);border:1px solid var(--coral-soft)} |
| .contrib-weight-B-plus{background:transparent;color:var(--muted);border:1px dashed var(--rule)} |
| .contrib-summary{font-size:.82rem;color:var(--muted);line-height:1.65} |
|
|
| /* ── Deploy ── */ |
| .deploy-tabs{display:flex;gap:.3rem;margin-bottom:1.5rem;border-bottom:1px solid var(--rule);padding-bottom:-1px} |
| .deploy-tab{padding:.5rem 1rem;font-size:.88rem;font-weight:500;color:var(--muted);cursor:pointer;border:none;background:none;border-bottom:2px solid transparent;transition:all .15s;font-family:var(--sans)} |
| .deploy-tab:hover{color:var(--ink)} |
| .deploy-tab.active{color:var(--coral);border-bottom-color:var(--coral)} |
| .deploy-content{display:none} |
| .deploy-content.active{display:block} |
| .deploy-steps{display:flex;flex-direction:column;gap:1.2rem;counter-reset:step} |
| .step{display:flex;gap:1rem;align-items:flex-start;counter-increment:step} |
| .step-num{width:32px;height:32px;border-radius:50%;background:var(--coral-wash);border:1.5px solid var(--coral-soft);color:var(--coral-deep);display:inline-flex;align-items:center;justify-content:center;font-family:var(--serif);font-size:.9rem;font-weight:500;flex-shrink:0} |
| .step-num::before{content:counter(step)} |
| .step-body{flex:1;min-width:0} |
| .step-body h3{font-size:1.1rem;margin-bottom:.3rem;font-weight:500} |
| .step-body>p{color:var(--muted);margin-bottom:.6rem;font-size:.9rem} |
| .code-box{background:#1E1B17;border-radius:8px;font-family:var(--mono);font-size:.78rem;color:#E8E2D4;overflow:hidden;border:1px solid #2A2520} |
| .code-box-head{display:flex;align-items:center;justify-content:space-between;padding:.45rem .9rem;background:#242019;border-bottom:1px solid #2E2922;font-size:.7rem;color:#8C8578} |
| .copy-btn{background:transparent;border:1px solid #3A352D;color:#A09888;cursor:pointer;padding:.15rem .5rem;border-radius:3px;font-family:var(--sans);font-size:.7rem;transition:all .15s} |
| .copy-btn:hover{color:#E8E2D4;border-color:#5A5448} |
| .copy-btn.ok{color:var(--coral);border-color:var(--coral)} |
| .code-box pre{padding:.9rem 1rem;overflow-x:auto;line-height:1.65} |
| .code-box .c-str{color:#B8C99A} |
| .code-box .c-flag{color:#D4A574} |
| .code-box .c-cmd{color:#E8E2D4;font-weight:500} |
| .code-box .c-com{color:#706A5C;font-style:italic} |
| .code-box .c-prompt{color:var(--coral);user-select:none} |
| .code-box .c-key{color:var(--sand)} |
|
|
| /* ── Usage ── */ |
| .usage-section{background:var(--bg-alt)} |
| .usage-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:1rem;margin-top:1.5rem} |
| .usage-card{background:var(--paper);border:1px solid var(--rule);border-radius:8px;padding:1.2rem 1.1rem;transition:all .2s ease} |
| .usage-card:hover{border-color:var(--coral-soft);transform:translateY(-2px);box-shadow:0 6px 16px -6px rgba(26,22,18,.07)} |
| .usage-card h4{font-family:var(--sans);font-size:.95rem;font-weight:600;margin-bottom:.2rem;display:flex;align-items:center;gap:.4rem} |
| .usage-card .logo-chip{width:22px;height:22px;border-radius:5px;display:inline-flex;align-items:center;justify-content:center;font-family:var(--serif);font-size:.65rem;font-weight:600;color:#fff;flex-shrink:0} |
| .usage-card .endpoint-tag{font-family:var(--mono);font-size:.72rem;color:var(--muted);margin-bottom:.7rem} |
| .usage-card .endpoint-tag strong{color:var(--coral)} |
| .usage-card pre{background:#1E1B17;padding:.7rem .9rem;border-radius:6px;font-family:var(--mono);font-size:.74rem;line-height:1.6;color:#E8E2D4;overflow-x:auto} |
| .usage-card pre .c-str{color:#B8C99A} |
| .usage-card pre .c-key{color:var(--sand)} |
| .usage-card pre .c-com{color:#706A5C;font-style:italic} |
|
|
| /* ── FAQ ── */ |
| .faq-list{display:flex;flex-direction:column;gap:.5rem;margin-top:1.5rem} |
| .faq-item{background:var(--paper);border:1px solid var(--rule);border-radius:8px;padding:.9rem 1.1rem;transition:border-color .15s} |
| .faq-item[open]{border-color:var(--coral-soft)} |
| .faq-item summary{cursor:pointer;list-style:none;font-weight:500;font-size:.95rem;display:flex;align-items:center;justify-content:space-between;gap:1rem} |
| .faq-item summary::-webkit-details-marker{display:none} |
| .faq-item summary::after{content:"+";font-family:var(--serif);font-size:1.3rem;color:var(--muted);line-height:1} |
| .faq-item[open] summary::after{content:"\2212";color:var(--coral)} |
| .faq-item .a{margin-top:.7rem;padding-top:.7rem;border-top:1px solid var(--rule);color:var(--muted);font-size:.9rem;line-height:1.65} |
| .faq-item .a code{font-size:.84em} |
|
|
| /* ── Footer ── */ |
| footer{border-top:1px solid var(--rule);padding:3rem 0 2.5rem;margin-top:2rem} |
| .footer-inner{display:grid;grid-template-columns:2fr 1fr 1fr;gap:2rem} |
| @media(max-width:720px){.footer-inner{grid-template-columns:1fr}} |
| .footer-brand .logo{font-family:var(--serif);font-size:1.15rem;font-weight:500;margin-bottom:.6rem;color:var(--ink)} |
| .footer-brand p{color:var(--muted);font-size:.85rem;max-width:320px} |
| .footer-col h5{font-size:.78rem;font-weight:600;color:var(--ink);margin-bottom:.6rem;text-transform:uppercase;letter-spacing:.08em} |
| .footer-col ul{list-style:none;display:flex;flex-direction:column;gap:.35rem} |
| .footer-col a{color:var(--muted);font-size:.85rem} |
| .footer-col a:hover{color:var(--ink)} |
| .footer-bottom{margin-top:2rem;padding-top:1.2rem;border-top:1px solid var(--rule);display:flex;justify-content:space-between;flex-wrap:wrap;gap:.8rem;font-size:.78rem;color:var(--muted-soft)} |
|
|
| /* ── Reveal ── */ |
| .reveal{opacity:0;transform:translateY(20px);transition:opacity .7s cubic-bezier(.23,1,.32,1),transform .7s cubic-bezier(.23,1,.32,1)} |
| .reveal.in{opacity:1;transform:none} |
| .reveal:nth-child(2){transition-delay:.08s} |
| .reveal:nth-child(3){transition-delay:.16s} |
| .reveal:nth-child(4){transition-delay:.24s} |
| @media(prefers-reduced-motion:reduce){.reveal{opacity:1;transform:none}} |
| </style> |
| </head> |
| <body> |
|
|
| |
| <nav> |
| <div class="nav-inner"> |
| <a href="#" class="logo">Windsurf API</a> |
| <div class="nav-links"> |
| <a href="#protocols">雙協定</a> |
| <a href="#models">模型</a> |
| <a href="#architecture">架構</a> |
| <a href="#deploy">部署</a> |
| <a href="#dashboard">管理</a> |
| <a href="#faq">FAQ</a> |
| </div> |
| <a href="https://github.com/dwgx/WindsurfAPI" class="nav-cta" target="_blank" rel="noopener"> |
| <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg> |
| GitHub |
| </a> |
| </div> |
| </nav> |
|
|
| |
| <section class="hero"> |
| <div class="container"> |
| <div class="hero-inner reveal"> |
| <div class="hero-badge">v1.5.0 — Docker + SOCKS5 + 工具調用修復</div> |
| <h1> |
| 把 <em>Windsurf</em> 的 AI 模型<br> |
| 變成你熟悉的 <span class="hl">API</span>。 |
| </h1> |
| <p class="lede"> |
| 同時支援 <code>/v1/chat/completions</code> 與 <code>/v1/messages</code> 雙協定。Claude Code、Cursor、Cline 直接連。零 npm 依賴,Docker 一鍵部署。 |
| </p> |
| <div class="hero-cta"> |
| <a href="#deploy" class="btn btn-primary"> |
| 開始部署 |
| <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 8h8M8 4l4 4-4 4" stroke-linecap="round" stroke-linejoin="round"/></svg> |
| </a> |
| <a href="https://github.com/dwgx/WindsurfAPI" class="btn btn-ghost" target="_blank" rel="noopener"> |
| <svg viewBox="0 0 16 16" fill="currentColor" width="14" height="14"><path d="M8 0C3.58 0 0 3.58 0 8a8 8 0 005.47 7.59c.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82a7.64 7.64 0 014 0c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg> |
| 查看原始碼 |
| </a> |
| </div> |
| <div class="hero-meta"> |
| <span>零 npm 依賴</span> |
| <span>Node.js ≥ 20</span> |
| <span>MIT License</span> |
| <span>Docker 就緒</span> |
| </div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="stats" style="padding:2.5rem 0"> |
| <div class="container"> |
| <div class="stats-grid"> |
| <div class="stat reveal"><div class="num" id="stat-models">0</div><div class="label">AI 模型</div></div> |
| <div class="stat reveal"><div class="num">9</div><div class="label">供應商</div></div> |
| <div class="stat reveal"><div class="num">2</div><div class="label">相容協定</div></div> |
| <div class="stat reveal"><div class="num">0</div><div class="label">npm 依賴</div></div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="dual-protocol" id="protocols"> |
| <div class="container"> |
| <div class="reveal"> |
| <div class="kicker">雙協定 · Dual Protocol</div> |
| <h2 class="section-title">同一個伺服器,<em>兩套標準</em>。</h2> |
| <p class="section-sub">不管你手上的客戶端走 OpenAI 還是 Anthropic 協定,指向同一個 Windsurf API 實例就能用。</p> |
| </div> |
|
|
| <div class="proto-grid reveal"> |
| <div class="proto-card"> |
| <div class="proto-head"> |
| <div class="proto-brand"><span class="logo-chip openai">S</span><h4>OpenAI 相容</h4></div> |
| <span class="proto-meta">POST /v1/chat/completions</span> |
| </div> |
| <pre class="proto-body"><span class="c-prompt">$</span> <span class="c-cmd">curl</span> http://localhost:3003<span class="c-str">/v1/chat/completions</span> \ |
| <span class="c-flag">-H</span> <span class="c-str">"Authorization: Bearer sk-ws-demo"</span> \ |
| <span class="c-flag">-d</span> <span class="c-str">'{ |
| <span class="c-key">"model"</span>: "gpt-5.2", |
| <span class="c-key">"messages"</span>: [ |
| {"role":"user","content":"hello"} |
| ], |
| <span class="c-key">"stream"</span>: true |
| }'</span> |
| <span class="c-com"># → Server-Sent Events (OpenAI chunk 格式)</span></pre> |
| </div> |
|
|
| <div class="proto-card"> |
| <div class="proto-head"> |
| <div class="proto-brand"><span class="logo-chip anthropic">A</span><h4>Anthropic 相容</h4></div> |
| <span class="proto-meta">POST /v1/messages</span> |
| </div> |
| <pre class="proto-body"><span class="c-prompt">$</span> <span class="c-cmd">curl</span> http://localhost:3003<span class="c-str">/v1/messages</span> \ |
| <span class="c-flag">-H</span> <span class="c-str">"x-api-key: sk-ws-demo"</span> \ |
| <span class="c-flag">-d</span> <span class="c-str">'{ |
| <span class="c-key">"model"</span>: "claude-opus-4.6", |
| <span class="c-key">"messages"</span>: [ |
| {"role":"user","content":"hello"} |
| ], |
| <span class="c-key">"stream"</span>: true |
| }'</span> |
| <span class="c-com"># → SSE (Anthropic message_delta 格式)</span></pre> |
| </div> |
| </div> |
|
|
| <div class="note reveal"> |
| <strong>同一個請求體格式、同一個帳號池、同一個速率限制器</strong>——切換協定只是換路由前綴。Claude Code、Cline 吃 <code>/v1/messages</code>,Cursor/OpenAI SDK 吃 <code>/v1/chat/completions</code>,互不干擾。 |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="models-section" id="models"> |
| <div class="container"> |
| <div class="reveal"> |
| <div class="kicker">模型 · Models</div> |
| <h2 class="section-title">一個 API 背後,<em>多個供應商</em>。</h2> |
| <p class="section-sub">從 Claude Opus 到 GPT-5、從 Gemini 3 到 DeepSeek R1,9 個供應商的模型統一接入。</p> |
| </div> |
|
|
| <div class="model-filters reveal" id="model-filters"> |
| <button class="filter-btn active" data-filter="all">全部 <span class="count" id="count-all">0</span></button> |
| <button class="filter-btn" data-filter="anthropic">Claude <span class="count" id="count-anthropic">0</span></button> |
| <button class="filter-btn" data-filter="openai">GPT <span class="count" id="count-openai">0</span></button> |
| <button class="filter-btn" data-filter="google">Gemini <span class="count" id="count-google">0</span></button> |
| <button class="filter-btn" data-filter="xai">Grok <span class="count" id="count-xai">0</span></button> |
| <button class="filter-btn" data-filter="qwen">Qwen <span class="count" id="count-qwen">0</span></button> |
| <button class="filter-btn" data-filter="moonshot">Kimi <span class="count" id="count-moonshot">0</span></button> |
| <button class="filter-btn" data-filter="zhipu">GLM <span class="count" id="count-zhipu">0</span></button> |
| <button class="filter-btn" data-filter="minimax">MiniMax <span class="count" id="count-minimax">0</span></button> |
| <button class="filter-btn" data-filter="windsurf">SWE / Arena <span class="count" id="count-windsurf">0</span></button> |
| <button class="filter-btn" data-filter="thinking">思考型 <span class="count" id="count-thinking">0</span></button> |
| <button class="filter-btn" data-filter="free">免費 <span class="count" id="count-free">0</span></button> |
| </div> |
|
|
| <div class="model-grid reveal" id="model-grid"></div> |
|
|
| <div class="note reveal"> |
| <strong>免費帳號:</strong>僅支援 <code>gemini-2.5-flash</code>(<code>gpt-4o-mini</code> 已被 Windsurf 下架)。其餘模型需 Windsurf Pro 訂閱。<br> |
| <strong>看不到最新模型?</strong>你的 Language Server binary 可能落後 Windsurf 雲端,從桌面端拷貝最新版 LS 覆蓋即可——<code>/v1/models</code> 會自動從雲端發現新模型。<br> |
| <strong>模型清單怎麼更新?</strong>本頁面上的清單由 <code>scripts/gen-docs-models.js</code> 從 <code>src/models.js</code> 自動生成,不會與後端漂移。 |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section id="architecture" class="arch-section"> |
| <div class="container"> |
| <div class="reveal"> |
| <div class="kicker">架構 · Architecture</div> |
| <h2 class="section-title">中間多一層,<em>全都接住</em>。</h2> |
| <p class="section-sub">請求從客戶端出發,經過協定翻譯、帳號池選號、工具仿真、路徑淨化,再由語言伺服器轉成 Protobuf 送到 Windsurf 雲端。回應原路返回,流式解析後交付。</p> |
| </div> |
|
|
| <div class="arch-canvas reveal"> |
| <div class="arch-row"> |
| |
| <div class="arch-nd"> |
| <div class="arch-nd-box"> |
| <div class="arch-nd-icon" style="background:rgba(232,226,212,.06);color:#E8E2D4"> |
| <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="14" rx="2"/><path d="M7 21h10M12 17v4"/></svg> |
| </div> |
| <div class="arch-nd-label">Client</div> |
| <div class="arch-nd-title">IDE / CLI</div> |
| <div class="arch-nd-sub">Claude Code · Cursor<br>Cline · OpenAI SDK</div> |
| </div> |
| </div> |
|
|
| |
| <div class="arch-conn"> |
| <div class="arch-conn-line"></div> |
| <div class="arch-conn-dot"></div> |
| <div class="arch-conn-dot ret"></div> |
| <div class="arch-conn-proto">HTTP · SSE</div> |
| </div> |
|
|
| |
| <div class="arch-nd hero-node"> |
| <div class="arch-hero-glow"></div> |
| <div class="arch-nd-box"> |
| <div class="arch-nd-icon" style="background:rgba(204,120,92,.15);color:#CC785C"> |
| <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg> |
| </div> |
| <div class="arch-nd-label">Node.js Proxy</div> |
| <div class="arch-nd-title">Windsurf API</div> |
| <div class="arch-nd-sub">協定翻譯 · 帳號池輪詢<br>工具仿真 · 上下文複用<br>速率限制 · 故障轉移</div> |
| </div> |
| </div> |
|
|
| |
| <div class="arch-conn"> |
| <div class="arch-conn-line"></div> |
| <div class="arch-conn-dot"></div> |
| <div class="arch-conn-dot ret"></div> |
| <div class="arch-conn-proto">Protobuf · gRPC</div> |
| </div> |
|
|
| |
| <div class="arch-nd"> |
| <div class="arch-nd-box"> |
| <div class="arch-nd-icon" style="background:rgba(232,226,212,.06);color:#A09888"> |
| <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 10h.01M10 10h.01M6 14h12"/></svg> |
| </div> |
| <div class="arch-nd-label">Binary · gRPC</div> |
| <div class="arch-nd-title">Language Server</div> |
| <div class="arch-nd-sub">Windsurf LS 二進位<br>Proto 序列化 / 反序列化</div> |
| </div> |
| </div> |
|
|
| |
| <div class="arch-conn"> |
| <div class="arch-conn-line"></div> |
| <div class="arch-conn-dot"></div> |
| <div class="arch-conn-dot ret"></div> |
| <div class="arch-conn-proto">TLS · HTTPS</div> |
| </div> |
|
|
| |
| <div class="arch-nd"> |
| <div class="arch-nd-box"> |
| <div class="arch-nd-icon" style="background:rgba(139,111,176,.08);color:#8B6FB0"> |
| <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M18 10a6 6 0 00-11.3-2A4.5 4.5 0 005 17h13a4 4 0 100-7z"/></svg> |
| </div> |
| <div class="arch-nd-label">Upstream</div> |
| <div class="arch-nd-title">Windsurf Cloud</div> |
| <div class="arch-nd-sub">Cascade AI 引擎<br>100+ 模型推理</div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="arch-features reveal"> |
| <div class="arch-ft"> |
| <span class="arch-ft-dot"></span> |
| <div><div class="arch-ft-text">雙協定翻譯</div><div class="arch-ft-desc">OpenAI ↔ Anthropic ↔ Cascade</div></div> |
| </div> |
| <div class="arch-ft"> |
| <span class="arch-ft-dot"></span> |
| <div><div class="arch-ft-text">多帳號池</div><div class="arch-ft-desc">輪詢 · 故障轉移 · 自動封禁偵測</div></div> |
| </div> |
| <div class="arch-ft"> |
| <span class="arch-ft-dot"></span> |
| <div><div class="arch-ft-text">工具仿真</div><div class="arch-ft-desc">Prompt 注入 · 三格式解析</div></div> |
| </div> |
| <div class="arch-ft"> |
| <span class="arch-ft-dot"></span> |
| <div><div class="arch-ft-text">上下文複用</div><div class="arch-ft-desc">Fingerprint 池 · Cascade 會話快取</div></div> |
| </div> |
| <div class="arch-ft"> |
| <span class="arch-ft-dot"></span> |
| <div><div class="arch-ft-text">安全淨化</div><div class="arch-ft-desc">路徑剝離 · SSRF 防護 · Proto 截斷檢測</div></div> |
| </div> |
| <div class="arch-ft"> |
| <span class="arch-ft-dot"></span> |
| <div><div class="arch-ft-text">SOCKS5 · Docker</div><div class="arch-ft-desc">每帳號獨立代理 · 容器化部署</div></div> |
| </div> |
| </div> |
|
|
| <div class="arch-flow-label reveal"> |
| <span class="arr req">請求流</span> |
| <span style="color:#4A433B">·</span> |
| <span class="arr res">回應流</span> |
| </div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="dashboard-section" id="dashboard"> |
| <div class="container"> |
| <div class="reveal"> |
| <div class="kicker">管理後台 · Dashboard</div> |
| <h2 class="section-title">10 個面板,<em>帳號運維</em>全搞定。</h2> |
| <p class="section-sub">訪問 <code>/dashboard</code>,暗色 Web 介面。日誌即時串流、帳號一鍵登入、模型黑白名單、封禁偵測。</p> |
| </div> |
|
|
| <div class="panel-grid reveal"> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">1</div><h4>總覽</h4></div><p>運行時間、帳號池狀態、分模型成功率</p></div> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">2</div><h4>登入取號</h4></div><p>Email/密碼直接註冊,自動取得 Token</p></div> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">3</div><h4>帳號管理</h4></div><p>新增、刪除、停用、餘額 Credits 查詢</p></div> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">4</div><h4>模型控制</h4></div><p>全域與帳號層的模型白/黑名單</p></div> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">5</div><h4>代理配置</h4></div><p>全域及個別帳號 HTTP/SOCKS5 代理</p></div> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">6</div><h4>日誌檢視</h4></div><p>即時 SSE 串流,級別篩選,關鍵字高亮</p></div> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">7</div><h4>請求統計</h4></div><p>按模型/帳號維度的指標與圖表</p></div> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">8</div><h4>封禁偵測</h4></div><p>錯誤模式偵測,帳號健康自動監控</p></div> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">9</div><h4>自動更新</h4></div><p>一鍵 git pull + PM2 重啟服務</p></div> |
| <div class="panel-card"><div class="panel-head"><div class="panel-icon">10</div><h4>致謝</h4></div><p>Contributors 列表</p></div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="deploy-section" id="deploy"> |
| <div class="container"> |
| <div class="reveal"> |
| <div class="kicker">部署 · Deploy</div> |
| <h2 class="section-title">從零到跑起來,<em>五分鐘</em>。</h2> |
| <p class="section-sub">兩種方式任選。</p> |
| </div> |
|
|
| <div class="reveal"> |
| <div class="deploy-tabs"> |
| <button class="deploy-tab active" data-tab="manual">手動部署</button> |
| <button class="deploy-tab" data-tab="docker">Docker</button> |
| </div> |
|
|
| <div class="deploy-content active" id="tab-manual"> |
| <div class="deploy-steps"> |
| <div class="step"><div class="step-num"></div><div class="step-body"> |
| <h3>安裝 Node.js 20+</h3> |
| <div class="code-box"><div class="code-box-head"><span class="lang">bash</span><button class="copy-btn" data-copy>複製</button></div> |
| <pre><span class="c-prompt">$</span> <span class="c-cmd">curl</span> -fsSL https://deb.nodesource.com/setup_20.x | bash - |
| <span class="c-prompt">$</span> <span class="c-cmd">apt install</span> -y nodejs</pre> |
| </div> |
| </div></div> |
|
|
| <div class="step"><div class="step-num"></div><div class="step-body"> |
| <h3>Clone + 安裝 Language Server</h3> |
| <div class="code-box"><div class="code-box-head"><span class="lang">bash</span><button class="copy-btn" data-copy>複製</button></div> |
| <pre><span class="c-prompt">$</span> <span class="c-cmd">git clone</span> https://github.com/dwgx/WindsurfAPI.git |
| <span class="c-prompt">$</span> <span class="c-cmd">cd</span> WindsurfAPI |
| <span class="c-prompt">$</span> <span class="c-cmd">bash</span> install-ls.sh</pre> |
| </div> |
| </div></div> |
|
|
| <div class="step"><div class="step-num"></div><div class="step-body"> |
| <h3>設定 <code>.env</code></h3> |
| <div class="code-box"><div class="code-box-head"><span class="lang">.env</span><button class="copy-btn" data-copy>複製</button></div> |
| <pre><span class="c-key">PORT</span>=<span class="c-str">3003</span> |
| <span class="c-key">API_KEY</span>= <span class="c-com"># 留空 = 不驗證</span> |
| <span class="c-key">DEFAULT_MODEL</span>=<span class="c-str">claude-4.5-sonnet-thinking</span> |
| <span class="c-key">LS_BINARY_PATH</span>=<span class="c-str">/opt/windsurf/language_server_linux_x64</span> |
| <span class="c-key">DASHBOARD_PASSWORD</span>= <span class="c-com"># 留空 = 後台免密碼</span></pre> |
| </div> |
| </div></div> |
|
|
| <div class="step"><div class="step-num"></div><div class="step-body"> |
| <h3>啟動</h3> |
| <div class="code-box"><div class="code-box-head"><span class="lang">bash</span><button class="copy-btn" data-copy>複製</button></div> |
| <pre><span class="c-prompt">$</span> <span class="c-cmd">npm install</span> -g pm2 |
| <span class="c-prompt">$</span> <span class="c-cmd">pm2 start</span> src/index.js --name windsurf-api |
| <span class="c-prompt">$</span> <span class="c-cmd">pm2 save</span> && <span class="c-cmd">pm2 startup</span></pre> |
| </div> |
| <div class="note"><strong>一鍵更新:</strong><code>bash update.sh</code></div> |
| </div></div> |
| </div> |
| </div> |
|
|
| <div class="deploy-content" id="tab-docker"> |
| <div class="deploy-steps"> |
| <div class="step"><div class="step-num"></div><div class="step-body"> |
| <h3>準備配置</h3> |
| <div class="code-box"><div class="code-box-head"><span class="lang">bash</span><button class="copy-btn" data-copy>複製</button></div> |
| <pre><span class="c-prompt">$</span> <span class="c-cmd">git clone</span> https://github.com/dwgx/WindsurfAPI.git |
| <span class="c-prompt">$</span> <span class="c-cmd">cd</span> WindsurfAPI |
| <span class="c-prompt">$</span> <span class="c-cmd">cp</span> .env.example .env</pre> |
| </div> |
| </div></div> |
|
|
| <div class="step"><div class="step-num"></div><div class="step-body"> |
| <h3>啟動容器</h3> |
| <div class="code-box"><div class="code-box-head"><span class="lang">bash</span><button class="copy-btn" data-copy>複製</button></div> |
| <pre><span class="c-prompt">$</span> <span class="c-cmd">docker compose</span> up -d --build |
| <span class="c-prompt">$</span> <span class="c-cmd">docker compose</span> logs -f</pre> |
| </div> |
| <div class="note"> |
| <strong>預設掛載:</strong><code>.docker-data/data</code> 持久化帳號/配置,<code>.docker-data/opt/windsurf</code> 放 LS binary。容器首次啟動會自動下載。 |
| </div> |
| </div></div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="usage-section"> |
| <div class="container"> |
| <div class="reveal"> |
| <div class="kicker">客戶端接入 · Integrations</div> |
| <h2 class="section-title">你常用的那個 IDE,<em>已經兼容了</em>。</h2> |
| <p class="section-sub">改 BASE_URL,塞 API KEY,完事。</p> |
| </div> |
|
|
| <div class="usage-grid reveal"> |
| <div class="usage-card"> |
| <h4><span class="logo-chip" style="background:#CC785C">C</span>Claude Code</h4> |
| <p class="endpoint-tag">→ <strong>/v1/messages</strong> · Anthropic 協定</p> |
| <pre><span class="c-key">export</span> ANTHROPIC_BASE_URL=<span class="c-str">"http://YOUR_IP:3003"</span> |
| <span class="c-key">export</span> ANTHROPIC_API_KEY=<span class="c-str">"sk-ws-your-key"</span> |
| claude</pre> |
| </div> |
| <div class="usage-card"> |
| <h4><span class="logo-chip" style="background:#3B82F6">C</span>Cursor</h4> |
| <p class="endpoint-tag">→ <strong>/v1/chat/completions</strong> · OpenAI 協定</p> |
| <pre><span class="c-com"># Settings → Models → Custom OpenAI</span> |
| Base URL: <span class="c-str">http://YOUR_IP:3003/v1</span> |
| API Key: <span class="c-str">sk-ws-your-key</span> |
| Model: <span class="c-str">claude-opus-4.6</span></pre> |
| </div> |
| <div class="usage-card"> |
| <h4><span class="logo-chip" style="background:#8B6FB0">C</span>Cline / Roo Code</h4> |
| <p class="endpoint-tag">→ <strong>Anthropic</strong> 或 <strong>OpenAI</strong> provider 皆可</p> |
| <pre><span class="c-com"># Provider: OpenAI Compatible</span> |
| Base URL: <span class="c-str">http://YOUR_IP:3003/v1</span> |
| API Key: <span class="c-str">sk-ws-your-key</span></pre> |
| </div> |
| <div class="usage-card"> |
| <h4><span class="logo-chip" style="background:#10A37F">O</span>OpenAI SDK</h4> |
| <p class="endpoint-tag">→ <strong>/v1/chat/completions</strong></p> |
| <pre><span class="c-key">from</span> openai <span class="c-key">import</span> OpenAI |
| client = OpenAI( |
| base_url=<span class="c-str">"http://YOUR_IP:3003/v1"</span>, |
| api_key=<span class="c-str">"sk-ws-your-key"</span>, |
| )</pre> |
| </div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section id="faq"> |
| <div class="narrow"> |
| <div class="reveal"> |
| <div class="kicker">常見問題 · FAQ</div> |
| <h2 class="section-title">你可能想問的。</h2> |
| </div> |
|
|
| <div class="faq-list reveal"> |
| <details class="faq-item"> |
| <summary>需要 Windsurf 付費帳號嗎?</summary> |
| <div class="a">免費帳號可以跑,但僅限 <code>gemini-2.5-flash</code>(<code>gpt-4o-mini</code> 已下架)。Claude、GPT-5 全系、Gemini 3、GLM 5.x、Kimi K2.x 等都需要 Windsurf Pro 訂閱。後台會自動偵測並標記每個帳號的 tier。</div> |
| </details> |
| <details class="faq-item"> |
| <summary>可以 Windows 上跑嗎?</summary> |
| <div class="a">HTTP 伺服器和管理後台能跑,但 Language Server binary 只有 Linux 版本(<code>language_server_linux_x64</code>),所以聊天功能僅限 Linux。Windows 建議放 WSL2 或純 Linux VM。</div> |
| </details> |
| <details class="faq-item"> |
| <summary>看不到最新模型(opus-4.7、gpt-5.3 之類)怎麼辦?</summary> |
| <div class="a">Exafunction 公開 release 停在 v2.12.5(2026-01),不含 4.7。從 Windsurf 桌面端 App 裡把 LS binary 拷出來即可:<br> |
| • macOS:<code>~/Library/Application Support/Windsurf/.../language_server_macos_arm</code><br> |
| • Linux:<code>~/.windsurf/bin/language_server_linux_x64</code><br> |
| 拷過去後 <code>/v1/models</code> 會自動從雲端 discover 最新目錄。</div> |
| </details> |
| <details class="faq-item"> |
| <summary>帳號會被封嗎?</summary> |
| <div class="a">高頻 burst 確實容易被識別。後台的封禁偵測面板會監控錯誤率,按 5 分鐘窗口做速率冷卻。推薦:每帳號 RPM < 10、配不同出口代理(支援 SOCKS5)、混用思考型和普通模型。</div> |
| </details> |
| <details class="faq-item"> |
| <summary>和其他類似專案的區別?</summary> |
| <div class="a">三點核心差異:<br> |
| (1) <strong>雙協定</strong>——同時有 <code>/v1/messages</code> 和 <code>/v1/chat/completions</code>。<br> |
| (2) <strong>planner_mode=NO_TOOL</strong>——關掉 Cascade 內建工具循環,消除路徑洩露。<br> |
| (3) <strong>完整管理後台 + 帳號池</strong>——多號輪詢 + 故障轉移 + Docker 一鍵部署。</div> |
| </details> |
| <details class="faq-item"> |
| <summary>MIT License,可以商用嗎?</summary> |
| <div class="a">代碼本體 MIT License,法律上允許商用。README 顶上有一段作者態度:沒給 Star 和 Follow 的請別商業轉售,點了的隨便用。</div> |
| </details> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="contributors-section" id="contributors" style="padding:5rem 0"> |
| <div class="container"> |
| <div class="reveal"> |
| <div class="kicker">致謝 · Credits</div> |
| <h2 class="section-title">這些朋友把這個專案<em>撐起來</em>了。</h2> |
| <p class="section-sub">每一條 PR 都附上 root-cause 分析;每一個 root-cause 都對應半夜在 Issues 區罵 Claude 的瞬間。權重按貢獻次數與精準度排,不是按代碼行數。</p> |
| </div> |
|
|
| <div class="contrib-grid reveal"> |
| <a class="contrib-card" href="https://github.com/aict666" target="_blank" rel="noopener"> |
| <div class="contrib-head"> |
| <img class="contrib-avatar" src="https://github.com/aict666.png?size=128" alt="aict666" loading="lazy" onerror="this.style.visibility='hidden'"> |
| <div class="contrib-meta"> |
| <div class="contrib-name">@aict666 <span class="contrib-weight contrib-weight-S-plus">S+</span></div> |
| <div class="contrib-prs">PR #44 · #51 · #53 · #54</div> |
| </div> |
| </div> |
| <p class="contrib-summary">四連刀。Opus 4.7 注入守衛繞行重寫、redact 標記血淚迭代到 U+2026 省略號、Pro/Trial tier 被誤降級的 inferTier 補丁、tool preamble 從 1600 字符瘦身到 330。每個 PR 都是 root-cause 直擊。</p> |
| </a> |
|
|
| <a class="contrib-card" href="https://github.com/baily-zhang" target="_blank" rel="noopener"> |
| <div class="contrib-head"> |
| <img class="contrib-avatar" src="https://github.com/baily-zhang.png?size=128" alt="baily-zhang" loading="lazy" onerror="this.style.visibility='hidden'"> |
| <div class="contrib-meta"> |
| <div class="contrib-name">@baily-zhang <span class="contrib-weight contrib-weight-S-plus">S+</span></div> |
| <div class="contrib-prs">PR #36 · #45 · #61</div> |
| </div> |
| </div> |
| <p class="contrib-summary">cascade reuse / fingerprint / trajectory offset 整套機器的實質 maintainer。從修 0% 命中率,到切斷舊 step 重放,到 Opus 4.7 多模態上下文爆炸,三次都在同一條技術線上深耕到底。</p> |
| </a> |
|
|
| <a class="contrib-card" href="https://github.com/youfak" target="_blank" rel="noopener"> |
| <div class="contrib-head"> |
| <img class="contrib-avatar" src="https://github.com/youfak.png?size=128" alt="youfak" loading="lazy" onerror="this.style.visibility='hidden'"> |
| <div class="contrib-meta"> |
| <div class="contrib-name">@youfak <span class="contrib-weight contrib-weight-A-plus">A+</span></div> |
| <div class="contrib-prs">PR #26</div> |
| </div> |
| </div> |
| <p class="contrib-summary">Docker 零依賴適配一整套:Dockerfile / docker-compose.yml / DATA_DIR 持久化 / CRLF pipefail 修復 / LS 重啟兜底。讓這個項目從「裸跑 pm2」邁進「docker compose up」。</p> |
| </a> |
|
|
| <a class="contrib-card" href="https://github.com/motto1" target="_blank" rel="noopener"> |
| <div class="contrib-head"> |
| <img class="contrib-avatar" src="https://github.com/motto1.png?size=128" alt="motto1" loading="lazy" onerror="this.style.visibility='hidden'"> |
| <div class="contrib-meta"> |
| <div class="contrib-name">@motto1 <span class="contrib-weight contrib-weight-A">A</span></div> |
| <div class="contrib-prs">PR #20</div> |
| </div> |
| </div> |
| <p class="contrib-summary">逆向 Windsurf 官網真正使用的 Auth1 登入鏈路 —— 4 步流程還原 + 批量導入 + 剪貼板讀取,補上 Firebase 路徑早已不是主鏈路的缺口。</p> |
| </a> |
|
|
| <a class="contrib-card" href="https://github.com/smeinecke" target="_blank" rel="noopener"> |
| <div class="contrib-head"> |
| <img class="contrib-avatar" src="https://github.com/smeinecke.png?size=128" alt="smeinecke" loading="lazy" onerror="this.style.visibility='hidden'"> |
| <div class="contrib-meta"> |
| <div class="contrib-name">@smeinecke <span class="contrib-weight contrib-weight-A">A</span></div> |
| <div class="contrib-prs">PR #43</div> |
| </div> |
| </div> |
| <p class="contrib-summary">Dashboard 完整 i18n 國際化 —— 14 個 commit 把每處硬編碼中文改成 I18n.t() 調用,再加 check-i18n.js 校驗防漏。從半成品翻譯到真正中英雙語切換。</p> |
| </a> |
|
|
| <a class="contrib-card" href="https://github.com/abwuge" target="_blank" rel="noopener"> |
| <div class="contrib-head"> |
| <img class="contrib-avatar" src="https://github.com/abwuge.png?size=128" alt="abwuge" loading="lazy" onerror="this.style.visibility='hidden'"> |
| <div class="contrib-meta"> |
| <div class="contrib-name">@abwuge <span class="contrib-weight contrib-weight-B-plus">B+</span></div> |
| <div class="contrib-prs">PR #58</div> |
| </div> |
| </div> |
| <p class="contrib-summary">首次貢獻就解了部署死鎖。docker-compose 起來後所有容器持續 Restart 的兩個成因(nginx zone 缺失 + config.js join 漏 import),+3 / -2 surgical 全堵上。</p> |
| </a> |
|
|
| <a class="contrib-card" href="https://github.com/dd373156" target="_blank" rel="noopener"> |
| <div class="contrib-head"> |
| <img class="contrib-avatar" src="https://github.com/dd373156.png?size=128" alt="dd373156" loading="lazy" onerror="this.style.visibility='hidden'"> |
| <div class="contrib-meta"> |
| <div class="contrib-name">@dd373156 <span class="contrib-weight contrib-weight-B-plus">B+</span></div> |
| <div class="contrib-prs">PR #1</div> |
| </div> |
| </div> |
| <p class="contrib-summary">首位外部貢獻者。MODEL_TIER_ACCESS.pro 是模塊載入時的快照,雲端動態合併進來的 claude-opus-4-7-* 永遠進不了 Pro 列表。一行 getter 修。三步 curl 復現寫得教科書級。</p> |
| </a> |
|
|
| <a class="contrib-card" href="https://github.com/colin1112a" target="_blank" rel="noopener"> |
| <div class="contrib-head"> |
| <img class="contrib-avatar" src="https://github.com/colin1112a.png?size=128" alt="colin1112a" loading="lazy" onerror="this.style.visibility='hidden'"> |
| <div class="contrib-meta"> |
| <div class="contrib-name">@colin1112a <span class="contrib-weight contrib-weight-B-plus">B+</span></div> |
| <div class="contrib-prs">PR #13</div> |
| </div> |
| </div> |
| <p class="contrib-summary">早期代碼審查先行者,一個 PR 涵蓋 15 個安全 / 並發 / 資源管理 bug:XSS 轉義、grpc HTTP/2 池、16MB frame 上限、varint BigInt。雖未直接合並,方向都對,後續多項被獨立重做。</p> |
| </a> |
| </div> |
|
|
| <p class="reveal" style="text-align:center;margin-top:2rem;font-size:.85rem;color:var(--muted)"> |
| 想加入這份名單?到 <a href="https://github.com/dwgx/WindsurfAPI/issues" target="_blank" rel="noopener" style="color:var(--coral-deep)">Issues</a> 提 bug 或到 <a href="https://github.com/dwgx/WindsurfAPI/pulls" target="_blank" rel="noopener" style="color:var(--coral-deep)">Pull requests</a> 直接動手都歡迎。 |
| </p> |
| </div> |
| </section> |
|
|
| |
| <footer> |
| <div class="container"> |
| <div class="footer-inner"> |
| <div class="footer-brand"> |
| <div class="logo">Windsurf API</div> |
| <p>Windsurf 反向代理 · 純 Node.js 零依賴 · MIT License · <a href="https://github.com/dwgx" target="_blank" rel="noopener">@dwgx</a></p> |
| </div> |
| <div class="footer-col"> |
| <h5>專案</h5> |
| <ul> |
| <li><a href="https://github.com/dwgx/WindsurfAPI" target="_blank" rel="noopener">GitHub</a></li> |
| <li><a href="https://github.com/dwgx/WindsurfAPI/releases" target="_blank" rel="noopener">Releases</a></li> |
| <li><a href="https://github.com/dwgx/WindsurfAPI/issues" target="_blank" rel="noopener">Issues</a></li> |
| <li><a href="https://github.com/dwgx/WindsurfAPI/blob/master/SECURITY.md" target="_blank" rel="noopener">Security</a></li> |
| </ul> |
| </div> |
| <div class="footer-col"> |
| <h5>文檔</h5> |
| <ul> |
| <li><a href="https://github.com/dwgx/WindsurfAPI/blob/master/README.md" target="_blank" rel="noopener">README (中文)</a></li> |
| <li><a href="https://github.com/dwgx/WindsurfAPI/blob/master/README.en.md" target="_blank" rel="noopener">README (EN)</a></li> |
| <li><a href="https://github.com/dwgx/WindsurfAPI/blob/master/CONTRIBUTING.md" target="_blank" rel="noopener">Contributing</a></li> |
| </ul> |
| </div> |
| </div> |
| <div class="footer-bottom"> |
| <span>Contributors: <a href="https://github.com/aict666">@aict666</a> · <a href="https://github.com/baily-zhang">@baily-zhang</a> · <a href="https://github.com/youfak">@youfak</a> · <a href="https://github.com/motto1">@motto1</a> · <a href="https://github.com/smeinecke">@smeinecke</a> · <a href="https://github.com/abwuge">@abwuge</a> · <a href="https://github.com/dd373156">@dd373156</a> · <a href="https://github.com/colin1112a">@colin1112a</a> · <a href="#contributors" style="opacity:.7">完整名單 ↑</a></span> |
| <span>© 2026 dwgx</span> |
| </div> |
| </div> |
| </footer> |
|
|
| <script> |
| |
| (function(){ |
| |
| |
| |
| |
| const MODELS = [ |
| {k:'claude-3.5-sonnet',p:'anthropic',c:2}, |
| {k:'claude-3.7-sonnet',p:'anthropic',c:2}, |
| {k:'claude-3.7-sonnet-thinking',p:'anthropic',c:3,thinking:1}, |
| {k:'claude-4-sonnet',p:'anthropic',c:2}, |
| {k:'claude-4-sonnet-thinking',p:'anthropic',c:3,thinking:1}, |
| {k:'claude-4-opus',p:'anthropic',c:4}, |
| {k:'claude-4-opus-thinking',p:'anthropic',c:5,thinking:1}, |
| {k:'claude-4.1-opus',p:'anthropic',c:4}, |
| {k:'claude-4.1-opus-thinking',p:'anthropic',c:5,thinking:1}, |
| {k:'claude-4.5-haiku',p:'anthropic',c:1}, |
| {k:'claude-4.5-sonnet',p:'anthropic',c:2}, |
| {k:'claude-4.5-sonnet-thinking',p:'anthropic',c:3,thinking:1}, |
| {k:'claude-4.5-opus',p:'anthropic',c:4}, |
| {k:'claude-4.5-opus-thinking',p:'anthropic',c:5,thinking:1}, |
| {k:'claude-sonnet-4.6',p:'anthropic',c:4}, |
| {k:'claude-sonnet-4.6-thinking',p:'anthropic',c:6,thinking:1}, |
| {k:'claude-sonnet-4.6-1m',p:'anthropic',c:12}, |
| {k:'claude-sonnet-4.6-thinking-1m',p:'anthropic',c:16,thinking:1}, |
| {k:'claude-opus-4.6',p:'anthropic',c:6}, |
| {k:'claude-opus-4.6-thinking',p:'anthropic',c:8,thinking:1}, |
| {k:'claude-opus-4-7-medium',p:'anthropic',c:8}, |
| {k:'claude-opus-4-7-low',p:'anthropic',c:6}, |
| {k:'claude-opus-4-7-high',p:'anthropic',c:10}, |
| {k:'claude-opus-4-7-xhigh',p:'anthropic',c:12}, |
| {k:'claude-opus-4-7-medium-thinking',p:'anthropic',c:10,thinking:1}, |
| {k:'claude-opus-4-7-high-thinking',p:'anthropic',c:12,thinking:1}, |
| {k:'claude-opus-4-7-xhigh-thinking',p:'anthropic',c:16,thinking:1}, |
| {k:'gpt-4o',p:'openai',c:1}, |
| {k:'gpt-4.1',p:'openai',c:1}, |
| {k:'gpt-5',p:'openai',c:0.5}, |
| {k:'gpt-5-medium',p:'openai',c:1}, |
| {k:'gpt-5-high',p:'openai',c:2}, |
| {k:'gpt-5-codex',p:'openai',c:0.5}, |
| {k:'gpt-5.1',p:'openai',c:0.5}, |
| {k:'gpt-5.1-low',p:'openai',c:0.5}, |
| {k:'gpt-5.1-medium',p:'openai',c:1}, |
| {k:'gpt-5.1-high',p:'openai',c:2}, |
| {k:'gpt-5.1-fast',p:'openai',c:1}, |
| {k:'gpt-5.1-low-fast',p:'openai',c:1}, |
| {k:'gpt-5.1-medium-fast',p:'openai',c:2}, |
| {k:'gpt-5.1-high-fast',p:'openai',c:4}, |
| {k:'gpt-5.1-codex-low',p:'openai',c:0.5}, |
| {k:'gpt-5.1-codex-medium',p:'openai',c:1}, |
| {k:'gpt-5.1-codex-mini-low',p:'openai',c:0.25}, |
| {k:'gpt-5.1-codex-mini',p:'openai',c:0.5}, |
| {k:'gpt-5.1-codex-max-low',p:'openai',c:1}, |
| {k:'gpt-5.1-codex-max-medium',p:'openai',c:1.25}, |
| {k:'gpt-5.1-codex-max-high',p:'openai',c:1.5}, |
| {k:'gpt-5.2',p:'openai',c:2}, |
| {k:'gpt-5.2-none',p:'openai',c:1}, |
| {k:'gpt-5.2-low',p:'openai',c:1}, |
| {k:'gpt-5.2-high',p:'openai',c:3}, |
| {k:'gpt-5.2-xhigh',p:'openai',c:8}, |
| {k:'gpt-5.2-none-fast',p:'openai',c:2}, |
| {k:'gpt-5.2-low-fast',p:'openai',c:2}, |
| {k:'gpt-5.2-medium-fast',p:'openai',c:4}, |
| {k:'gpt-5.2-high-fast',p:'openai',c:6}, |
| {k:'gpt-5.2-xhigh-fast',p:'openai',c:16}, |
| {k:'gpt-5.2-codex-low',p:'openai',c:1}, |
| {k:'gpt-5.2-codex-medium',p:'openai',c:1}, |
| {k:'gpt-5.2-codex-high',p:'openai',c:2}, |
| {k:'gpt-5.2-codex-xhigh',p:'openai',c:3}, |
| {k:'gpt-5.2-codex-low-fast',p:'openai',c:2}, |
| {k:'gpt-5.2-codex-medium-fast',p:'openai',c:2}, |
| {k:'gpt-5.2-codex-high-fast',p:'openai',c:4}, |
| {k:'gpt-5.2-codex-xhigh-fast',p:'openai',c:6}, |
| {k:'gpt-5.3-codex',p:'openai',c:1}, |
| {k:'gpt-5.4-none',p:'openai',c:0.5}, |
| {k:'gpt-5.4-low',p:'openai',c:1}, |
| {k:'gpt-5.4-medium',p:'openai',c:2}, |
| {k:'gpt-5.4-high',p:'openai',c:4}, |
| {k:'gpt-5.4-xhigh',p:'openai',c:8}, |
| {k:'gpt-5.4-mini-low',p:'openai',c:1.5}, |
| {k:'gpt-5.4-mini-medium',p:'openai',c:1.5}, |
| {k:'gpt-5.4-mini-high',p:'openai',c:4.5}, |
| {k:'gpt-5.4-mini-xhigh',p:'openai',c:12}, |
| {k:'gpt-oss-120b',p:'openai',c:0.25}, |
| {k:'o3-mini',p:'openai',c:0.5}, |
| {k:'o3',p:'openai',c:1}, |
| {k:'o3-high',p:'openai',c:1}, |
| {k:'o3-pro',p:'openai',c:4}, |
| {k:'o4-mini',p:'openai',c:0.5}, |
| {k:'gemini-2.5-pro',p:'google',c:1}, |
| {k:'gemini-2.5-flash',p:'google',c:0.5,free:1}, |
| {k:'gemini-3.0-pro',p:'google',c:1}, |
| {k:'gemini-3.0-flash-minimal',p:'google',c:0.75,free:1}, |
| {k:'gemini-3.0-flash-low',p:'google',c:1}, |
| {k:'gemini-3.0-flash',p:'google',c:1}, |
| {k:'gemini-3.0-flash-high',p:'google',c:1.75}, |
| {k:'gemini-3.1-pro-low',p:'google',c:1}, |
| {k:'gemini-3.1-pro-high',p:'google',c:2}, |
| {k:'grok-3',p:'xai',c:1}, |
| {k:'grok-3-mini-thinking',p:'xai',c:0.125,thinking:1}, |
| {k:'grok-code-fast-1',p:'xai',c:0.5}, |
| {k:'kimi-k2',p:'moonshot',c:0.5}, |
| {k:'kimi-k2-thinking',p:'moonshot',c:1,thinking:1}, |
| {k:'kimi-k2.5',p:'moonshot',c:1}, |
| {k:'kimi-k2-6',p:'moonshot',c:1}, |
| {k:'glm-4.7',p:'zhipu',c:0.25,free:1}, |
| {k:'glm-4.7-fast',p:'zhipu',c:0.5}, |
| {k:'glm-5',p:'zhipu',c:1.5}, |
| {k:'glm-5.1',p:'zhipu',c:1.5}, |
| {k:'minimax-m2.5',p:'minimax',c:1}, |
| {k:'swe-1.5',p:'windsurf',c:0.5}, |
| {k:'swe-1.5-fast',p:'windsurf',c:0.5,free:1}, |
| {k:'swe-1.5-thinking',p:'windsurf',c:0.75,thinking:1}, |
| {k:'swe-1.6',p:'windsurf',c:0.5}, |
| {k:'swe-1.6-fast',p:'windsurf',c:0.5}, |
| {k:'adaptive',p:'windsurf',c:1}, |
| {k:'arena-fast',p:'windsurf',c:0.5}, |
| {k:'arena-smart',p:'windsurf',c:1}, |
| ]; |
| |
| const grid = document.getElementById('model-grid'); |
| if(!grid) return; |
| const providerShort = {anthropic:'Claude',openai:'GPT',google:'Gemini',xai:'Grok',qwen:'Qwen',moonshot:'Kimi',zhipu:'GLM',minimax:'MiniMax',windsurf:'SWE'}; |
| |
| function badge(m){ |
| if(m.free) return '<span class="model-badge free">免費</span>'; |
| if(m.thinking) return '<span class="model-badge thinking">思考</span>'; |
| return ''; |
| } |
| |
| MODELS.forEach(m => { |
| const el = document.createElement('div'); |
| el.className = 'model-card'; |
| el.dataset.provider = m.p; |
| if(m.thinking) el.dataset.thinking = '1'; |
| if(m.free) el.dataset.free = '1'; |
| el.innerHTML = '<span class="model-name" title="'+m.k+'">'+m.k+'</span><div class="model-meta"><span class="model-provider">'+providerShort[m.p]+'</span>'+(badge(m)?'<span style="margin-left:.15rem">'+badge(m)+'</span>':'')+'<span class="model-cost">×'+m.c+'</span></div>'; |
| grid.appendChild(el); |
| }); |
| |
| document.getElementById('stat-models').textContent = MODELS.length; |
| document.getElementById('count-all').textContent = MODELS.length; |
| ['anthropic','openai','google','xai','qwen','moonshot','zhipu','minimax','windsurf'].forEach(p => { |
| const el = document.getElementById('count-'+p); |
| if (el) el.textContent = MODELS.filter(m=>m.p===p).length; |
| }); |
| document.getElementById('count-thinking').textContent = MODELS.filter(m=>m.thinking).length; |
| document.getElementById('count-free').textContent = MODELS.filter(m=>m.free).length; |
| |
| document.querySelectorAll('.filter-btn').forEach(b => b.addEventListener('click', () => { |
| document.querySelectorAll('.filter-btn').forEach(x => x.classList.remove('active')); |
| b.classList.add('active'); |
| const f = b.dataset.filter; |
| grid.querySelectorAll('.model-card').forEach(card => { |
| let show = f==='all' || (f==='thinking'?card.dataset.thinking==='1':f==='free'?card.dataset.free==='1':card.dataset.provider===f); |
| card.classList.toggle('hide',!show); |
| }); |
| })); |
| })(); |
| |
| |
| (function(){ |
| document.querySelectorAll('.deploy-tab').forEach(tab => { |
| tab.addEventListener('click', () => { |
| document.querySelectorAll('.deploy-tab').forEach(t => t.classList.remove('active')); |
| document.querySelectorAll('.deploy-content').forEach(c => c.classList.remove('active')); |
| tab.classList.add('active'); |
| document.getElementById('tab-'+tab.dataset.tab).classList.add('active'); |
| }); |
| }); |
| })(); |
| |
| |
| (function(){ |
| document.querySelectorAll('[data-copy]').forEach(btn => { |
| btn.addEventListener('click', async () => { |
| const pre = btn.closest('.code-box').querySelector('pre'); |
| try{ |
| await navigator.clipboard.writeText(pre.innerText); |
| btn.textContent='已複製';btn.classList.add('ok'); |
| setTimeout(()=>{btn.textContent='複製';btn.classList.remove('ok')},1400); |
| }catch{btn.textContent='失敗'} |
| }); |
| }); |
| })(); |
| |
| |
| (function(){ |
| const io = new IntersectionObserver(entries => { |
| entries.forEach(e => { if(e.isIntersecting){e.target.classList.add('in');io.unobserve(e.target)} }); |
| },{threshold:.12,rootMargin:'0px 0px -40px 0px'}); |
| document.querySelectorAll('.reveal').forEach(el => io.observe(el)); |
| })(); |
| </script> |
|
|
| </body> |
| </html> |
|
|