Feat : Kiro MITM
This commit is contained in:
1 parent
065761268c
commit
03ff35100d
13 files changed
+266
-169
No files matched your search
@@ -0,0 +1,20 @@
|
||||
const { err } = require("../logger");
|
||||
const { fetchRouter, pipeSSE } = require("./base");
|
||||
|
||||
/**
|
||||
* Intercept Antigravity (Gemini) request — replace model and forward to router
|
||||
*/
|
||||
async function intercept(req, res, bodyBuffer, mappedModel) {
|
||||
try {
|
||||
const body = JSON.parse(bodyBuffer.toString());
|
||||
body.model = mappedModel;
|
||||
const routerRes = await fetchRouter(body);
|
||||
await pipeSSE(routerRes, res);
|
||||
} catch (error) {
|
||||
err(`[antigravity] ${error.message}`);
|
||||
if (!res.headersSent) res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: { message: error.message, type: "mitm_error" } }));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { intercept };
|
||||
@@ -0,0 +1,50 @@
|
||||
const { log, err } = require("../logger");
|
||||
|
||||
const ROUTER_BASE = "http://localhost:20128";
|
||||
const API_KEY = process.env.ROUTER_API_KEY;
|
||||
|
||||
/**
|
||||
* Send body to 9Router at the given path and return the fetch Response object
|
||||
*/
|
||||
async function fetchRouter(openaiBody, path = "/v1/chat/completions") {
|
||||
const response = await fetch(`${ROUTER_BASE}${path}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(API_KEY && { "Authorization": `Bearer ${API_KEY}` })
|
||||
},
|
||||
body: JSON.stringify(openaiBody)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errText = await response.text().catch(() => "");
|
||||
throw new Error(`[${response.status}]: ${errText}`);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pipe SSE stream from router directly to client response
|
||||
*/
|
||||
async function pipeSSE(routerRes, res) {
|
||||
const ct = routerRes.headers.get("content-type") || "application/json";
|
||||
const resHeaders = { "Content-Type": ct, "Cache-Control": "no-cache", "Connection": "keep-alive" };
|
||||
if (ct.includes("text/event-stream")) resHeaders["X-Accel-Buffering"] = "no";
|
||||
res.writeHead(200, resHeaders);
|
||||
|
||||
if (!routerRes.body) {
|
||||
res.end(await routerRes.text().catch(() => ""));
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = routerRes.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) { res.end(); break; }
|
||||
res.write(decoder.decode(value, { stream: true }));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { fetchRouter, pipeSSE };
|
||||
@@ -0,0 +1,35 @@
|
||||
const { err } = require("../logger");
|
||||
const { fetchRouter, pipeSSE } = require("./base");
|
||||
|
||||
// Map Copilot endpoint → 9Router path
|
||||
const URL_MAP = {
|
||||
"/chat/completions": "/v1/chat/completions",
|
||||
"/v1/messages": "/v1/messages",
|
||||
"/responses": "/v1/responses",
|
||||
};
|
||||
|
||||
function resolveRouterPath(reqUrl) {
|
||||
for (const [pattern, routerPath] of Object.entries(URL_MAP)) {
|
||||
if (reqUrl.includes(pattern)) return routerPath;
|
||||
}
|
||||
return "/v1/chat/completions";
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept Copilot request — replace model and forward to matching 9Router endpoint
|
||||
*/
|
||||
async function intercept(req, res, bodyBuffer, mappedModel) {
|
||||
try {
|
||||
const body = JSON.parse(bodyBuffer.toString());
|
||||
body.model = mappedModel;
|
||||
const routerPath = resolveRouterPath(req.url);
|
||||
const routerRes = await fetchRouter(body, routerPath);
|
||||
await pipeSSE(routerRes, res);
|
||||
} catch (error) {
|
||||
err(`[copilot] ${error.message}`);
|
||||
if (!res.headersSent) res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: { message: error.message, type: "mitm_error" } }));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { intercept };
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Cursor MITM handler — coming soon
|
||||
* This feature is currently under development.
|
||||
*/
|
||||
async function intercept(req, res) {
|
||||
res.writeHead(501, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({
|
||||
error: {
|
||||
message: "Cursor MITM support is coming soon.",
|
||||
type: "not_implemented"
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
module.exports = { intercept };
|
||||
@@ -0,0 +1,20 @@
|
||||
const { err } = require("../logger");
|
||||
const { fetchRouter, pipeSSE } = require("./base");
|
||||
|
||||
/**
|
||||
* Intercept Kiro request — replace model and forward to router
|
||||
*/
|
||||
async function intercept(req, res, bodyBuffer, mappedModel) {
|
||||
try {
|
||||
const body = JSON.parse(bodyBuffer.toString());
|
||||
body.model = mappedModel;
|
||||
const routerRes = await fetchRouter(body);
|
||||
await pipeSSE(routerRes, res);
|
||||
} catch (error) {
|
||||
err(`[Kiro] ${error.message}`);
|
||||
if (!res.headersSent) res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: { message: error.message, type: "mitm_error" } }));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { intercept };
|
||||
Reference in new issue
Block a user