mslxdff 0.1.129 → 0.1.130

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.129",
3
+ "version": "0.1.130",
4
4
  "description": "测试项目,请勿使用。",
5
5
  "type": "module",
6
6
  "bin": {
package/src/chat/tools.js CHANGED
@@ -7,6 +7,7 @@ import { FORBIDDEN } from "./config.js";
7
7
  import { logDir } from "../logs.js";
8
8
  import { defaultStateFile, loadProviderKeys, loadProviderConfigs } from "../state.js";
9
9
  import { compatFetch } from "../compat.js";
10
+ import { opencodeClientIdentity } from "../opencode-identity.js";
10
11
 
11
12
  const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
12
13
  const allowedRoots = [
@@ -220,12 +221,17 @@ export async function curlTool({ url, method, headers, body, timeoutMs }) {
220
221
  if (headers && typeof headers === "object") {
221
222
  for (const [k, v] of Object.entries(headers)) if (typeof v === "string") h[String(k)] = v;
222
223
  }
223
- // 上游自动补最小可用头
224
+ // 上游自动补最小可用头(zen 免费层门禁:UA 带版本 + opencode 形状 session/request)
224
225
  if (/opencode\.ai/i.test(target)) {
226
+ const ident = opencodeClientIdentity();
225
227
  if (!h["x-opencode-client"]) h["x-opencode-client"] = "desktop";
226
228
  if (!h["Authorization"] && !h["authorization"]) h["Authorization"] = "Bearer public";
227
229
  if (!h["Accept"]) h["Accept"] = m === "GET" ? "*/*" : "application/json";
228
230
  if (!h["Content-Type"] && m !== "GET" && m !== "HEAD") h["Content-Type"] = "application/json";
231
+ if (!h["User-Agent"] && !h["user-agent"]) h["User-Agent"] = ident["User-Agent"];
232
+ if (!h["x-opencode-session"]) h["x-opencode-session"] = ident["x-opencode-session"];
233
+ if (!h["x-opencode-request"]) h["x-opencode-request"] = ident["x-opencode-request"];
234
+ if (!h["x-opencode-project"]) h["x-opencode-project"] = "global";
229
235
  }
230
236
  // 本机自动带 token(/v1/* 需要鉴权);/v1/models 必须是 GET,模型常误用 POST 直接纠正
231
237
  if (/127\.0\.0\.1|localhost/i.test(target) && /\/v1\//i.test(target)) {
@@ -5,6 +5,7 @@ import { runOne } from "../../../bench/runner.js";
5
5
  import { formatViaReport } from "../../../bench/report.js";
6
6
  import { clineBenchOne } from "../../../bench/cline-bench.js";
7
7
  import { workbuddyBenchOne } from "../../../bench/workbuddy-bench.js";
8
+ import { opencodeClientIdentity } from "../../../opencode-identity.js";
8
9
 
9
10
  export function buildHeadersForProvider(providerId, apiKey, auth) {
10
11
  const h = {};
@@ -21,6 +22,16 @@ export function buildHeadersForProvider(providerId, apiKey, auth) {
21
22
  if (auth?.enterpriseId) { h["X-Enterprise-Id"] = auth.enterpriseId; h["X-Tenant-Id"] = auth.enterpriseId; }
22
23
  return h;
23
24
  }
25
+ // opencode 免费层门禁:UA 带版本 + opencode 形状 session/request,否则 403
26
+ if (String(providerId).toLowerCase() === "opencode") {
27
+ const ident = opencodeClientIdentity();
28
+ h["Content-Type"] = "application/json";
29
+ if (!h["User-Agent"]) h["User-Agent"] = ident["User-Agent"];
30
+ h["x-opencode-client"] = "desktop";
31
+ h["x-opencode-session"] = ident["x-opencode-session"];
32
+ h["x-opencode-request"] = ident["x-opencode-request"];
33
+ h["x-opencode-project"] = "global";
34
+ }
24
35
  if (apiKey) h["Authorization"] = `Bearer ${apiKey}`;
25
36
  return h;
26
37
  }
@@ -0,0 +1,51 @@
1
+ import crypto from "node:crypto";
2
+
3
+ // opencode 客户端身份规格单一来源(源码 packages/schema/src/identifier.ts +
4
+ // packages/opencode/src/session/llm/request.ts):id = <prefix>_ + 12 位 hex
5
+ // (timestamp*4096+同毫秒计数,截 48bit) + 14 位 base62,共 26 字符;UA 必须 "opencode/<semver>"。
6
+ // zen 免费层 2026-09-17 起按该规格放行(缺版本 UA 或 id 形状不符 → 403 FreeTierError)。
7
+ const ID_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
8
+ const DEFAULT_OPENCODE_UA = "opencode/1.17.20";
9
+ let idLastTs = 0;
10
+ let idCounter = 0;
11
+
12
+ export function opencodeIdTail(timestamp = Date.now()) {
13
+ if (timestamp !== idLastTs) {
14
+ idLastTs = timestamp;
15
+ idCounter = 0;
16
+ }
17
+ idCounter += 1;
18
+ const value = BigInt(timestamp) * 0x1000n + BigInt(idCounter);
19
+ let tail = "";
20
+ for (let i = 0; i < 6; i++) {
21
+ tail += Number((value >> BigInt(40 - 8 * i)) & 0xffn)
22
+ .toString(16)
23
+ .padStart(2, "0");
24
+ }
25
+ for (const b of crypto.randomBytes(14)) tail += ID_CHARS[b % 62];
26
+ return tail;
27
+ }
28
+
29
+ // 摘要派生:同种子恒同 id(会话亲和),形态仍合规
30
+ export function digestIdTail(digest) {
31
+ let tail = digest.subarray(0, 6).toString("hex");
32
+ for (const b of digest.subarray(6, 20)) tail += ID_CHARS[b % 62];
33
+ return tail;
34
+ }
35
+
36
+ export function genId(prefix) {
37
+ return `${prefix}${opencodeIdTail()}`;
38
+ }
39
+
40
+ export function opencodeUa() {
41
+ return process.env.MSLXDFF_OPENCODE_UA || DEFAULT_OPENCODE_UA;
42
+ }
43
+
44
+ // 旁路(-chat curl 工具 / bench 直连)共用同一身份,避免各自伪造头再被 403
45
+ export function opencodeClientIdentity() {
46
+ return {
47
+ "User-Agent": opencodeUa(),
48
+ "x-opencode-session": genId("ses_"),
49
+ "x-opencode-request": genId("msg_"),
50
+ };
51
+ }
package/src/upstream.js CHANGED
@@ -7,11 +7,9 @@ import { isFreeModel } from "./models.js";
7
7
  import { fmtShanghaiYMDHMS } from "./time.js";
8
8
  import { createTransport } from "./transport/index.js";
9
9
  import { isResponsesModel, chatToResponsesBody, toChatResponse, reshapeResponsesSse } from "./upstream-responses.js";
10
- import { uuid } from "./compat.js";
10
+ import { digestIdTail, genId, opencodeClientIdentity, opencodeUa } from "./opencode-identity.js";
11
11
 
12
- function genId(prefix) {
13
- return `${prefix}${uuid().replace(/-/g, "")}`;
14
- }
12
+ export { opencodeClientIdentity, opencodeUa };
15
13
  // 上游按 session 做粘性路由(实测:固定 session 两次请求均 ~1.2s;每次随机时可能撞冷机器 26s+)。
16
14
  // 客户端(opencode AI SDK 路径)不带会话标识 → 用对话首两条消息(system + 首条 user)哈希做稳定会话:
17
15
  // 同一会话多轮里这两条不变 ⇒ 路由亲和稳定;不同会话天然分散。
@@ -25,7 +23,7 @@ function sessionFromMessages(messages) {
25
23
  };
26
24
  const seed = `${pick("system")}|${pick("user")}`.slice(0, 4000);
27
25
  if (seed === "|") return null;
28
- return `ses_${crypto.createHash("sha1").update(seed).digest("hex").slice(0, 32)}`;
26
+ return `ses_${digestIdTail(crypto.createHash("sha1").update(seed).digest())}`;
29
27
  } catch {
30
28
  return null;
31
29
  }
@@ -56,7 +54,7 @@ export function createOpencodeHeaderBuilder({ authToken = "public", env = proces
56
54
  "Content-Type": "application/json",
57
55
  Authorization: "",
58
56
  "x-opencode-client": "desktop",
59
- "User-Agent": "opencode",
57
+ "User-Agent": opencodeUa(),
60
58
  "HTTP-Referer": "https://hermes-agent.nousresearch.com",
61
59
  "X-Title": "Hermes Agent",
62
60
  }
@@ -73,7 +71,7 @@ export function createOpencodeHeaderBuilder({ authToken = "public", env = proces
73
71
  const base = {
74
72
  ...baseHeaders,
75
73
  Accept: isStream ? "text/event-stream" : "*/*",
76
- "User-Agent": "opencode",
74
+ "User-Agent": opencodeUa(),
77
75
  "x-opencode-session": session,
78
76
  "x-opencode-request": genId("msg_"),
79
77
  "x-opencode-project": "global",