mslxdff 0.1.222 → 0.1.224

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mslxdff",
3
- "version": "0.1.222",
3
+ "version": "0.1.224",
4
4
  "description": "测试项目,请勿使用。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,7 @@ export async function renderOtherProviders({ pickedIds, ids, fullAliases }) {
10
10
  try { _la2(); } catch {}
11
11
  const configs = loadProviderConfigs();
12
12
  const otherIds = Object.keys(configs).filter((k) => String(k).toLowerCase() !== "opencode");
13
- const order2 = ["workbuddy", "cline", "openrouter", "bai", "qoder", "qwenwork", "globalqwenwork", "traework", "zcode", "raccoon", "codearts"];
13
+ const order2 = ["workbuddy", "cline", "openrouter", "bai", "qoder", "qwenwork", "globalqwenwork", "traework", "zcode", "raccoon", "codearts", "railway"];
14
14
  otherIds.sort((a, b) => {
15
15
  const ia = order2.indexOf(a), ib = order2.indexOf(b);
16
16
  if (ia !== -1 || ib !== -1) {
@@ -31,10 +31,25 @@ export async function renderOtherProviders({ pickedIds, ids, fullAliases }) {
31
31
  ? (allowed.length ? `allowlist ${allowed.length} (allowAny ON)` : `allowAny ON (allowlist 空=放行全部)`)
32
32
  : (allowed.length ? `allowlist ${allowed.length} (allowAny OFF)` : `allowlist 空 + allowAny OFF = 阻塞`);
33
33
  console.log(`\n ── ${pid} (${header})${baseUrl ? ` baseUrl=${baseUrl}` : ""} ──`);
34
+ // allowAny ON 且没设 allowlist = 该供应商全部模型放行。此时**把 live 列表展开**:
35
+ // 只打「未设 allowlist」两行会让用户以为这家没有模型(2026-10-20 railway 就是这么"消失"的)。
36
+ const live = (ids || []).filter((id) => String(id).startsWith(`${pid}/`));
34
37
  if (!allowed.length) {
35
38
  if (allowAny) {
36
- console.log(` (未设 allowlist,全部模型放行) 查看 live 列表: mslxdff -provider ${pid} models`);
37
- console.log(` 限制可用模型: mslxdff -provider ${pid} allowlist set <model1> <model2>`);
39
+ if (live.length) {
40
+ for (const canonical of live.sort(compareModelIds)) {
41
+ let alias = null;
42
+ try { alias = _gaf(canonical); } catch {}
43
+ if (!alias && String(canonical).includes("/")) alias = String(canonical).replace(/\//g, "-");
44
+ const aliasStr = alias && alias !== canonical ? ` (别名: ${alias})` : "";
45
+ const pickedMark = pickedIds.includes(canonical) || pickedIds.includes(alias || "") ? "*" : " ";
46
+ console.log(` ${pickedMark} ${canonical}${aliasStr}`);
47
+ }
48
+ console.log(` 管理: mslxdff -provider ${pid} allowlist [list|add|remove|clear] | allowAny on|off`);
49
+ } else {
50
+ console.log(` (未设 allowlist,全部模型放行) 查看 live 列表: mslxdff -provider ${pid} models`);
51
+ console.log(` 限制可用模型: mslxdff -provider ${pid} allowlist set <model1> <model2>`);
52
+ }
38
53
  } else {
39
54
  console.log(` 阻塞中:无可用模型 — 设白名单: mslxdff -provider ${pid} allowlist set <model1> <model2>`);
40
55
  console.log(` 或放行全部: mslxdff -provider ${pid} allowAny on`);
@@ -30,7 +30,31 @@ export async function handleModelList(args, idx, sub) {
30
30
  }
31
31
  }
32
32
  const cacheFile = join(logDir(), "models.json");
33
+ // 模型来源:优先问**本机网关**(daemon 的多供应商聚合,含 railway/codearts/workbuddy 等),
34
+ // 拉不到才退回「直连 opencode 单家」——后者只有一个供应商,会让所有定制供应商的模型消失
35
+ // (2026-10-20 实测:-models 看不到 railway 正是这个原因)。
36
+ async function tryGatewayModels() {
37
+ try {
38
+ const port = process.env.PORT || process.env.MSLXDFF_PORT || 8989;
39
+ const { loadToken } = await import("../../../state.js");
40
+ const { token } = await loadToken();
41
+ if (!token) return null;
42
+ const res = await fetch(`http://127.0.0.1:${port}/v1/models?all=1`, {
43
+ headers: { Authorization: `Bearer ${token}` },
44
+ signal: AbortSignal.timeout(6000),
45
+ });
46
+ if (!res.ok) return null;
47
+ const j = await res.json();
48
+ return Array.isArray(j?.data) && j.data.length ? j : null;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
33
53
  async function tryRefreshModels() {
54
+ // ① 网关聚合(最全,含所有已装配供应商)
55
+ const viaGateway = await tryGatewayModels();
56
+ if (viaGateway) return viaGateway;
57
+ // ② 回退:直连 opencode(daemon 没起/无 token 时的单家兜底)
34
58
  try {
35
59
  const models = createModelsService({
36
60
  baseUrl: process.env.UPSTREAM_BASE_URL || "https://opencode.ai",
@@ -7,12 +7,13 @@
7
7
  // ③ 带 opencode 身份头(UA + client/session/request/project),否则同样 403。
8
8
  //
9
9
  // 端点:<base>/zen/v1/models、<base>/zen/v1/chat/completions(base 缺省为 Railway 反代根)。
10
+ import crypto from "node:crypto";
10
11
  import { createKeyRing } from "./keyring.js";
11
12
  import { collectApiKeysGeneric, envInt, createAgent, getUndici, joinUrl } from "./base.js";
12
13
  import { compatFetch } from "../compat.js";
13
14
  import { loadProviderBaseUrl, loadProviderKeys, saveProviderConfig } from "../state.js";
14
15
  import { ensureFreeLaneShape, aggregateChatSse } from "../free-lane.js";
15
- import { genId, opencodeUa } from "../opencode-identity.js";
16
+ import { genId, opencodeUa, digestIdTail } from "../opencode-identity.js";
16
17
  import { joinModelId } from "./model-id.js";
17
18
 
18
19
  const { UndiciFetch } = getUndici();
@@ -72,17 +73,31 @@ export function createRailwayProvider({
72
73
  } catch {}
73
74
 
74
75
  // 身份头 + free-lane:railway 反代的是 zen,两样缺一不可(缺任一都是 403 FreeTierError)。
76
+ // 会话取值同 generic.js 的 resolveGoSession:已合规(ses_ 前缀)直接用;客户端透传的任意串
77
+ // 做 sha1 摘要派生(稳定 + 合规);无则每请求 fresh。绝不可裸拼 `ses_` + 客户端串——上游会反解
78
+ // 低 12 位校验 counter,形状不符 403 FreeTierError(见 opencode-identity.js 头注)。
79
+ function resolveSession(id) {
80
+ const raw = String(id || "").trim();
81
+ if (/^ses_/.test(raw)) return raw;
82
+ if (raw) {
83
+ try {
84
+ return `ses_${digestIdTail(crypto.createHash("sha1").update(raw).digest())}`;
85
+ } catch {
86
+ return genId("ses_");
87
+ }
88
+ }
89
+ return genId("ses_");
90
+ }
91
+
75
92
  function buildHeaders(opts = {}) {
76
93
  return {
77
94
  "Content-Type": "application/json",
78
95
  "Accept": "text/event-stream",
79
96
  "User-Agent": opencodeUa(),
80
97
  "x-opencode-client": "desktop",
81
- // session 复用调用方给的会话 id(同会话粘同一上游会话),没有才现生成
82
- "x-opencode-session": opts.sessionId ? `ses_${String(opts.sessionId).slice(0, 24)}` : genId("ses_"),
98
+ "x-opencode-session": resolveSession(opts.sessionId),
83
99
  "x-opencode-request": genId("msg_"),
84
100
  "x-opencode-project": "global",
85
- ...(ring.size ? {} : {}),
86
101
  };
87
102
  }
88
103