兼容 API 网关
同一进程暴露 OpenAI Chat Completions、OpenAI Responses 和 Anthropic Messages 三套入口,内部统一落到 chat handler 和 Cascade 请求模型。
这页把项目按运行路径拆开:Node 入口、HTTP 路由、OpenAI/Anthropic 兼容层、Windsurf Cascade 协议桥、账号池、LangServer 池、工具调用、Dashboard、部署与测试。它不是发布公告,而是给维护者和代码阅读者看的项目地图。
WindsurfAPI 是一个零依赖 Node.js 本地代理,把 Windsurf / Codeium 的本地 Language Server 和 Cascade 云端能力包装成常见 SDK 能直接接入的 API 面。
同一进程暴露 OpenAI Chat Completions、OpenAI Responses 和 Anthropic Messages 三套入口,内部统一落到 chat handler 和 Cascade 请求模型。
账号池负责 API key、token、限流、能力探测和模型可用性;LangServer 池按默认实例或代理实例启动、复用和回收本地 LS 进程。
核心协议层把标准请求翻译成 RawGetChatMessage 或 Cascade trajectory,再把上游文本、thinking、tool calls、usage 和错误翻译回客户端格式。
tool_use / tool_calls,由 Claude Code、Cline、Cursor、Aider 等客户端在调用方机器上执行工具并把结果送回下一轮。入口是 node src/index.js。启动路径先准备运行环境,再启动 HTTP 服务;LS 缺失时会告警并继续提供健康检查和管理面,关闭路径负责中止 SSE、保存账号状态和停止 LS。
src/index.js 首行导入 dashboard logger,然后读 config、版本信息和核心模块。
当 LS binary 存在时,resetWorkspace() 清空临时 workspace,避免上次 Cascade 工具痕迹污染新会话。
缺二进制时按平台提示或自动安装;HTTP 仍会启动,LS 可预热 default 实例,也可按配置延迟到首个请求。
initAuth() 读取和迁移账号状态,后台刷新 Firebase token、能力探测和 drought 摘要。
startServer() 注册路由、CORS、认证门和 Dashboard 静态页;信号处理和 graceful shutdown 留在 src/index.js。
Source: src/index.js, src/server.js, src/config.js
内部不是把 HTTP 请求直接转发到一个云 API,而是和本地 Language Server 说 gRPC。Cascade 路径先初始化 panel/workspace,再启动 cascade,发送用户消息,然后轮询 trajectory。
InitializeCascadePanelState、AddTrackedWorkspace、UpdateWorkspaceTrust、Heartbeat 建立 LS 侧状态。
WindsurfClient.cascadeChat() 调用 buildStartCascadeRequest() 拿到 cascade_id。
buildSendCascadeMessageRequest() 拼装模型、history、tool preamble、native allowlist 和附加步骤。
循环读取 GetCascadeTrajectorySteps 和 GetCascadeTrajectory,只把新增 text/thinking/tool step 发给上层。
conversation-pool 复用 cascade_id,按 caller、model、tools、history、LS generation 做隔离。
src/grpc.js 管 HTTP/2 gRPC session、unary 和 stream;src/connect.js 是可选 Connect envelope;src/proto.js 是 schema-less wire codec;src/windsurf.js 才是业务 protobuf builder/parser。
WINDSURFAPI_PROTO_TRACE=1 才开启协议 trace。默认只记录长度、hash、字段树和语义摘要;字符串 preview 属于受控调试开关,不适合公网长期打开。
这个仓库不是传统 MVC。更准确的读法是:入口层、兼容 handler、Cascade 协议层、账号/LS 运行时、Dashboard 管理面、测试与 docs。
WindsurfAPI
├─ src/index.js process entry, LS bootstrap, shutdown
├─ src/server.js HTTP route hub, auth gate, body limit
├─ src/handlers/
│ ├─ chat.js OpenAI Chat core pipeline
│ ├─ responses.js Responses API adapter
│ ├─ messages.js Anthropic Messages adapter
│ ├─ tool-emulation.js prompt-level tool protocol
│ └─ intent-extractor.js narrative tool-call recovery
├─ src/client.js WindsurfClient, Cascade loop, polling
├─ src/windsurf.js protobuf builders and parsers
├─ src/grpc.js / connect.js HTTP/2 gRPC and Connect transport
├─ src/auth.js account pool, rate limits, probes
├─ src/langserver.js LS process pool, memory guard, proxy keys
├─ src/cascade-native-bridge.js native tool bridge mapping and gates
├─ src/dashboard/ management API, UI, i18n, self-update
├─ scripts/ smoke tests, docs model generation, scans
├─ test/ focused unit/integration invariants
└─ docs/ public site, maintainer docs, auditssrc/server.js 直接按路径分发。/health 基础响应公开且包含部署指纹;verbose health、API 路由和管理读写动作分别经过 API key 或 dashboard password 边界。
| Route | Handler | Role | Boundary |
|---|---|---|---|
| GET /health | route() | 版本、commit、branch、uptime、账号计数;带合法 key 和 verbose=1 时附加池/缓存/native 状态。 | 基础公开;公网可在反代层限制。 |
| GET /v1/models | handleModels() | 返回 OpenAI 格式模型目录。 | API key gate。 |
| POST /v1/chat/completions | handleChatCompletions() | 主 OpenAI Chat 路径,处理模型解析、账号选择、Cascade、stream、tool calls、usage。 | API key + account pool。 |
| POST /v1/responses | handleResponses() | 把 Responses input/tool/text format 转成 chat,再把 SSE/非流式结果转回 Responses 事件;/v1/response 是兼容别名。 | API key + account pool + input validation。 |
| POST /v1/messages | handleMessages() | Anthropic Messages 兼容层,转换 system/content/tool_use/tool_result/thinking/cache policy。 | API key + Anthropic error shape。 |
| /auth/* | auth.js helpers | 账号添加、列表、删除、状态。 | API key gate。 |
| GET /dashboard, /dashboard/i18n/*, /dashboard/data/* | route() | Dashboard shell、翻译 JSON、贡献者等静态数据。 | 公开静态资源;文件名白名单。 |
| /dashboard/api/* | handleDashboardApi() | 账号、代理、模型访问、自更新、日志、运行时凭证等 operator 管理动作。 | X-Dashboard-Password;公网无密码 fail closed。 |
| Subsystem | Files | Key methods | What to know before editing |
|---|---|---|---|
| Chat pipeline | src/handlers/chat.js | handleChatCompletions, buildToolRoutingPlan, filterToolCallsByAllowlist | 这是最大文件,也是语义中心。改动会影响 stream、non-stream、tool calls、cache、account retry 和 conversation reuse。 |
| Protocol bridge | src/client.js, src/windsurf.js | WindsurfClient.cascadeChat, buildSendCascadeMessageRequest, parseTrajectorySteps | Cascade 是 StartCascade -> SendUserCascadeMessage -> poll trajectory/generator metadata 的状态机,timeout 和 retry 不是普通 HTTP retry。 |
| Transport | src/grpc.js, src/connect.js, src/proto.js | grpcUnary, grpcStream, StreamingFrameParser, parseFields | gRPC 是默认路径,Connect 可用环境变量切换;proto 层是手写最小编码/解析器。 |
| Accounts | src/auth.js, src/account/sticky-session.js | initAuth, getApiKey, releaseAccount, probeAccount | 账号选择同时看模型权限、RPM、错误状态、drought、sticky caller 和后台维护状态。 |
| LangServer pool | src/langserver.js | ensureLs, getLsAdmissionStatus, getLsMemoryGuardStatus | 每个代理可以有独立 LS,池容量受内存、空闲回收、pending start reservation 和进程健康影响。 |
| Tools | tool-emulation.js, intent-extractor.js, cascade-native-bridge.js | buildToolPreamble, extractIntentFromNarrative, getNativeBridgeDecision | 默认安全路径是 prompt-level emulation;native bridge 只在显式 gate 下把映射工具送进 Cascade planner。 |
| Dashboard | src/dashboard/api.js, src/dashboard/index.html | handleDashboardApi, checkAuth, parseProxyUrl | 管理面可改凭证、代理、LS、自更新、模型访问;公网绑定必须有明确 dashboard password。 |
handleChatCompletions() 是内部主干,负责模型解析、账号选择、LS 获取、Cascade/legacy 分流、stream/non-stream、cache、tool routing 和错误响应。
handleMessages() 先把 system、content blocks、tool_use、tool_result、thinking 和 cache policy 转成内部 Chat 形态,再把结果转回 Anthropic event/message。
handleResponses() 把 instructions、input items、function_call、custom tool、web_search 和 text.format 归一成 Chat 请求,再把 SSE 转成 Responses 生命周期事件。
file_search、computer_use_preview、mcp 这类 server-side tools 当前不会被伪装成已实现能力;适配层会丢弃无法桥接的 server-side tool 类型,保留普通 function tool 路径。DATA_DIR 收敛账号、运行时配置、统计、日志等 JSON/日志状态。Docker 默认挂载到 /data,本地默认使用项目根或配置目录。
请求进入 handler 后先解析模型和调用方 key,再从账号池取一个可用账号,按账号代理确保 LS 实例,最后用对应 port/csrfToken 调用 WindsurfClient。
账号池不是简单 round-robin。选择会综合 active 状态、模型 entitlement、per-model cooldown、RPM、inflight、quota score、sticky binding 和 caller hash。
LangServer 池按 default/proxy key 隔离,启动前检查 pool 容量、pending start reservation、内存水位和 idle non-default 可驱逐实例。
Cascade 请求没有 OpenAI tools[] 原生槽位,代理把工具 schema 压缩成文本协议,让模型输出 tool marker,再解析回 OpenAI/Anthropic tool call。
当模型用自然语言说“我要调用工具”但没有按协议输出 marker,intent-extractor 会在声明工具 allowlist 内做保守恢复,非流式路径还可二次 correction。
显式开启后,映射且 allowlist 的工具可进入 Cascade native planner;未映射工具继续走 emulation。默认不把本地 IDE 工具改成远端执行。
swe-1.6、adaptive、arena-* 等需要显式 WINDSURFAPI_SPECIAL_AGENT_BACKEND=devin-cli,默认拒绝 caller-local tools 和 media。Authorization: Bearer 或 x-api-key;API_KEY 持有者等同账号池管理员,公网 Dashboard API 仍必须单独设置 DASHBOARD_PASSWORD。Dashboard shell 可以公开加载,真正的管理读写动作在 /dashboard/api/*。公开部署时必须把 API_KEY 与 DASHBOARD_PASSWORD 都当成高权限 secret;localhost-only 部署才保留便利 fallback。
Dashboard proxy test、账号添加、batch import、/auth/login 与 proxy setters 默认拒绝本机、私网、metadata 地址,除非显式设置 ALLOW_PRIVATE_PROXY_HOSTS=1;导出日志前仍应复核敏感上下文。
/var/run/docker.sock 时进入 fallback;docker socket 等价于把宿主机 Docker 控制权交给管理面,只适合可信单操作者环境。
基础 /health 公开返回版本、Git 和账号计数等部署指纹;公网部署若不希望暴露这些字段,应在反向代理层限制访问。
Secret scan 只打印 path/rule,不打印命中的 secret 值;核心日志路径使用 hash/masking 引用账号、邮箱和 key,但日志导出仍属于管理面数据。
Relevant tests: dashboard-auth-fail-closed.test.js, dashboard-brute-force.test.js, secret-scan.test.js, ssrf.test.js, log-safety.test.js, native-tool-routing.test.js.
npm test 当前覆盖根层 test/*.test.js 的 114 个测试文件,集中覆盖 handler/tool/native/account/dashboard 等风险面。下面按文件名归类,实际 CI 以 npm test 和 npm run test:release 为准。
npm run test:release focuses on cache, dashboard API/syntax, native bridge docs, proto trace, secret scan, shard script, release workflow and version checks.
npm test runs the test files under test/*.test.js with Node's built-in test runner and force-exit behavior for long-running handles.