mimo2codex 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +124 -0
- package/LICENSE +21 -0
- package/README.md +536 -0
- package/README.zh.md +750 -0
- package/dist/cli.js +202 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +86 -0
- package/dist/config.js.map +1 -0
- package/dist/server.js +161 -0
- package/dist/server.js.map +1 -0
- package/dist/translate/reqToChat.js +381 -0
- package/dist/translate/reqToChat.js.map +1 -0
- package/dist/translate/respToResponses.js +94 -0
- package/dist/translate/respToResponses.js.map +1 -0
- package/dist/translate/streamToSse.js +352 -0
- package/dist/translate/streamToSse.js.map +1 -0
- package/dist/translate/types.js +4 -0
- package/dist/translate/types.js.map +1 -0
- package/dist/upstream/chatStream.js +56 -0
- package/dist/upstream/chatStream.js.map +1 -0
- package/dist/upstream/mimoClient.js +99 -0
- package/dist/upstream/mimoClient.js.map +1 -0
- package/dist/util/ids.js +19 -0
- package/dist/util/ids.js.map +1 -0
- package/dist/util/log.js +32 -0
- package/dist/util/log.js.map +1 -0
- package/dist/util/sse.js +61 -0
- package/dist/util/sse.js.map +1 -0
- package/mimoskill/SKILL.md +145 -0
- package/mimoskill/assets/pet_prompt_template.md +94 -0
- package/mimoskill/references/models.md +111 -0
- package/mimoskill/references/pet_workflow.md +197 -0
- package/mimoskill/scripts/generate_pet.py +365 -0
- package/mimoskill/scripts/install_pet.sh +220 -0
- package/mimoskill/scripts/mimo_chat.py +199 -0
- package/package.json +69 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { buildConfig, parseArgv } from "./config.js";
|
|
3
|
+
import { startServer } from "./server.js";
|
|
4
|
+
import { setVerbose, log, redactKey } from "./util/log.js";
|
|
5
|
+
const VERSION = "0.1.0";
|
|
6
|
+
const HELP = `mimo2codex v${VERSION} — local proxy: Codex Responses API → Xiaomi MiMo Chat Completions
|
|
7
|
+
|
|
8
|
+
USAGE
|
|
9
|
+
mimo2codex [options]
|
|
10
|
+
mimo2codex print-config
|
|
11
|
+
mimo2codex print-cc-switch
|
|
12
|
+
|
|
13
|
+
OPTIONS
|
|
14
|
+
-p, --port <n> listen port (default: 8788, env: MIMO2CODEX_PORT)
|
|
15
|
+
--host <h> bind host (default: 127.0.0.1, env: MIMO2CODEX_HOST)
|
|
16
|
+
--base-url <url> MiMo base url (default: https://api.xiaomimimo.com/v1, env: MIMO_BASE_URL)
|
|
17
|
+
--api-key <key> MiMo api key (env: MIMO_API_KEY) — required
|
|
18
|
+
--no-reasoning hide MiMo reasoning_content from Codex (still re-injected for multi-turn quality)
|
|
19
|
+
--reasoning force reasoning passthrough (default)
|
|
20
|
+
-v, --verbose log every request (env: MIMO2CODEX_VERBOSE=1)
|
|
21
|
+
-V, --version print version
|
|
22
|
+
-h, --help show this help
|
|
23
|
+
|
|
24
|
+
SUBCOMMANDS
|
|
25
|
+
print-config print ~/.codex/auth.json + config.toml snippets (default;
|
|
26
|
+
works for Codex CLI and desktop app)
|
|
27
|
+
print-config --env-key print env-var-based variant (Codex CLI only — desktop app
|
|
28
|
+
will NOT see shell env vars set via export/setx)
|
|
29
|
+
print-cc-switch print auth.json + config.toml snippets for the cc-switch
|
|
30
|
+
desktop app (https://github.com/farion1231/cc-switch)
|
|
31
|
+
|
|
32
|
+
EXAMPLES
|
|
33
|
+
MIMO_API_KEY=sk-... mimo2codex
|
|
34
|
+
mimo2codex --port 9000 --base-url https://token-plan-cn.xiaomimimo.com/v1
|
|
35
|
+
mimo2codex print-config > codex-mimo.toml
|
|
36
|
+
mimo2codex print-config --env-key # legacy env-var variant
|
|
37
|
+
mimo2codex print-cc-switch
|
|
38
|
+
`;
|
|
39
|
+
// Default snippet — uses ~/.codex/auth.json + requires_openai_auth = true.
|
|
40
|
+
// This avoids the common "Missing environment variable: MIMO2CODEX_KEY" error
|
|
41
|
+
// on the Codex desktop app, which doesn't inherit shell env vars set via
|
|
42
|
+
// `export` or `setx`. Works for both CLI and desktop with no env setup.
|
|
43
|
+
function configSnippet(cfg) {
|
|
44
|
+
return `# Step 1 — write ~/.codex/auth.json (Windows: %USERPROFILE%\\.codex\\auth.json)
|
|
45
|
+
# Any non-empty value works; mimo2codex does not validate inbound credentials.
|
|
46
|
+
{
|
|
47
|
+
"OPENAI_API_KEY": "mimo2codex-local"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Step 2 — append to ~/.codex/config.toml (Windows: %USERPROFILE%\\.codex\\config.toml)
|
|
51
|
+
model = "mimo-v2.5-pro"
|
|
52
|
+
model_provider = "mimo"
|
|
53
|
+
|
|
54
|
+
[model_providers.mimo]
|
|
55
|
+
name = "MiMo (via mimo2codex)"
|
|
56
|
+
base_url = "http://${cfg.host}:${cfg.port}/v1"
|
|
57
|
+
wire_api = "responses"
|
|
58
|
+
requires_openai_auth = true
|
|
59
|
+
request_max_retries = 1
|
|
60
|
+
|
|
61
|
+
# Step 3 — completely quit and restart Codex (the desktop app must be relaunched
|
|
62
|
+
# for the new auth.json to be picked up). Then run \`codex\` and pick this provider.
|
|
63
|
+
|
|
64
|
+
# ⚠️ If you also use Codex with your real OpenAI account, this auth.json overwrites
|
|
65
|
+
# your OpenAI login. Use cc-switch (\`mimo2codex print-cc-switch\`) instead to switch
|
|
66
|
+
# between providers cleanly, or use \`mimo2codex print-config --env-key\` for the
|
|
67
|
+
# env-var-based variant (works for Codex CLI but not the desktop app).
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
// Legacy env_key variant — keeps ~/.codex/auth.json untouched (preserving any
|
|
71
|
+
// existing OpenAI login). Requires MIMO2CODEX_KEY to be set in the environment
|
|
72
|
+
// of the process running \`codex\`. Codex DESKTOP APP does not inherit shell env
|
|
73
|
+
// vars on macOS/Windows, so this variant only works reliably for the CLI.
|
|
74
|
+
function configSnippetEnvKey(cfg) {
|
|
75
|
+
return `# ~/.codex/config.toml — env-var variant (Codex CLI only; desktop app won't see shell env vars)
|
|
76
|
+
model = "mimo-v2.5-pro"
|
|
77
|
+
model_provider = "mimo"
|
|
78
|
+
|
|
79
|
+
[model_providers.mimo]
|
|
80
|
+
name = "MiMo (via mimo2codex)"
|
|
81
|
+
base_url = "http://${cfg.host}:${cfg.port}/v1"
|
|
82
|
+
wire_api = "responses"
|
|
83
|
+
env_key = "MIMO2CODEX_KEY"
|
|
84
|
+
request_max_retries = 1
|
|
85
|
+
|
|
86
|
+
# Then in your shell (the same shell you launch \`codex\` from):
|
|
87
|
+
# export MIMO2CODEX_KEY=anything # macOS/Linux/Git Bash
|
|
88
|
+
# $env:MIMO2CODEX_KEY="anything" # Windows PowerShell
|
|
89
|
+
# set MIMO2CODEX_KEY=anything # Windows CMD (current session only)
|
|
90
|
+
#
|
|
91
|
+
# For Codex DESKTOP APP, this variant does NOT work — desktop apps launched from
|
|
92
|
+
# Finder/Start Menu don't inherit shell env vars. Use the default print-config
|
|
93
|
+
# (auth.json variant) or \`mimo2codex print-cc-switch\` instead.
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
// cc-switch (https://github.com/farion1231/cc-switch) is a desktop app that
|
|
97
|
+
// manages multiple Codex providers via a "+" → "Custom" panel. It writes
|
|
98
|
+
// ~/.codex/auth.json + ~/.codex/config.toml when you switch providers.
|
|
99
|
+
// This subcommand prints both snippets in a copy-pasteable form so users can
|
|
100
|
+
// add mimo2codex as a custom Codex provider in cc-switch.
|
|
101
|
+
function ccSwitchSnippet(cfg) {
|
|
102
|
+
const authJson = JSON.stringify({ OPENAI_API_KEY: "mimo2codex-local" }, null, 2);
|
|
103
|
+
const configToml = `model_provider = "mimo2codex"
|
|
104
|
+
model = "mimo-v2.5-pro"
|
|
105
|
+
|
|
106
|
+
[model_providers.mimo2codex]
|
|
107
|
+
name = "MiMo (via mimo2codex)"
|
|
108
|
+
base_url = "http://${cfg.host}:${cfg.port}/v1"
|
|
109
|
+
wire_api = "responses"
|
|
110
|
+
requires_openai_auth = true
|
|
111
|
+
request_max_retries = 1
|
|
112
|
+
`;
|
|
113
|
+
return `# cc-switch — Add Provider → Codex tab → Custom
|
|
114
|
+
|
|
115
|
+
# ───────── auth.json (paste into the auth.json textarea) ─────────
|
|
116
|
+
${authJson}
|
|
117
|
+
|
|
118
|
+
# ───────── config.toml (paste into the config.toml textarea) ─────────
|
|
119
|
+
${configToml}
|
|
120
|
+
# Note: OPENAI_API_KEY can be any non-empty string — mimo2codex does not
|
|
121
|
+
# validate inbound credentials. Your real MiMo key stays in MIMO_API_KEY
|
|
122
|
+
# on the machine running mimo2codex.
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
function printStartupBanner(cfg) {
|
|
126
|
+
// eslint-disable-next-line no-console
|
|
127
|
+
console.log(`mimo2codex v${VERSION} listening on http://${cfg.host}:${cfg.port}`);
|
|
128
|
+
// eslint-disable-next-line no-console
|
|
129
|
+
console.log(`upstream: ${cfg.baseUrl}`);
|
|
130
|
+
// eslint-disable-next-line no-console
|
|
131
|
+
console.log(`api key: ${redactKey(cfg.apiKey)}`);
|
|
132
|
+
// eslint-disable-next-line no-console
|
|
133
|
+
console.log(`reasoning: ${cfg.exposeReasoning ? "passthrough" : "hidden"}`);
|
|
134
|
+
// eslint-disable-next-line no-console
|
|
135
|
+
console.log("");
|
|
136
|
+
// eslint-disable-next-line no-console
|
|
137
|
+
console.log(configSnippet({ host: cfg.host, port: cfg.port }));
|
|
138
|
+
}
|
|
139
|
+
function main() {
|
|
140
|
+
let parsed;
|
|
141
|
+
try {
|
|
142
|
+
parsed = parseArgv(process.argv.slice(2));
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// eslint-disable-next-line no-console
|
|
146
|
+
console.error(`error: ${err.message}`);
|
|
147
|
+
process.exit(2);
|
|
148
|
+
}
|
|
149
|
+
if (parsed.showHelp) {
|
|
150
|
+
// eslint-disable-next-line no-console
|
|
151
|
+
console.log(HELP);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (parsed.showVersion) {
|
|
155
|
+
// eslint-disable-next-line no-console
|
|
156
|
+
console.log(VERSION);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (parsed.positional[0] === "print-config") {
|
|
160
|
+
const host = parsed.host ?? "127.0.0.1";
|
|
161
|
+
const port = parsed.port ?? 8788;
|
|
162
|
+
const useEnvKey = parsed.envKey === true;
|
|
163
|
+
// eslint-disable-next-line no-console
|
|
164
|
+
console.log(useEnvKey ? configSnippetEnvKey({ host, port }) : configSnippet({ host, port }));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (parsed.positional[0] === "print-cc-switch") {
|
|
168
|
+
const host = parsed.host ?? "127.0.0.1";
|
|
169
|
+
const port = parsed.port ?? 8788;
|
|
170
|
+
// eslint-disable-next-line no-console
|
|
171
|
+
console.log(ccSwitchSnippet({ host, port }));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
let cfg;
|
|
175
|
+
try {
|
|
176
|
+
cfg = buildConfig(parsed, process.env, VERSION);
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
// eslint-disable-next-line no-console
|
|
180
|
+
console.error(`error: ${err.message}`);
|
|
181
|
+
process.exit(2);
|
|
182
|
+
}
|
|
183
|
+
setVerbose(cfg.verbose);
|
|
184
|
+
printStartupBanner(cfg);
|
|
185
|
+
const server = startServer(cfg);
|
|
186
|
+
server.on("listening", () => {
|
|
187
|
+
log.debug("server listening");
|
|
188
|
+
});
|
|
189
|
+
server.on("error", (err) => {
|
|
190
|
+
log.error("server error", { error: err.message });
|
|
191
|
+
process.exit(1);
|
|
192
|
+
});
|
|
193
|
+
const shutdown = (sig) => {
|
|
194
|
+
log.info(`received ${sig}, shutting down`);
|
|
195
|
+
server.close(() => process.exit(0));
|
|
196
|
+
setTimeout(() => process.exit(1), 5000).unref();
|
|
197
|
+
};
|
|
198
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
199
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
200
|
+
}
|
|
201
|
+
main();
|
|
202
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const DEFAULTS = {
|
|
2
|
+
host: "127.0.0.1",
|
|
3
|
+
port: 8788,
|
|
4
|
+
baseUrl: "https://api.xiaomimimo.com/v1",
|
|
5
|
+
};
|
|
6
|
+
export function parseArgv(argv) {
|
|
7
|
+
const out = { positional: [], showHelp: false, showVersion: false };
|
|
8
|
+
for (let i = 0; i < argv.length; i++) {
|
|
9
|
+
const a = argv[i];
|
|
10
|
+
const next = () => {
|
|
11
|
+
const v = argv[i + 1];
|
|
12
|
+
if (v === undefined)
|
|
13
|
+
throw new Error(`flag ${a} requires a value`);
|
|
14
|
+
i++;
|
|
15
|
+
return v;
|
|
16
|
+
};
|
|
17
|
+
switch (a) {
|
|
18
|
+
case "--port":
|
|
19
|
+
case "-p":
|
|
20
|
+
out.port = Number(next());
|
|
21
|
+
if (Number.isNaN(out.port))
|
|
22
|
+
throw new Error("--port must be a number");
|
|
23
|
+
break;
|
|
24
|
+
case "--host":
|
|
25
|
+
out.host = next();
|
|
26
|
+
break;
|
|
27
|
+
case "--base-url":
|
|
28
|
+
case "--baseurl":
|
|
29
|
+
out.baseUrl = next();
|
|
30
|
+
break;
|
|
31
|
+
case "--api-key":
|
|
32
|
+
out.apiKey = next();
|
|
33
|
+
break;
|
|
34
|
+
case "--no-reasoning":
|
|
35
|
+
out.exposeReasoning = false;
|
|
36
|
+
break;
|
|
37
|
+
case "--reasoning":
|
|
38
|
+
out.exposeReasoning = true;
|
|
39
|
+
break;
|
|
40
|
+
case "--verbose":
|
|
41
|
+
case "-v":
|
|
42
|
+
out.verbose = true;
|
|
43
|
+
break;
|
|
44
|
+
case "--env-key":
|
|
45
|
+
out.envKey = true;
|
|
46
|
+
break;
|
|
47
|
+
case "--help":
|
|
48
|
+
case "-h":
|
|
49
|
+
out.showHelp = true;
|
|
50
|
+
break;
|
|
51
|
+
case "--version":
|
|
52
|
+
case "-V":
|
|
53
|
+
out.showVersion = true;
|
|
54
|
+
break;
|
|
55
|
+
default:
|
|
56
|
+
if (a.startsWith("--")) {
|
|
57
|
+
throw new Error(`unknown flag: ${a}`);
|
|
58
|
+
}
|
|
59
|
+
out.positional.push(a);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
export function buildConfig(parsed, env, version) {
|
|
65
|
+
const exposeReasoningEnv = env.MIMO2CODEX_NO_REASONING ? false : true;
|
|
66
|
+
const verboseEnv = !!env.MIMO2CODEX_VERBOSE;
|
|
67
|
+
const apiKey = parsed.apiKey ?? env.MIMO_API_KEY ?? "";
|
|
68
|
+
if (!apiKey) {
|
|
69
|
+
throw new Error("missing MiMo API key — set MIMO_API_KEY env var or pass --api-key. " +
|
|
70
|
+
"Get one at https://platform.xiaomimimo.com/#/console/api-keys");
|
|
71
|
+
}
|
|
72
|
+
const portFromEnv = env.MIMO2CODEX_PORT ? Number(env.MIMO2CODEX_PORT) : undefined;
|
|
73
|
+
if (portFromEnv !== undefined && Number.isNaN(portFromEnv)) {
|
|
74
|
+
throw new Error("MIMO2CODEX_PORT must be a number");
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
host: parsed.host ?? env.MIMO2CODEX_HOST ?? DEFAULTS.host,
|
|
78
|
+
port: parsed.port ?? portFromEnv ?? DEFAULTS.port,
|
|
79
|
+
baseUrl: parsed.baseUrl ?? env.MIMO_BASE_URL ?? DEFAULTS.baseUrl,
|
|
80
|
+
apiKey,
|
|
81
|
+
exposeReasoning: parsed.exposeReasoning ?? exposeReasoningEnv,
|
|
82
|
+
verbose: parsed.verbose ?? verboseEnv,
|
|
83
|
+
userAgent: `mimo2codex/${version}`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { reqToChat } from "./translate/reqToChat.js";
|
|
3
|
+
import { respToResponses } from "./translate/respToResponses.js";
|
|
4
|
+
import { pipeChatStreamToResponses } from "./translate/streamToSse.js";
|
|
5
|
+
import { iterChatStreamChunks } from "./upstream/chatStream.js";
|
|
6
|
+
import { callMimo, UpstreamError } from "./upstream/mimoClient.js";
|
|
7
|
+
import { makeServerResponseSink } from "./util/sse.js";
|
|
8
|
+
import { log } from "./util/log.js";
|
|
9
|
+
const KEEPALIVE_INTERVAL_MS = 15_000;
|
|
10
|
+
async function readJsonBody(req, maxBytes = 16 * 1024 * 1024) {
|
|
11
|
+
return await new Promise((resolve, reject) => {
|
|
12
|
+
const chunks = [];
|
|
13
|
+
let total = 0;
|
|
14
|
+
req.on("data", (chunk) => {
|
|
15
|
+
total += chunk.length;
|
|
16
|
+
if (total > maxBytes) {
|
|
17
|
+
reject(new Error("request body too large"));
|
|
18
|
+
req.destroy();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
chunks.push(chunk);
|
|
22
|
+
});
|
|
23
|
+
req.on("end", () => {
|
|
24
|
+
try {
|
|
25
|
+
const text = Buffer.concat(chunks).toString("utf-8");
|
|
26
|
+
if (!text)
|
|
27
|
+
return resolve({});
|
|
28
|
+
resolve(JSON.parse(text));
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
reject(err);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
req.on("error", reject);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function sendJson(res, status, body) {
|
|
38
|
+
res.statusCode = status;
|
|
39
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
40
|
+
res.end(JSON.stringify(body));
|
|
41
|
+
}
|
|
42
|
+
function errorEnvelope(status, code, message) {
|
|
43
|
+
return {
|
|
44
|
+
error: {
|
|
45
|
+
type: status === 401
|
|
46
|
+
? "authentication_error"
|
|
47
|
+
: status === 429
|
|
48
|
+
? "rate_limit_exceeded"
|
|
49
|
+
: status >= 500
|
|
50
|
+
? "server_error"
|
|
51
|
+
: "invalid_request_error",
|
|
52
|
+
code,
|
|
53
|
+
message,
|
|
54
|
+
status,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function handleResponses(cfg, req, res) {
|
|
59
|
+
let payload;
|
|
60
|
+
try {
|
|
61
|
+
payload = await readJsonBody(req);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
return sendJson(res, 400, errorEnvelope(400, "invalid_json", `failed to parse request body: ${err.message}`));
|
|
65
|
+
}
|
|
66
|
+
if (!payload.model) {
|
|
67
|
+
return sendJson(res, 400, errorEnvelope(400, "missing_model", "request body must include 'model'"));
|
|
68
|
+
}
|
|
69
|
+
const chat = reqToChat(payload);
|
|
70
|
+
const stream = !!payload.stream;
|
|
71
|
+
chat.stream = stream;
|
|
72
|
+
const ac = new AbortController();
|
|
73
|
+
req.on("close", () => ac.abort());
|
|
74
|
+
if (!stream) {
|
|
75
|
+
try {
|
|
76
|
+
const upstreamRes = await callMimo({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey, userAgent: cfg.userAgent }, chat, ac.signal);
|
|
77
|
+
const chatJson = (await upstreamRes.json());
|
|
78
|
+
const responses = respToResponses(chatJson, payload, {
|
|
79
|
+
exposeReasoning: cfg.exposeReasoning,
|
|
80
|
+
});
|
|
81
|
+
return sendJson(res, 200, responses);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
if (err instanceof UpstreamError) {
|
|
85
|
+
return sendJson(res, err.status, errorEnvelope(err.status, err.code, err.message));
|
|
86
|
+
}
|
|
87
|
+
log.error("non-stream request failed", { error: err.message });
|
|
88
|
+
return sendJson(res, 500, errorEnvelope(500, "internal_error", err.message));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Streaming path. Strategy: don't open the SSE stream to the client until we
|
|
92
|
+
// know the upstream is OK. This way upstream errors map to clean HTTP errors
|
|
93
|
+
// instead of half-opened SSE streams that confuse the Codex client.
|
|
94
|
+
let upstreamRes;
|
|
95
|
+
try {
|
|
96
|
+
upstreamRes = await callMimo({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey, userAgent: cfg.userAgent }, chat, ac.signal);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
if (err instanceof UpstreamError) {
|
|
100
|
+
return sendJson(res, err.status, errorEnvelope(err.status, err.code, err.message));
|
|
101
|
+
}
|
|
102
|
+
log.error("stream request failed (pre-stream)", { error: err.message });
|
|
103
|
+
return sendJson(res, 500, errorEnvelope(500, "internal_error", err.message));
|
|
104
|
+
}
|
|
105
|
+
// Upstream returned 200 — now we can safely open the SSE stream.
|
|
106
|
+
const sink = makeServerResponseSink(res);
|
|
107
|
+
const keepalive = setInterval(() => sink.comment("keepalive"), KEEPALIVE_INTERVAL_MS);
|
|
108
|
+
res.on("close", () => clearInterval(keepalive));
|
|
109
|
+
try {
|
|
110
|
+
const chunks = iterChatStreamChunks(upstreamRes);
|
|
111
|
+
await pipeChatStreamToResponses(sink, { chunks }, payload, { exposeReasoning: cfg.exposeReasoning });
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
log.error("stream request failed (mid-stream)", { error: err.message });
|
|
115
|
+
// pipeChatStreamToResponses handles its own errors with response.failed,
|
|
116
|
+
// so reaching here means something unexpected in our own code.
|
|
117
|
+
if (!sink.closed()) {
|
|
118
|
+
sink.write("error", {
|
|
119
|
+
type: "error",
|
|
120
|
+
code: "server_error",
|
|
121
|
+
message: err.message,
|
|
122
|
+
sequence_number: 9999,
|
|
123
|
+
});
|
|
124
|
+
sink.end();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
clearInterval(keepalive);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function handleModels(res) {
|
|
132
|
+
sendJson(res, 200, {
|
|
133
|
+
object: "list",
|
|
134
|
+
data: [
|
|
135
|
+
{ id: "mimo-v2.5-pro", object: "model", owned_by: "xiaomi" },
|
|
136
|
+
{ id: "mimo-v2.5-pro[1m]", object: "model", owned_by: "xiaomi" },
|
|
137
|
+
{ id: "mimo-v2-flash", object: "model", owned_by: "xiaomi" },
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
export function startServer(cfg) {
|
|
142
|
+
const server = createServer((req, res) => {
|
|
143
|
+
const url = req.url ?? "/";
|
|
144
|
+
if (req.method === "GET" && (url === "/healthz" || url === "/")) {
|
|
145
|
+
sendJson(res, 200, { ok: true, name: "mimo2codex", baseUrl: cfg.baseUrl });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (req.method === "GET" && url.startsWith("/v1/models")) {
|
|
149
|
+
handleModels(res);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (req.method === "POST" && url.startsWith("/v1/responses")) {
|
|
153
|
+
void handleResponses(cfg, req, res);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
sendJson(res, 404, errorEnvelope(404, "not_found", `no route for ${req.method} ${url}`));
|
|
157
|
+
});
|
|
158
|
+
server.listen(cfg.port, cfg.host);
|
|
159
|
+
return server;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|