免费AI编程:代理程序+ClaudeCode+OpenRouter+MIMO-V2-Pro 完整搭建教程
我会把全部写全,零基础也能直接跟着做,实现Claude Code 免费调用 MIMO-V2-Pro。
·
我会把每一步操作、配置、代码、注意事项全部写全,零基础也能直接跟着做,实现Claude Code 免费调用 MIMO-V2-Pro
前置说明
-
核心目标:通过本地代理程序,免费调用 OpenRouter+ ClaudeCode + MIMO-V2-Pro 编程模型
-
工具准备:电脑(Windows/Mac/Linux)、网络、10分钟时间
-
完全免费:全程无付费、无充值,使用平台免费额度
1.1 注册 OpenRouter(核心聚合平台)
-
点击右上角 Sign in,用谷歌/邮箱注册
-
点击 Create API Key,名称随便填(如
proxy) -
复制生成的API Key(保存好,后面要用)
-
免费额度:每日有免费调用额度,足够日常编程
1.2 运行代理程序proxy.js
#运行代理程序
node proxy.js
// OpenRouter Transparent Proxy — 转发至 OpenRouter Xiaomi MiMo 免费端点
//
// Xiaomi MiMo 系列模型通过 OpenClaw 渠道在 OpenRouter 上提供免费推理服务。
// 客户端请求需携带 HTTP-Referer / X-OpenRouter-Title 头以识别 OpenClaw 来源。
// 本代理自动注入上述头部,并将请求透传至 openrouter.ai,对上层应用透明。
//
// Usage:
// node proxy.js
// → 将目标 base URL 从 https://openrouter.ai 替换为 http://127.0.0.1:8899
// → 原有 API Path / 请求体 / 返回格式均保持不变
const http = require("http");
const https = require("https");
const { URL } = require("url");
// ─── 配置 ────────────────────────────────────────────────
const LISTEN_HOST = "127.0.0.1";
const LISTEN_PORT = 8899;
const TARGET_PROTOCOL = "https:";
const TARGET_HOSTNAME = "openrouter.ai";
const TARGET_PORT = null;
const SSL_REJECT_UNAUTHORIZED = true;
const VERBOSE = true;
const HEADERS_ADD = {};
const HEADERS_REMOVE = ["host", "x-forwarded-for"];
const HEADERS_OVERRIDE = {
"HTTP-Referer": "https://openclaw.ai",
"X-OpenRouter-Title": "OpenClaw",
};
// ─── 日志 ────────────────────────────────────────────────────
const COLORS = {
reset: "\x1b[0m",
dim: "\x1b[2m",
red: "\x1b[31m",
green: "\x1b[32m",
yellow: "\x1b[33m",
blue: "\x1b[34m",
magenta: "\x1b[35m",
cyan: "\x1b[36m",
};
function log(level, msg, extra) {
const ts = new Date().toISOString().slice(11, 23);
const prefix = {
info: `${COLORS.green}●${COLORS.reset}`,
warn: `${COLORS.yellow}●${COLORS.reset}`,
error: `${COLORS.red}●${COLORS.reset}`,
proxy: `${COLORS.cyan}→${COLORS.reset}`,
sse: `${COLORS.magenta}?${COLORS.reset}`,
ws: `${COLORS.blue}◎${COLORS.reset}`,
}[level] || " ";
const line = `${COLORS.dim}${ts}${COLORS.reset} ${prefix} ${msg}`;
if (extra) {
console.log(line, extra);
} else {
console.log(line);
}
}
// ─── Header 处理 ─────────────────────────────────────────────
function buildProxyHeaders(originalHeaders) {
const headers = { ...originalHeaders };
for (const key of HEADERS_REMOVE) {
delete headers[key.toLowerCase()];
}
for (const [key, val] of Object.entries(HEADERS_ADD)) {
const lower = key.toLowerCase();
if (!(lower in headers)) {
headers[lower] = val;
}
}
for (const [key, val] of Object.entries(HEADERS_OVERRIDE)) {
headers[key.toLowerCase()] = val;
}
return headers;
}
// ─── SSE 检测 ────────────────────────────────────────────────
function isSSEResponse(headers) {
return (headers["content-type"] || "").includes("text/event-stream");
}
function isStreamingRequest(headers) {
return (headers["accept"] || "").includes("text/event-stream");
}
// ─── 核心:代理请求 ─────────────────────────────────────────
function proxyRequest(clientReq, clientRes) {
const targetUrl = new URL(clientReq.url, `${TARGET_PROTOCOL}//${TARGET_HOSTNAME}`);
if (TARGET_PORT) targetUrl.port = TARGET_PORT;
const proxyHeaders = buildProxyHeaders(clientReq.headers);
proxyHeaders["host"] = TARGET_HOSTNAME + (TARGET_PORT ? `:${TARGET_PORT}` : "");
proxyHeaders["x-forwarded-for"] = clientReq.socket.remoteAddress || "unknown";
proxyHeaders["x-forwarded-host"] = clientReq.headers.host || "";
proxyHeaders["x-forwarded-proto"] = "http";
for (const h of ["connection", "keep-alive", "proxy-authenticate", "proxy-authorization", "te", "trailers", "transfer-encoding", "upgrade"]) {
delete proxyHeaders[h];
}
const isSSE = isStreamingRequest(clientReq.headers);
const protocol = targetUrl.protocol === "https:" ? https : http;
const proxyOptions = {
hostname: targetUrl.hostname,
port: targetUrl.port || (targetUrl.protocol === "https:" ? 443 : 80),
path: targetUrl.pathname + targetUrl.search,
method: clientReq.method,
headers: proxyHeaders,
rejectUnauthorized: SSL_REJECT_UNAUTHORIZED,
};
if (VERBOSE) {
const mode = isSSE ? `${COLORS.magenta}[SSE]${COLORS.reset} ` : "";
log("proxy", `${mode}${clientReq.method} ${COLORS.cyan}${targetUrl.hostname}${COLORS.reset}${targetUrl.pathname}${targetUrl.search}`);
}
const proxyReq = protocol.request(proxyOptions, (proxyRes) => {
const isSSE = isSSEResponse(proxyRes.headers);
const resHeaders = { ...proxyRes.headers };
if (isSSE) {
delete resHeaders["content-length"];
delete resHeaders["content-encoding"];
delete resHeaders["cache-control"];
resHeaders["cache-control"] = "no-cache, no-transform";
resHeaders["connection"] = "keep-alive";
resHeaders["x-accel-buffering"] = "no";
if (VERBOSE) log("sse", `SSE stream opened — ${proxyRes.statusCode}`);
}
clientRes.writeHead(proxyRes.statusCode, proxyRes.statusMessage, resHeaders);
proxyRes.pipe(clientRes);
if (isSSE && VERBOSE) {
let buffer = "";
proxyRes.on("data", (chunk) => {
buffer += chunk.toString();
const lines = buffer.split("\n");
buffer = lines.pop();
for (const line of lines) {
if (line.startsWith("data:")) {
const data = line.slice(5).trim();
if (data === "[DONE]") {
log("sse", `${COLORS.green}Stream ended [DONE]${COLORS.reset}`);
} else if (data.length > 0) {
const preview = data.length > 80 ? data.slice(0, 80) + "..." : data;
log("sse", `${COLORS.dim}data: ${preview}${COLORS.reset}`);
}
} else if (line.startsWith("event:")) {
log("sse", `${COLORS.yellow}${line}${COLORS.reset}`);
}
}
});
}
proxyRes.on("end", () => {
if (VERBOSE && !isSSE) {
log("info", `Response complete — ${proxyRes.statusCode} ${clientReq.method} ${targetUrl.pathname}`);
}
});
proxyRes.on("error", (err) => {
log("error", `Proxy response error: ${err.message}`);
if (!clientRes.headersSent) clientRes.writeHead(502, { "Content-Type": "text/plain" });
clientRes.end("Bad Gateway: upstream response error");
});
});
clientReq.pipe(proxyReq);
clientReq.on("error", (err) => {
log("error", `Client request error: ${err.message}`);
proxyReq.destroy();
});
proxyReq.on("error", (err) => {
log("error", `Proxy request error: ${err.message}`);
if (!clientRes.headersSent) clientRes.writeHead(502, { "Content-Type": "text/plain" });
clientRes.end(`Bad Gateway: ${err.message}`);
});
proxyReq.setTimeout(isSSE ? 0 : 60000, () => {
log("warn", "Proxy request timeout");
proxyReq.destroy();
});
}
// ─── WebSocket 代理 ──────────────────────────────────────────
function proxyWebSocket(clientReq, clientSocket, clientHead) {
const targetUrl = new URL(clientReq.url, `ws://${TARGET_HOSTNAME}`);
if (TARGET_PORT) targetUrl.port = TARGET_PORT;
const proxyHeaders = buildProxyHeaders(clientReq.headers);
proxyHeaders["host"] = TARGET_HOSTNAME + (TARGET_PORT ? `:${TARGET_PORT}` : "");
delete proxyHeaders["sec-websocket-key"];
delete proxyHeaders["sec-websocket-version"];
delete proxyHeaders["sec-websocket-extensions"];
const protocol = targetUrl.protocol === "wss:" ? https : http;
const proxyReq = protocol.request({
hostname: targetUrl.hostname,
port: targetUrl.port || (targetUrl.protocol === "wss:" ? 443 : 80),
path: targetUrl.pathname + targetUrl.search,
method: "GET",
headers: {
...proxyHeaders,
upgrade: "websocket",
connection: "Upgrade",
"sec-websocket-key": clientReq.headers["sec-websocket-key"] || "",
"sec-websocket-version": clientReq.headers["sec-websocket-version"] || "13",
"sec-websocket-extensions": clientReq.headers["sec-websocket-extensions"] || "",
},
});
proxyReq.on("upgrade", (proxyRes, proxySocket, proxyHead) => {
if (VERBOSE) log("ws", `WebSocket connected → ${targetUrl.hostname}${targetUrl.pathname}`);
clientSocket.write(
`HTTP/1.1 101 Switching Protocols\r\n` +
Object.entries(proxyRes.headers).map(([k, v]) => `${k}: ${v}`).join("\r\n") +
"\r\n\r\n"
);
if (clientHead && clientHead.length) proxySocket.write(clientHead);
proxySocket.pipe(clientSocket);
clientSocket.pipe(proxySocket);
proxySocket.on("error", () => clientSocket.destroy());
clientSocket.on("error", () => proxySocket.destroy());
});
proxyReq.on("error", (err) => {
log("error", `WebSocket proxy error: ${err.message}`);
clientSocket.end();
});
proxyReq.end();
}
// ─── 启动服务器 ──────────────────────────────────────────────
const server = http.createServer((req, res) => proxyRequest(req, res));
server.on("upgrade", (req, socket, head) => {
if (req.headers.upgrade?.toLowerCase() === "websocket") {
proxyWebSocket(req, socket, head);
} else {
socket.end();
}
});
server.on("clientError", (err, socket) => {
if (err.code === "ECONNRESET" || !socket.writable) return;
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
});
server.listen(LISTEN_PORT, LISTEN_HOST, () => {
const target = `${TARGET_PROTOCOL}//${TARGET_HOSTNAME}`;
console.log(`
${COLORS.cyan}╔══════════════════════════════════════════════════╗
║ Transparent Proxy — 透明代理 ║
╚══════════════════════════════════════════════════╝${COLORS.reset}
${COLORS.green}●${COLORS.reset} Listening: ${COLORS.yellow}http://${LISTEN_HOST}:${LISTEN_PORT}${COLORS.reset}
${COLORS.green}●${COLORS.reset} Target: ${COLORS.cyan}${target}${COLORS.reset}
${COLORS.dim}Test:${COLORS.reset}
curl http://localhost:${LISTEN_PORT}/your/path
`);
});
const shutdown = () => {
log("info", "Shutting down...");
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 5000);
};
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
1.3 修改Claude Code for VS Code配置信息
将可以key内容修改为第一步注册创建key
好的,准备工作完成,就开始开始了ai编程了。

OpenRouter的日志模块可以清晰地看到调用情况,如下图:

这里要特别感谢雷总,又给我们免费试用一周mimo大模型

更多推荐




所有评论(0)