easyrouter-config 1.0.7 → 1.0.9

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.
Files changed (2) hide show
  1. package/dist/index.js +71 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,14 +4,14 @@
4
4
  import {
5
5
  intro,
6
6
  outro,
7
- text,
8
7
  multiselect,
9
8
  confirm,
10
9
  spinner,
11
10
  isCancel as isCancel2,
12
11
  cancel,
13
12
  log as log3,
14
- note
13
+ note,
14
+ password
15
15
  } from "@clack/prompts";
16
16
  import pc6 from "picocolors";
17
17
 
@@ -20,7 +20,7 @@ import { parseArgs } from "util";
20
20
  import pc from "picocolors";
21
21
 
22
22
  // src/config/constants.ts
23
- var VERSION = true ? "1.0.7" : "0.0.0-dev";
23
+ var VERSION = true ? "1.0.9" : "0.0.0-dev";
24
24
  var PRIMARY_HOST = "https://easyrouter.io";
25
25
  var FALLBACK_HOSTS = ["https://ezr.sh"];
26
26
  var HOST_PROBE_TIMEOUT_MS = 5e3;
@@ -250,21 +250,21 @@ async function runZcf(opts) {
250
250
  windowsHide: true
251
251
  });
252
252
  const handleChunk = (chunk, target) => {
253
- const text2 = typeof chunk === "string" ? chunk : chunk.toString("utf8");
254
- if (target === "out") capturedOut += text2;
255
- else capturedErr += text2;
253
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
254
+ if (target === "out") capturedOut += text;
255
+ else capturedErr += text;
256
256
  lastDataAt = Date.now();
257
257
  if (opts.onProgress) {
258
258
  const now = Date.now();
259
259
  if (now - lastProgressAt >= PROGRESS_THROTTLE_MS) {
260
260
  lastProgressAt = now;
261
- const lines = text2.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
261
+ const lines = text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
262
262
  const last = lines[lines.length - 1];
263
263
  if (last) opts.onProgress(last.slice(0, 60));
264
264
  }
265
265
  }
266
266
  if (opts.verbose) {
267
- process.stderr.write(text2);
267
+ process.stderr.write(text);
268
268
  }
269
269
  };
270
270
  child.stdout?.on("data", (c) => handleChunk(c, "out"));
