easyrouter-config 1.0.4 → 1.0.5
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/dist/index.js +67 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,11 +16,11 @@ import {
|
|
|
16
16
|
import { execa } from "execa";
|
|
17
17
|
import pc from "picocolors";
|
|
18
18
|
import { parseArgs } from "util";
|
|
19
|
-
var VERSION = true ? "1.0.
|
|
19
|
+
var VERSION = true ? "1.0.5" : "0.0.0-dev";
|
|
20
20
|
var PRIMARY_HOST = "https://easyrouter.io";
|
|
21
21
|
var FALLBACK_HOSTS = ["https://ezr.sh"];
|
|
22
22
|
var PROBE_TIMEOUT_MS = 5e3;
|
|
23
|
-
var
|
|
23
|
+
var CODEX_FALLBACK_MODEL = "gpt-5.5";
|
|
24
24
|
function parseCliArgs() {
|
|
25
25
|
try {
|
|
26
26
|
const { values } = parseArgs({
|
|
@@ -104,6 +104,61 @@ async function probeHost(host) {
|
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
var FETCH_MODELS_TIMEOUT_MS = 8e3;
|
|
108
|
+
async function fetchEasyRouterModels(host, verbose) {
|
|
109
|
+
const url = host.replace(/\/+$/, "") + "/api/pricing";
|
|
110
|
+
try {
|
|
111
|
+
const ctrl = new AbortController();
|
|
112
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_MODELS_TIMEOUT_MS);
|
|
113
|
+
const res = await fetch(url, { signal: ctrl.signal });
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
if (!res.ok) {
|
|
116
|
+
if (verbose) log.warn(`\u62C9\u53D6\u6A21\u578B\u5217\u8868\u5931\u8D25\uFF1AHTTP ${res.status}\uFF08\u5C06\u4F7F\u7528\u515C\u5E95\u6A21\u578B\uFF09`);
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
const json = await res.json();
|
|
120
|
+
const models = Array.isArray(json?.data) ? json.data : [];
|
|
121
|
+
if (verbose) log.info(`\u2713 \u62C9\u53D6\u5230 ${models.length} \u4E2A EasyRouter \u6A21\u578B`);
|
|
122
|
+
return models;
|
|
123
|
+
} catch (err) {
|
|
124
|
+
if (verbose) log.warn(`\u62C9\u53D6\u6A21\u578B\u5217\u8868\u5F02\u5E38\uFF1A${err?.message || err}\uFF08\u5C06\u4F7F\u7528\u515C\u5E95\u6A21\u578B\uFF09`);
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function pickBestCodexModel(models) {
|
|
129
|
+
const isLlm = (m) => (m.tags || "").toUpperCase().includes("LLM");
|
|
130
|
+
const supportsOpenAI = (m) => !m.supported_endpoint_types || m.supported_endpoint_types.includes("openai");
|
|
131
|
+
const isGpt5 = (m) => /^gpt-5(\b|[.\-])/i.test(m.model_name);
|
|
132
|
+
const isSmallTier = (m) => /(^|[\-_.])(mini|nano|small|lite|tiny)([\-_.]|$)/i.test(m.model_name);
|
|
133
|
+
const byNameDesc = (a, b) => b.model_name.localeCompare(a.model_name, "en");
|
|
134
|
+
const gpt5Big = models.filter((m) => isGpt5(m) && isLlm(m) && supportsOpenAI(m) && !isSmallTier(m)).sort(byNameDesc);
|
|
135
|
+
if (gpt5Big.length) return gpt5Big[0].model_name;
|
|
136
|
+
const gpt5Any = models.filter((m) => isGpt5(m) && isLlm(m) && supportsOpenAI(m)).sort(byNameDesc);
|
|
137
|
+
if (gpt5Any.length) return gpt5Any[0].model_name;
|
|
138
|
+
const anyBig = models.filter((m) => isLlm(m) && supportsOpenAI(m) && !isSmallTier(m)).sort(byNameDesc);
|
|
139
|
+
if (anyBig.length) return anyBig[0].model_name;
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
async function resolveCodexModel(host, interactive, verbose) {
|
|
143
|
+
const models = await fetchEasyRouterModels(host, verbose);
|
|
144
|
+
const auto = pickBestCodexModel(models);
|
|
145
|
+
if (auto) {
|
|
146
|
+
if (verbose) log.info(`\u81EA\u52A8\u9009\u62E9 Codex \u6A21\u578B\uFF1A${pc.cyan(auto)}`);
|
|
147
|
+
return auto;
|
|
148
|
+
}
|
|
149
|
+
if (interactive && models.length) {
|
|
150
|
+
const candidates = models.filter((m) => /^gpt-5|codex/i.test(m.model_name)).sort((a, b) => b.model_name.localeCompare(a.model_name, "en")).slice(0, 8);
|
|
151
|
+
if (candidates.length) {
|
|
152
|
+
const picked = await select({
|
|
153
|
+
message: "\u672A\u81EA\u52A8\u8BC6\u522B Codex \u63A8\u8350\u6A21\u578B\uFF0C\u8BF7\u624B\u52A8\u9009\u62E9",
|
|
154
|
+
options: candidates.map((m) => ({ value: m.model_name, label: m.model_name }))
|
|
155
|
+
});
|
|
156
|
+
if (!isCancel(picked)) return picked;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (verbose) log.warn(`\u65E0\u6CD5\u81EA\u52A8\u9009\u62E9 Codex \u6A21\u578B\uFF0C\u4F7F\u7528\u515C\u5E95\uFF1A${CODEX_FALLBACK_MODEL}`);
|
|
160
|
+
return CODEX_FALLBACK_MODEL;
|
|
161
|
+
}
|
|
107
162
|
var IDLE_TIMEOUT_MS = 6e4;
|
|
108
163
|
var HARD_TIMEOUT_MS = 3e5;
|
|
109
164
|
async function configureViaZcf(opts) {
|
|
@@ -137,7 +192,7 @@ async function configureViaZcf(opts) {
|
|
|
137
192
|
"false"
|
|
138
193
|
];
|
|
139
194
|
if (opts.target === "codex") {
|
|
140
|
-
args.push("--api-model",
|
|
195
|
+
args.push("--api-model", opts.apiModel || CODEX_FALLBACK_MODEL);
|
|
141
196
|
}
|
|
142
197
|
const isWin = process.platform === "win32";
|
|
143
198
|
const cmd = isWin ? "npx.cmd" : "npx";
|
|
@@ -368,6 +423,12 @@ async function main() {
|
|
|
368
423
|
}
|
|
369
424
|
}
|
|
370
425
|
if (configCodex) {
|
|
426
|
+
const codexModel = await resolveCodexModel(
|
|
427
|
+
selectedHost,
|
|
428
|
+
/* interactive */
|
|
429
|
+
!args.apiKey,
|
|
430
|
+
verbose
|
|
431
|
+
);
|
|
371
432
|
const useSpinner = !verbose && process.stdout.isTTY;
|
|
372
433
|
const s = useSpinner ? spinner() : null;
|
|
373
434
|
s?.start("\u914D\u7F6E Codex");
|
|
@@ -377,11 +438,12 @@ async function main() {
|
|
|
377
438
|
target: "codex",
|
|
378
439
|
baseUrl: codexBaseUrl,
|
|
379
440
|
apiKey,
|
|
441
|
+
apiModel: codexModel,
|
|
380
442
|
verbose,
|
|
381
443
|
onProgress: (line) => s?.message(`\u914D\u7F6E Codex \xB7 ${pc.dim(line)}`)
|
|
382
444
|
});
|
|
383
|
-
s?.stop(pc.green("\u2713 Codex \u914D\u7F6E\u5B8C\u6210 ") + pc.dim(
|
|
384
|
-
if (!s) log.success(pc.green(
|
|
445
|
+
s?.stop(pc.green("\u2713 Codex \u914D\u7F6E\u5B8C\u6210 ") + pc.dim(`\u2192 model=${codexModel}`));
|
|
446
|
+
if (!s) log.success(pc.green(`\u2713 Codex \u914D\u7F6E\u5B8C\u6210 \u2192 model=${codexModel}`));
|
|
385
447
|
} catch (err) {
|
|
386
448
|
s?.stop(pc.red("\u2717 Codex \u914D\u7F6E\u5931\u8D25"));
|
|
387
449
|
console.error(err.message);
|