Spaces:
Runtime error
Runtime error
File size: 5,958 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | import { describe, it, before } from "node:test";
import assert from "node:assert";
import { MimocodeExecutor, generateFingerprint } from "../../open-sse/executors/mimocode.ts";
const PROXY_URL = process.env.MIMOCODE_SOCKS5_PROXY;
function parseProxyUrl(url: string): { type: string; host: string; port: number } | null {
try {
const parsed = new URL(url);
return { type: parsed.protocol.replace(":", ""), host: parsed.hostname, port: parsed.port ? Number(parsed.port) : 1080 };
} catch {
return null;
}
}
function requireProxy() {
if (!PROXY_URL) {
return false;
}
const parsed = parseProxyUrl(PROXY_URL);
return parsed !== null;
}
const proxyConfig = PROXY_URL ? parseProxyUrl(PROXY_URL) : null;
describe("mimocode per-account proxy — SOCKS5 integration", { timeout: 30_000 }, () => {
before(() => {
if (!PROXY_URL) {
console.log("# MIMOCODE_SOCKS5_PROXY not set, skipping live proxy tests");
}
});
it("bootstrap returns JWT through configured proxy", { skip: !requireProxy() ? "MIMOCODE_SOCKS5_PROXY not set" : false }, async () => {
process.env.ENABLE_SOCKS5_PROXY = "true";
const { Socks5ProxyAgent } = await import("undici");
const agent = new Socks5ProxyAgent(PROXY_URL!);
const fp = generateFingerprint("integration-bootstrap-" + Date.now());
const resp = await fetch("https://api.xiaomimimo.com/api/free-ai/bootstrap", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ client: fp }),
// @ts-expect-error — undici dispatcher
dispatcher: agent,
signal: AbortSignal.timeout(15_000),
});
assert.strictEqual(resp.status, 200, `Bootstrap through proxy: expected 200, got ${resp.status}`);
const data = await resp.json();
assert.ok(data.jwt, "Response should contain JWT");
assert.ok(typeof data.jwt === "string" && data.jwt.length > 10, "JWT should be a non-trivial string");
});
it("chat request succeeds through configured proxy", { skip: !requireProxy() ? "MIMOCODE_SOCKS5_PROXY not set" : false }, async () => {
process.env.ENABLE_SOCKS5_PROXY = "true";
const { Socks5ProxyAgent } = await import("undici");
const agent = new Socks5ProxyAgent(PROXY_URL!);
const fp = generateFingerprint("integration-chat-" + Date.now());
const bootstrapResp = await fetch("https://api.xiaomimimo.com/api/free-ai/bootstrap", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ client: fp }),
// @ts-expect-error — undici dispatcher
dispatcher: agent,
signal: AbortSignal.timeout(15_000),
});
assert.strictEqual(bootstrapResp.status, 200);
const { jwt } = await bootstrapResp.json();
const chatResp = await fetch("https://api.xiaomimimo.com/api/free-ai/openai/chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${jwt}`,
"X-Mimo-Source": "mimocode-cli-free",
},
body: JSON.stringify({
model: "mimo-auto",
messages: [
{ role: "system", content: "You are MiMoCode, an interactive CLI tool that helps users with software engineering tasks." },
{ role: "user", content: "Say exactly: proxy-integration-ok" },
],
stream: false,
}),
// @ts-expect-error — undici dispatcher
dispatcher: agent,
signal: AbortSignal.timeout(20_000),
});
assert.ok(chatResp.status === 200 || chatResp.status === 429,
`Chat through proxy: expected 200/429, got ${chatResp.status}`);
});
it("accounts carry proxy config after sync", () => {
const exec = new MimocodeExecutor();
const fp = "integration-fp-1";
const cfg = proxyConfig || { type: "socks5", host: "127.0.0.1", port: 1080 };
(exec as any).accounts = [
{ fingerprint: fp, jwt: "", expiresAt: 0, cooldownUntil: 0, consecutiveFails: 0, proxy: null },
];
(exec as any).nextAccountIdx = 0;
(exec as any).syncAccountsFromCredentials({
providerSpecificData: {
accountProxies: [{ fingerprint: fp, proxy: cfg }],
},
});
const acct = (exec as any).accounts.find((a: any) => a.fingerprint === fp);
assert.ok(acct, "Account should exist");
assert.deepStrictEqual(acct.proxy, cfg);
});
it("two accounts with different proxies tracked independently", () => {
const exec = new MimocodeExecutor();
const fp1 = "integration-fp-a";
const fp2 = "integration-fp-b";
const proxy1 = { type: "http" as const, host: "proxy-a.example.com", port: 8080 };
const proxy2 = { type: "socks5" as const, host: "proxy-b.example.com", port: 1080 };
(exec as any).accounts = [
{ fingerprint: fp1, jwt: "", expiresAt: 0, cooldownUntil: 0, consecutiveFails: 0, proxy: null },
{ fingerprint: fp2, jwt: "", expiresAt: 0, cooldownUntil: 0, consecutiveFails: 0, proxy: null },
];
(exec as any).nextAccountIdx = 0;
(exec as any).syncAccountsFromCredentials({
providerSpecificData: {
accountProxies: [
{ fingerprint: fp1, proxy: proxy1 },
{ fingerprint: fp2, proxy: proxy2 },
],
},
});
const a1 = (exec as any).accounts.find((a: any) => a.fingerprint === fp1);
const a2 = (exec as any).accounts.find((a: any) => a.fingerprint === fp2);
assert.deepStrictEqual(a1.proxy, proxy1, "Account 1 should have proxy1");
assert.deepStrictEqual(a2.proxy, proxy2, "Account 2 should have proxy2");
assert.notDeepStrictEqual(a1.proxy, a2.proxy, "Proxies should differ");
});
it("no accountProxies keeps all proxies null (backward compat)", () => {
const exec = new MimocodeExecutor();
const accounts = (exec as any).accounts;
assert.ok(accounts.length >= 1);
for (const acct of accounts) {
assert.strictEqual(acct.proxy, null, "Default account proxy should be null");
}
});
});
|