@@ -362,7 +362,46 @@ var claudeClient = {
362
362
  // src/config/models.ts
363
363
  import { log as log2, select, isCancel } from "@clack/prompts";
364
364
  import pc4 from "picocolors";
365
- function toOpenClawCost(m) {
365
+ async function fetchOpenRouterContextMap(verbose) {
366
+ const map = /* @__PURE__ */ new Map();
367
+ try {
368
+ const ctrl = new AbortController();
369
+ const timer = setTimeout(() => ctrl.abort(), FETCH_MODELS_TIMEOUT_MS);
370
+ const res = await fetch("https://openrouter.ai/api/v1/models", {
371
+ signal: ctrl.signal,
372
+ headers: { "User-Agent": "easyrouter-config/1.0" }
373
+ });
374
+ clearTimeout(timer);
375
+ if (!res.ok) return map;
376
+ const json = await res.json();
377
+ const models = Array.isArray(json?.data) ? json.data : [];
378
+ for (const m of models) {
379
+ const short = m.id.split("/").pop() ?? m.id;
380
+ const ctx = m.context_length ?? 0;
381
+ const maxOut = m.max_completion_tokens ?? Math.min(32768, Math.max(4096, Math.floor(ctx / 4)));
382
+ const entry = { contextLength: ctx, maxTokens: maxOut };
383
+ map.set(short.toLowerCase(), entry);
384
+ map.set(normalizeModelId(short), entry);
385
+ }
386
+ if (verbose) log2.info(`\u2713 OpenRouter: \u62C9\u53D6\u5230 ${models.length} \u4E2A\u6A21\u578B\u4E0A\u4E0B\u6587\u4FE1\u606F`);
387
+ } catch (err) {
388
+ if (verbose) log2.warn(`OpenRouter \u62C9\u53D6\u5931\u8D25\uFF1A${err?.message}\uFF08\u5C06\u7528\u515C\u5E95 contextWindow\uFF09`);
389
+ }
390
+ return map;
391
+ }
392
+ function normalizeModelId(s) {
393
+ s = s.replace(/-\d{6}$/, "");
394
+ s = s.replace(/(?<=[a-zA-Z])-(\d+)-(\d+)(?=-|$)/g, "-$1.$2");
395
+ return s.toLowerCase();
396
+ }
397
+ var _orContextMapPromise = null;
398
+ function getOpenRouterContextMap(verbose) {
399
+ if (!_orContextMapPromise) {
400
+ _orContextMapPromise = fetchOpenRouterContextMap(verbose);
401
+ }
402
+ return _orContextMapPromise;
403
+ }
404
+ function toOpenClawCost(m, orContextMap) {
366
405
  const ratio = m.model_ratio ?? 0;
367
406
  const compRatio = m.completion_ratio ?? 1;
368
407
  const cacheRatio = m.cache_ratio ?? 0;
@@ -370,9 +409,16 @@ function toOpenClawCost(m) {
370
409
  const output = round6(ratio * compRatio * 2e-3);
371
410
  const cacheRead = round6(ratio * cacheRatio * 2e-3);
372
411
  const cacheWrite = round6(input * 1.25);
373
- const tiered = m.tiered_pricing;
374
- const contextWindow = tiered?.input?.threshold ?? tiered?.output?.threshold ?? tiered?.cache_read?.threshold ?? 131072;
375
- const maxTokens = Math.min(32768, Math.max(4096, Math.floor(contextWindow / 4)));
412
+ let contextWindow = 131072;
413
+ let maxTokens = 32768;
414
+ if (orContextMap) {
415
+ const name = m.model_name;
416
+ const hit = orContextMap.get(name.toLowerCase()) ?? orContextMap.get(normalizeModelId(name));
417
+ if (hit && hit.contextLength > 0) {
418
+ contextWindow = hit.contextLength;
419
+ maxTokens = hit.maxTokens;
420
+ }
421
+ }
376
422
  return {
377
423
  cost: { input, output, cacheRead, cacheWrite },
378
424
  contextWindow,
@@ -577,7 +623,8 @@ var openClawClient = {
577
623
  await runOpenClawSubprocess(args, ctx);
578
624
  let registered = 0;
579
625
  if (llms.length > 1) {
580
- registered = await patchOpenClawJson(llms, defaultModel, ctx);
626
+ const orContextMap = await getOpenRouterContextMap(ctx.verbose);
627
+ registered = await patchOpenClawJson(llms, defaultModel, ctx, orContextMap);
581
628
  }
582
629
  return {
583
630
  configPaths: ["~/.openclaw/openclaw.json"],
@@ -585,7 +632,7 @@ var openClawClient = {
585
632
  };
586
633
  }
587
634
  };
588
- async function patchOpenClawJson(llms, defaultModel, ctx) {
635
+ async function patchOpenClawJson(llms, defaultModel, ctx, orContextMap) {
589
636
  const configPath = join(homedir(), ".openclaw", "openclaw.json");
590
637
  let json;
591
638
  try {
@@ -623,11 +670,13 @@ async function patchOpenClawJson(llms, defaultModel, ctx) {
623
670
  let updated = 0;
624
671
  const agentsModels = json?.agents?.defaults?.models ?? null;
625
672
  for (const m of llms) {
626
- const { cost, contextWindow, maxTokens } = toOpenClawCost(m);
673
+ const { cost, contextWindow, maxTokens } = toOpenClawCost(m, orContextMap);
627
674
  const idx = existingById.get(m.model_name);
628
675
  if (idx !== void 0) {
629
676
  const existing = provider.models[idx];
630
- if (existing.cost?.input === 0 && existing.cost?.output === 0) {
677
+ const costChanged = existing.cost?.input !== cost.input || existing.cost?.output !== cost.output;
678
+ const ctxChanged = existing.contextWindow !== contextWindow;
679
+ if (costChanged || ctxChanged) {
631
680
  existing.cost = cost;
632
681
  existing.contextWindow = contextWindow;
633
682
  existing.maxTokens = maxTokens;
@@ -692,20 +741,20 @@ async function runOpenClawSubprocess(args, ctx) {
692
741
  windowsHide: true
693
742
  });
694
743
  const handleChunk = (chunk, target) => {
695
- const text2 = typeof chunk === "string" ? chunk : chunk.toString("utf8");
696
- if (target === "out") capturedOut += text2;
697
- else capturedErr += text2;
744
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
745
+ if (target === "out") capturedOut += text;
746
+ else capturedErr += text;
698
747
  lastDataAt = Date.now();
699
748
  if (ctx.onProgress) {
700
749
  const now = Date.now();
701
750
  if (now - lastProgressAt >= PROGRESS_THROTTLE_MS) {
702
751
  lastProgressAt = now;
703
- const lines = text2.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
752
+ const lines = text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
704
753
  const last = lines[lines.length - 1];
705
754
  if (last) ctx.onProgress(last.slice(0, 60));
706
755
  }
707
756
  }
708
- if (ctx.verbose) process.stderr.write(text2);
757
+ if (ctx.verbose) process.stderr.write(text);
709
758
  };
710
759
  child.stdout?.on("data", (c) => handleChunk(c, "out"));
711
760
  child.stderr?.on("data", (c) => handleChunk(c, "err"));
@@ -892,9 +941,8 @@ async function main() {
892
941
  apiKey = normalizeApiKey(args.apiKey);
893
942
  log3.info(`API Key: ${pc6.dim(maskKey(apiKey))}\uFF08\u6765\u81EA\u547D\u4EE4\u884C\u53C2\u6570\uFF09`);
894
943
  } else {
895
- const input = await text({
944
+ const input = await password({
896
945
  message: "\u8BF7\u8F93\u5165\u4F60\u7684 EasyRouter API Key",
897
- placeholder: "sk-xxxxxxxxxxxxxxxx",
898
946
  validate(value) {
899
947
  if (!value) return "\u4E0D\u80FD\u4E3A\u7A7A";
900
948
  if (value.trim().length < 8) return "Key \u957F\u5EA6\u770B\u8D77\u6765\u4E0D\u5BF9";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easyrouter-config",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "🚀 一键把 EasyRouter 接入 Claude Code & Codex —— 粘贴 Key 即用",
5
5
  "type": "module",
6
6
  "bin": {