mslxdff 0.1.200 → 0.1.203
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/README.md +3 -3
- package/bin/mslxdff.js +1 -1
- package/package.json +38 -38
- package/plugins/README.md +14 -14
- package/plugins/prefer-model.mjs.example +14 -14
- package/src/anthropic/count-tokens.js +61 -61
- package/src/anthropic/request-body.js +126 -126
- package/src/anthropic/shared.js +18 -18
- package/src/anthropic/sse-aggregate.js +56 -56
- package/src/anthropic/translate.js +287 -287
- package/src/anthropic/web-search.js +361 -361
- package/src/api-kind.js +31 -31
- package/src/autostart.js +288 -288
- package/src/bench/probe.js +1 -1
- package/src/bench/runner.js +1 -1
- package/src/bench/via-probe.js +1 -1
- package/src/bench/via.js +144 -144
- package/src/bench/workbuddy-bench.js +1 -1
- package/src/chat/config.js +11 -11
- package/src/chat/cooling.js +81 -81
- package/src/chat/direct.js +58 -58
- package/src/chat/index.js +6 -6
- package/src/chat/spinner.js +38 -38
- package/src/chat/sse.js +69 -69
- package/src/chat/stats.js +247 -247
- package/src/chat/store.js +48 -48
- package/src/chat/tool-handlers.js +67 -67
- package/src/chat/tools.js +1 -1
- package/src/chat/upstream.js +59 -59
- package/src/chat-pipeline/auto-race.js +158 -158
- package/src/chat-pipeline/empty-turn.js +147 -147
- package/src/chat-pipeline/engine.js +17 -17
- package/src/chat-pipeline/serial-trial.js +283 -283
- package/src/chat-pipeline/terminal-verdict.js +147 -147
- package/src/chooser.js +54 -54
- package/src/claude-desktop/apply.js +101 -101
- package/src/claude-desktop/context1m.js +38 -38
- package/src/claude-desktop/doctor.js +146 -146
- package/src/claude-desktop/slots.js +152 -152
- package/src/cli/bootstrap.js +1 -1
- package/src/cli/commands/claude-desktop.js +284 -284
- package/src/cli/commands/daemon.js +231 -231
- package/src/cli/commands/model/list-live.js +22 -22
- package/src/cli/commands/model/list-providers.js +76 -76
- package/src/cli/commands/model/list-render.js +77 -77
- package/src/cli/commands/model/list-sort.js +52 -52
- package/src/cli/commands/model/list-tty.js +76 -76
- package/src/cli/commands/model/list.js +166 -166
- package/src/cli/commands/model/live-models.js +24 -24
- package/src/cli/commands/model/stats.js +43 -43
- package/src/cli/commands/model/status.js +47 -47
- package/src/cli/commands/model.js +51 -51
- package/src/cli/commands/provider/add.js +108 -108
- package/src/cli/commands/provider/allowlist.js +99 -99
- package/src/cli/commands/provider/cline-free.js +99 -99
- package/src/cli/commands/provider/cline-login.js +218 -218
- package/src/cli/commands/provider/cline-quota.js +65 -65
- package/src/cli/commands/provider/codearts-login.js +45 -45
- package/src/cli/commands/provider/config.js +119 -119
- package/src/cli/commands/provider/globalqwenwork-login.js +126 -126
- package/src/cli/commands/provider/keys-guard.js +50 -50
- package/src/cli/commands/provider/keys.js +161 -161
- package/src/cli/commands/provider/qoder-checkin.js +81 -81
- package/src/cli/commands/provider/qoder-login.js +102 -102
- package/src/cli/commands/provider/raccoon-login.js +180 -180
- package/src/cli/commands/provider/raccoon-password.js +162 -162
- package/src/cli/commands/provider/raccoon-quota.js +109 -109
- package/src/cli/commands/provider/traework-checkin.js +144 -144
- package/src/cli/commands/provider/traework-login.js +164 -164
- package/src/cli/commands/provider/workbuddy-login.js +153 -153
- package/src/cli/commands/provider/zcode-login.js +77 -77
- package/src/cli/commands/provider/zcode-quota.js +55 -55
- package/src/cli/commands/provider.js +1 -1
- package/src/cli/commands/stats.js +149 -149
- package/src/cli/commands/system.js +261 -261
- package/src/cli/commands/timezone.js +47 -47
- package/src/cli/commands/use-group.js +60 -60
- package/src/cli/commands/workbuddy.js +189 -189
- package/src/cli/format.js +141 -141
- package/src/cli/group-helpers.js +70 -70
- package/src/cli/help.js +80 -80
- package/src/cli/index.js +70 -70
- package/src/cli/interactive.js +121 -121
- package/src/cli/join-core.js +99 -99
- package/src/cli/join-wizard.js +175 -175
- package/src/cli/policy.js +136 -136
- package/src/cli/provider-row.js +126 -126
- package/src/cli/util.js +24 -24
- package/src/compat.js +53 -53
- package/src/events.js +38 -38
- package/src/free-lane.js +131 -131
- package/src/free-watcher.js +63 -63
- package/src/groups.js +262 -262
- package/src/logs.js +195 -195
- package/src/metrics.js +62 -62
- package/src/model-capabilities/context-lookup.js +65 -65
- package/src/model-capabilities/merge.js +142 -142
- package/src/model-trace.js +187 -187
- package/src/models.js +1 -1
- package/src/opencode-identity.js +72 -72
- package/src/peers.js +254 -251
- package/src/plugins.js +107 -107
- package/src/providers/base.js +164 -164
- package/src/providers/classify.js +22 -22
- package/src/providers/cline/allowlist-sync.js +93 -93
- package/src/providers/cline/free-catalog.js +71 -71
- package/src/providers/cline/headers.js +37 -37
- package/src/providers/cline/index.js +130 -130
- package/src/providers/cline/models.js +150 -150
- package/src/providers/cline/usage.js +274 -274
- package/src/providers/codearts/auth-pool.js +148 -148
- package/src/providers/codearts/concurrency.js +34 -34
- package/src/providers/codearts/const.js +65 -65
- package/src/providers/codearts/dpop.js +95 -95
- package/src/providers/codearts/index.js +175 -175
- package/src/providers/codearts/login.js +214 -214
- package/src/providers/codearts/models.js +182 -182
- package/src/providers/codearts/sign.js +96 -96
- package/src/providers/codearts/sse.js +204 -204
- package/src/providers/codearts/stream.js +209 -209
- package/src/providers/codearts/sts.js +124 -124
- package/src/providers/codearts.js +1 -1
- package/src/providers/dispatcher.js +146 -146
- package/src/providers/generic.js +156 -156
- package/src/providers/globalqwenwork/account-store.js +141 -141
- package/src/providers/globalqwenwork/constants.js +73 -73
- package/src/providers/globalqwenwork/crypto.js +2 -2
- package/src/providers/globalqwenwork/http.js +2 -2
- package/src/providers/globalqwenwork/index.js +331 -331
- package/src/providers/globalqwenwork/payload.js +147 -147
- package/src/providers/globalqwenwork/rsa.js +2 -2
- package/src/providers/globalqwenwork/sse.js +2 -2
- package/src/providers/globalqwenwork/stream.js +2 -2
- package/src/providers/globalqwenwork.js +1 -1
- package/src/providers/globalworkbuddy/account-store.js +198 -198
- package/src/providers/globalworkbuddy/auth.js +97 -97
- package/src/providers/globalworkbuddy/catalog.js +138 -138
- package/src/providers/globalworkbuddy/constants.js +73 -73
- package/src/providers/globalworkbuddy/device-auth.js +120 -120
- package/src/providers/globalworkbuddy/errors.js +98 -98
- package/src/providers/globalworkbuddy/headers.js +89 -89
- package/src/providers/globalworkbuddy/models.js +96 -96
- package/src/providers/globalworkbuddy/payload.js +80 -80
- package/src/providers/index.js +5 -5
- package/src/providers/model-id.js +172 -172
- package/src/providers/opencode.js +19 -19
- package/src/providers/openrouter.js +31 -31
- package/src/providers/qoder/account-store.js +106 -106
- package/src/providers/qoder/aggregate.js +60 -60
- package/src/providers/qoder/baseprompt.json +591 -591
- package/src/providers/qoder/checkin.js +160 -160
- package/src/providers/qoder/constants.js +44 -44
- package/src/providers/qoder/encode.js +41 -41
- package/src/providers/qoder/fingerprint.js +38 -38
- package/src/providers/qoder/models.js +77 -77
- package/src/providers/qoder/oauth.js +84 -84
- package/src/providers/qoder/payload.js +162 -162
- package/src/providers/qoder/request.js +27 -27
- package/src/providers/qoder/session.js +134 -134
- package/src/providers/qoder/sticky.js +38 -38
- package/src/providers/qoder/stream.js +116 -116
- package/src/providers/qoder.js +1 -1
- package/src/providers/raccoon/account-store.js +216 -216
- package/src/providers/raccoon/auth.js +67 -67
- package/src/providers/raccoon/chat.js +228 -228
- package/src/providers/raccoon/const.js +180 -165
- package/src/providers/raccoon/credits.js +185 -175
- package/src/providers/raccoon/crypto.js +43 -0
- package/src/providers/raccoon/envelope.js +31 -31
- package/src/providers/raccoon/headers.js +45 -45
- package/src/providers/raccoon/index.js +216 -216
- package/src/providers/raccoon/login.js +279 -279
- package/src/providers/raccoon/models.js +160 -160
- package/src/providers/raccoon/password-login.js +102 -87
- package/src/providers/raccoon/qr-codewords.js +115 -115
- package/src/providers/raccoon/qr.js +236 -236
- package/src/providers/raccoon/sse.js +135 -135
- package/src/providers/responses-channel.js +120 -120
- package/src/providers/share-keys.js +53 -53
- package/src/providers/traework/account-store.js +234 -234
- package/src/providers/traework/chat.js +217 -217
- package/src/providers/traework/checkin.js +77 -77
- package/src/providers/traework/constants.js +41 -41
- package/src/providers/traework/errors.js +52 -52
- package/src/providers/traework/headers.js +79 -79
- package/src/providers/traework/index.js +132 -132
- package/src/providers/traework/market-identity.js +116 -116
- package/src/providers/traework/models.js +98 -98
- package/src/providers/traework/payload.js +86 -86
- package/src/providers/traework/sse.js +184 -184
- package/src/providers/traework/token.js +52 -52
- package/src/providers/traework.js +2 -2
- package/src/providers/workbuddy/account-store.js +186 -186
- package/src/providers/workbuddy/auth.js +158 -158
- package/src/providers/workbuddy/balance.js +86 -86
- package/src/providers/workbuddy/cat-travel.js +125 -125
- package/src/providers/workbuddy/checkin.js +75 -75
- package/src/providers/workbuddy/desktop-info.js +78 -78
- package/src/providers/workbuddy/growth-api.js +67 -67
- package/src/providers/workbuddy/growth-plans.js +63 -63
- package/src/providers/workbuddy/growth.js +181 -181
- package/src/providers/workbuddy/index.js +217 -217
- package/src/providers/workbuddy/models.js +126 -126
- package/src/providers/workbuddy/payload.js +141 -141
- package/src/providers/workbuddy/rotation-log.js +83 -83
- package/src/providers/workbuddy/sanitize-tools.js +61 -61
- package/src/providers/workbuddy/sdk-chat.js +26 -26
- package/src/providers/workbuddy-balance.js +78 -78
- package/src/providers/zcode/account-store.js +129 -129
- package/src/providers/zcode/auth.js +28 -28
- package/src/providers/zcode/chat.js +179 -179
- package/src/providers/zcode/const.js +54 -54
- package/src/providers/zcode/context-shape.js +162 -162
- package/src/providers/zcode/headers.js +93 -93
- package/src/providers/zcode/index.js +124 -124
- package/src/providers/zcode/models.js +65 -65
- package/src/providers/zcode/oauth.js +120 -120
- package/src/providers/zcode/quota.js +176 -176
- package/src/providers/zcode/sse.js +196 -196
- package/src/readline-compat.js +25 -25
- package/src/responses/translate.js +311 -311
- package/src/routes/chat/broadband-handler.js +72 -72
- package/src/routes/chat/empty-turn-report.js +31 -31
- package/src/routes/chat/exhausted-handler.js +87 -87
- package/src/routes/chat/hedge-handler.js +130 -130
- package/src/routes/chat/index.js +1 -1
- package/src/routes/chat/local-handler.js +63 -63
- package/src/routes/chat/peer-handler.js +81 -81
- package/src/routes/chat.js +3 -3
- package/src/routes/fallback.js +88 -88
- package/src/routes/groups-relay.js +214 -214
- package/src/routes/groups.js +122 -122
- package/src/routes/helpers.js +83 -83
- package/src/routes/index.js +147 -147
- package/src/routes/messages-route.js +365 -365
- package/src/routes/peers.js +23 -1
- package/src/routes/relay-queue.js +181 -181
- package/src/routes/relay.js +60 -60
- package/src/routes/responses-route.js +213 -213
- package/src/routes/stream-body.js +84 -84
- package/src/routes/stream-hold.js +337 -337
- package/src/routes/stream-scan.js +366 -366
- package/src/routes.js +3 -3
- package/src/runtime/auto-update.js +113 -113
- package/src/runtime/bootstrap.js +8 -0
- package/src/runtime/broadband-stream.js +79 -79
- package/src/runtime/broadband.js +103 -103
- package/src/runtime/group-sync.js +81 -81
- package/src/runtime/keep-warm.js +120 -0
- package/src/runtime/lifecycle-forensics.js +116 -116
- package/src/runtime/lifecycle-log.js +87 -87
- package/src/runtime/log-stamp.js +73 -73
- package/src/runtime/proc-info.js +137 -137
- package/src/runtime/qoder-checkin.js +93 -93
- package/src/runtime/raccoon-checkin.js +126 -126
- package/src/runtime/server-lifecycle.js +189 -189
- package/src/runtime/traework-checkin.js +118 -118
- package/src/runtime/workbuddy-checkin.js +120 -120
- package/src/runtime/workbuddy-growth.js +133 -133
- package/src/server.js +7 -0
- package/src/state/facade.js +58 -58
- package/src/state/memory.js +162 -162
- package/src/state/merge.js +25 -25
- package/src/state/migrations/cline-unify.js +94 -94
- package/src/state/migrations.js +33 -33
- package/src/state/persist.js +42 -42
- package/src/state/provider-config.js +142 -142
- package/src/state/schemas/group.js +31 -31
- package/src/state/schemas/model.js +94 -94
- package/src/state/schemas/peer.js +31 -31
- package/src/state/schemas/port.js +10 -10
- package/src/state/schemas/provider.js +202 -202
- package/src/state/schemas/timezone.js +70 -70
- package/src/state/schemas/token.js +48 -48
- package/src/state/schemas/use-group.js +102 -102
- package/src/state/store.js +176 -176
- package/src/state.js +1 -1
- package/src/sync-claude-desktop.js +234 -234
- package/src/sync-claude.js +152 -152
- package/src/sync-codex.js +116 -116
- package/src/sync-workbuddy.js +158 -158
- package/src/talk-full-redact.js +129 -129
- package/src/talk-full.js +131 -131
- package/src/talk-log.js +257 -257
- package/src/text-tokens.js +52 -52
- package/src/thinking-effort.js +67 -67
- package/src/time.js +106 -106
- package/src/timeline.js +26 -26
- package/src/transport/index.js +1 -1
- package/src/upstream-engine/index.js +74 -74
- package/src/upstream-engine/mode.js +15 -15
- package/src/upstream-engine/sdk/attempt.js +221 -221
- package/src/upstream-engine/sdk/chat.js +28 -28
- package/src/upstream-engine/sdk/convert.js +192 -192
- package/src/upstream-engine/sdk/diagnose.js +62 -62
- package/src/upstream-engine/sdk/dispatch.js +55 -55
- package/src/upstream-engine/sdk/responses.js +176 -176
- package/src/upstream-engine/sdk/sse.js +166 -166
- package/src/upstream-responses.js +305 -305
- package/src/usage/record.js +106 -106
- package/src/usage/report.js +195 -195
- package/src/v6pool/claim.js +157 -157
- package/src/v6pool/config.js +65 -65
- package/src/v6pool/detect.js +130 -130
- package/src/v6pool/index.js +152 -152
- package/src/v6pool/prefix.js +140 -140
- package/src/v6pool/probe.js +40 -40
- package/src/v6pool/report.js +104 -104
- package/src/v6pool/rotate.js +263 -263
- package/src/warp-pool/config.js +74 -74
- package/src/warp-pool/health.js +106 -106
- package/src/warp-pool/identity.js +141 -141
- package/src/warp-pool/index.js +358 -358
- package/src/warp-pool/keys.js +29 -29
- package/src/warp-pool/nodes.js +95 -95
- package/src/warp-pool/ports.js +160 -160
- package/src/warp-pool/register.js +113 -113
- package/src/warp-pool/report.js +232 -232
- package/src/warp-pool/revive.js +168 -168
- package/src/warp-pool/rotate.js +214 -214
- package/src/warp-pool/strategy.js +43 -43
- package/src/warp-pool/verdict.js +69 -69
- package/src/warp-pool/wireproxy.js +162 -162
- package/docs/cli_help_mini.md +0 -146
- package/src/providers/AGENTS.md +0 -49
- package/src/routes/AGENTS.md +0 -37
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
// qoder 签到/额度(移植 qoder2api checkin.go,修两处硬伤):
|
|
2
|
-
// ① 域名按区域选(Go 硬编码 openapi.qoder.com.cn → 国际号必然 401 TOKEN_EXPIRE);
|
|
3
|
-
// ② 活动类型放宽(Go 只认 CLAIM_BENEFIT,国际站活动是 VIEW_DETAILS 会被漏掉)。
|
|
4
|
-
// 认证只需 Bearer deviceToken + cosy-clienttype:10,无 COSY 签名、claim 空 body。
|
|
5
|
-
// 主流程:cn 走 daily-check-in(GET status → POST claim,409=今日已领);
|
|
6
|
-
// 404/非 200 → 回落 campaigns(GET /sash/api/v1/me/campaigns → POST .../{id}/claim)。
|
|
7
|
-
// Note: 域名必须按每号 region 选(两站账号不通用),默认只领 CLAIM_BENEFIT — 见 .agents/notes/implemented/feature/2026-09-21-qoder-checkin-region-endpoints.md
|
|
8
|
-
import { EP_CAMPAIGNS, EP_CHECKIN_STATUS, EP_CHECKIN_CLAIM, EP_QUOTA, openapiBase, normalizeRegion } from "./constants.js";
|
|
9
|
-
import { compatFetch, timeoutSignal } from "../../compat.js";
|
|
10
|
-
|
|
11
|
-
export function checkinHeaders(deviceToken) {
|
|
12
|
-
return {
|
|
13
|
-
authorization: "Bearer " + String(deviceToken || ""),
|
|
14
|
-
accept: "application/json",
|
|
15
|
-
"accept-language": "zh-CN",
|
|
16
|
-
"user-agent": "Qoder",
|
|
17
|
-
"cosy-clienttype": "10",
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async function call(fetchImpl, region, path, { method = "GET", deviceToken, body, timeoutMs = 20000 } = {}) {
|
|
22
|
-
const base = openapiBase(region);
|
|
23
|
-
const headers = checkinHeaders(deviceToken);
|
|
24
|
-
const init = { method, headers, signal: timeoutSignal(timeoutMs) };
|
|
25
|
-
if (method !== "GET") {
|
|
26
|
-
headers.origin = base;
|
|
27
|
-
if (body !== undefined) init.body = JSON.stringify(body);
|
|
28
|
-
}
|
|
29
|
-
let res;
|
|
30
|
-
try {
|
|
31
|
-
res = await fetchImpl(base + path, init);
|
|
32
|
-
} catch (e) {
|
|
33
|
-
return { status: 0, json: null, text: "", error: String(e?.message || e).slice(0, 200) };
|
|
34
|
-
}
|
|
35
|
-
const text = await res.text().catch(() => "");
|
|
36
|
-
let json = null;
|
|
37
|
-
if (text) { try { json = JSON.parse(text); } catch {} }
|
|
38
|
-
return { status: res.status, json, text };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function listCampaigns({ deviceToken, region, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
42
|
-
return call(fetchImpl, region, EP_CAMPAIGNS, { deviceToken, timeoutMs });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function claimCampaign({ deviceToken, region, campaignId, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
46
|
-
return call(fetchImpl, region, `${EP_CAMPAIGNS}/${campaignId}/claim`, { method: "POST", deviceToken, timeoutMs });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function dailyCheckinStatus({ deviceToken, region, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
50
|
-
return call(fetchImpl, region, EP_CHECKIN_STATUS, { deviceToken, timeoutMs });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function dailyCheckinClaim({ deviceToken, region, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
54
|
-
return call(fetchImpl, region, EP_CHECKIN_CLAIM, { method: "POST", deviceToken, body: {}, timeoutMs });
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 余额:/api/v2/quota/usage → userQuota{total,used,remaining,unit} + isQuotaExceeded
|
|
58
|
-
export async function fetchQuota({ deviceToken, region, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
59
|
-
const r = await call(fetchImpl, region, EP_QUOTA, { deviceToken, timeoutMs });
|
|
60
|
-
if (r.status !== 200) return { ok: false, status: r.status, error: r.error || r.text.slice(0, 160) };
|
|
61
|
-
const q = r.json?.userQuota || {};
|
|
62
|
-
return {
|
|
63
|
-
ok: true,
|
|
64
|
-
total: Number(q.total) || 0,
|
|
65
|
-
used: Number(q.used) || 0,
|
|
66
|
-
remaining: Number(q.remaining) || 0,
|
|
67
|
-
unit: String(q.unit || "credits"),
|
|
68
|
-
exhausted: Boolean(r.json?.isQuotaExceeded),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// 选活动:优先 CLAIM_BENEFIT(真积分),allowPromo 时也认 VIEW_DETAILS 等促销条目。
|
|
73
|
-
export function pickCampaign(campaigns, { allowPromo = false } = {}) {
|
|
74
|
-
const list = Array.isArray(campaigns) ? campaigns : [];
|
|
75
|
-
let benefit = null;
|
|
76
|
-
let promo = null;
|
|
77
|
-
let claimed = false;
|
|
78
|
-
for (const c of list) {
|
|
79
|
-
if (c?.claimStatus === "CLAIMED") claimed = true;
|
|
80
|
-
if (c?.claimStatus !== "CLAIMABLE") continue;
|
|
81
|
-
if (c?.actionType === "CLAIM_BENEFIT") benefit = benefit || c;
|
|
82
|
-
else if (allowPromo) promo = promo || c;
|
|
83
|
-
}
|
|
84
|
-
return { target: benefit || promo, claimed };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 单账号签到:返回 { ok, status: claimed|already|no_campaign|error, amount, streak, ... }
|
|
88
|
-
export async function runCheckin({ deviceToken, region, fetchImpl = compatFetch, allowPromo = false, probeOnly = false, timeoutMs } = {}) {
|
|
89
|
-
const reg = normalizeRegion(region);
|
|
90
|
-
const r = {
|
|
91
|
-
region: reg, ok: false, status: "error", message: "", amount: 0,
|
|
92
|
-
streak: 0, totalDays: 0, totalCredits: 0, reward: 0,
|
|
93
|
-
};
|
|
94
|
-
if (!deviceToken) { r.message = "无 device token(先 mslxdff -provider qoder login)"; return r; }
|
|
95
|
-
|
|
96
|
-
const st = await dailyCheckinStatus({ deviceToken, region: reg, fetchImpl, timeoutMs });
|
|
97
|
-
if (st.status === 200 && st.json?.status) {
|
|
98
|
-
r.streak = Number(st.json.currentStreakDays) || 0;
|
|
99
|
-
r.totalDays = Number(st.json.totalClaimDays) || 0;
|
|
100
|
-
r.totalCredits = Number(st.json.totalRewardCredits) || 0;
|
|
101
|
-
r.reward = Number(st.json.rewardCredits) || 0;
|
|
102
|
-
}
|
|
103
|
-
if (st.status === 401) { r.message = `device token 失效(HTTP 401):请重新 mslxdff -provider qoder login`; return r; }
|
|
104
|
-
|
|
105
|
-
// 干跑:只报状态,不发任何 claim 请求
|
|
106
|
-
if (probeOnly && st.status === 200 && st.json?.status) {
|
|
107
|
-
r.ok = true;
|
|
108
|
-
r.status = st.json.status === "CLAIMED" ? "already" : "claimable";
|
|
109
|
-
r.message = `干跑:status=${st.json.status} 每日奖励 ${r.reward}`;
|
|
110
|
-
return r;
|
|
111
|
-
}
|
|
112
|
-
// 主流程:daily-check-in(国内站)
|
|
113
|
-
if (st.status === 200 && st.json?.status) {
|
|
114
|
-
const claim = await dailyCheckinClaim({ deviceToken, region: reg, fetchImpl, timeoutMs });
|
|
115
|
-
if (claim.status === 409) { r.ok = true; r.status = "already"; r.message = "今日已领取"; return r; }
|
|
116
|
-
if (claim.status === 200) {
|
|
117
|
-
r.ok = true; r.status = "claimed";
|
|
118
|
-
r.amount = Number(claim.json?.rewardCredits) || r.reward || 0;
|
|
119
|
-
r.streak += 1; r.totalDays += 1; r.totalCredits += r.amount;
|
|
120
|
-
r.message = `签到成功 +${r.amount}`;
|
|
121
|
-
return r;
|
|
122
|
-
}
|
|
123
|
-
// 其它错误 → 保留统计,回落 campaigns
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const list = await listCampaigns({ deviceToken, region: reg, fetchImpl, timeoutMs });
|
|
127
|
-
if (list.status === 401) { r.message = "device token 失效(HTTP 401):请重新 mslxdff -provider qoder login"; return r; }
|
|
128
|
-
if (list.status !== 200) {
|
|
129
|
-
r.message = `查询活动失败 HTTP ${list.status || "-"}: ${String(list.error || list.text).slice(0, 160)}`;
|
|
130
|
-
return r;
|
|
131
|
-
}
|
|
132
|
-
const { target, claimed } = pickCampaign(list.json?.campaigns, { allowPromo });
|
|
133
|
-
if (probeOnly) {
|
|
134
|
-
r.ok = true; r.status = target ? "claimable" : "no_campaign";
|
|
135
|
-
r.message = target
|
|
136
|
-
? `干跑:可领 ${target.actionType} ${target.campaignKey || ""}`.trim()
|
|
137
|
-
: (claimed ? "干跑:今日已领取" : "干跑:无可用签到活动");
|
|
138
|
-
return r;
|
|
139
|
-
}
|
|
140
|
-
if (!target) {
|
|
141
|
-
r.ok = true; r.status = "no_campaign";
|
|
142
|
-
r.message = claimed ? "今日已领取" : "无可用签到活动";
|
|
143
|
-
return r;
|
|
144
|
-
}
|
|
145
|
-
const claim = await claimCampaign({ deviceToken, region: reg, campaignId: target.campaignId, fetchImpl, timeoutMs });
|
|
146
|
-
if (claim.status !== 200 || claim.json?.status !== "CLAIMED") {
|
|
147
|
-
r.message = `领取失败 HTTP ${claim.status || "-"}: ${String(claim.error || claim.text).slice(0, 160)}`;
|
|
148
|
-
return r;
|
|
149
|
-
}
|
|
150
|
-
r.ok = true;
|
|
151
|
-
r.amount = Number(claim.json?.benefit?.amount) || 0;
|
|
152
|
-
r.expiresAt = String(claim.json?.expiresAt || "");
|
|
153
|
-
r.campaignKey = String(target.campaignKey || "");
|
|
154
|
-
if (claim.json?.replayed) { r.status = "already"; r.message = "今日已领取(幂等返回)"; }
|
|
155
|
-
else {
|
|
156
|
-
r.status = "claimed";
|
|
157
|
-
r.message = r.amount > 0 ? `领取成功 +${r.amount} ${r.campaignKey}`.trim() : `领取成功 ${r.campaignKey}`.trim();
|
|
158
|
-
}
|
|
159
|
-
return r;
|
|
160
|
-
}
|
|
1
|
+
// qoder 签到/额度(移植 qoder2api checkin.go,修两处硬伤):
|
|
2
|
+
// ① 域名按区域选(Go 硬编码 openapi.qoder.com.cn → 国际号必然 401 TOKEN_EXPIRE);
|
|
3
|
+
// ② 活动类型放宽(Go 只认 CLAIM_BENEFIT,国际站活动是 VIEW_DETAILS 会被漏掉)。
|
|
4
|
+
// 认证只需 Bearer deviceToken + cosy-clienttype:10,无 COSY 签名、claim 空 body。
|
|
5
|
+
// 主流程:cn 走 daily-check-in(GET status → POST claim,409=今日已领);
|
|
6
|
+
// 404/非 200 → 回落 campaigns(GET /sash/api/v1/me/campaigns → POST .../{id}/claim)。
|
|
7
|
+
// Note: 域名必须按每号 region 选(两站账号不通用),默认只领 CLAIM_BENEFIT — 见 .agents/notes/implemented/feature/2026-09-21-qoder-checkin-region-endpoints.md
|
|
8
|
+
import { EP_CAMPAIGNS, EP_CHECKIN_STATUS, EP_CHECKIN_CLAIM, EP_QUOTA, openapiBase, normalizeRegion } from "./constants.js";
|
|
9
|
+
import { compatFetch, timeoutSignal } from "../../compat.js";
|
|
10
|
+
|
|
11
|
+
export function checkinHeaders(deviceToken) {
|
|
12
|
+
return {
|
|
13
|
+
authorization: "Bearer " + String(deviceToken || ""),
|
|
14
|
+
accept: "application/json",
|
|
15
|
+
"accept-language": "zh-CN",
|
|
16
|
+
"user-agent": "Qoder",
|
|
17
|
+
"cosy-clienttype": "10",
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function call(fetchImpl, region, path, { method = "GET", deviceToken, body, timeoutMs = 20000 } = {}) {
|
|
22
|
+
const base = openapiBase(region);
|
|
23
|
+
const headers = checkinHeaders(deviceToken);
|
|
24
|
+
const init = { method, headers, signal: timeoutSignal(timeoutMs) };
|
|
25
|
+
if (method !== "GET") {
|
|
26
|
+
headers.origin = base;
|
|
27
|
+
if (body !== undefined) init.body = JSON.stringify(body);
|
|
28
|
+
}
|
|
29
|
+
let res;
|
|
30
|
+
try {
|
|
31
|
+
res = await fetchImpl(base + path, init);
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return { status: 0, json: null, text: "", error: String(e?.message || e).slice(0, 200) };
|
|
34
|
+
}
|
|
35
|
+
const text = await res.text().catch(() => "");
|
|
36
|
+
let json = null;
|
|
37
|
+
if (text) { try { json = JSON.parse(text); } catch {} }
|
|
38
|
+
return { status: res.status, json, text };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function listCampaigns({ deviceToken, region, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
42
|
+
return call(fetchImpl, region, EP_CAMPAIGNS, { deviceToken, timeoutMs });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function claimCampaign({ deviceToken, region, campaignId, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
46
|
+
return call(fetchImpl, region, `${EP_CAMPAIGNS}/${campaignId}/claim`, { method: "POST", deviceToken, timeoutMs });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function dailyCheckinStatus({ deviceToken, region, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
50
|
+
return call(fetchImpl, region, EP_CHECKIN_STATUS, { deviceToken, timeoutMs });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function dailyCheckinClaim({ deviceToken, region, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
54
|
+
return call(fetchImpl, region, EP_CHECKIN_CLAIM, { method: "POST", deviceToken, body: {}, timeoutMs });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 余额:/api/v2/quota/usage → userQuota{total,used,remaining,unit} + isQuotaExceeded
|
|
58
|
+
export async function fetchQuota({ deviceToken, region, fetchImpl = compatFetch, timeoutMs } = {}) {
|
|
59
|
+
const r = await call(fetchImpl, region, EP_QUOTA, { deviceToken, timeoutMs });
|
|
60
|
+
if (r.status !== 200) return { ok: false, status: r.status, error: r.error || r.text.slice(0, 160) };
|
|
61
|
+
const q = r.json?.userQuota || {};
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
total: Number(q.total) || 0,
|
|
65
|
+
used: Number(q.used) || 0,
|
|
66
|
+
remaining: Number(q.remaining) || 0,
|
|
67
|
+
unit: String(q.unit || "credits"),
|
|
68
|
+
exhausted: Boolean(r.json?.isQuotaExceeded),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 选活动:优先 CLAIM_BENEFIT(真积分),allowPromo 时也认 VIEW_DETAILS 等促销条目。
|
|
73
|
+
export function pickCampaign(campaigns, { allowPromo = false } = {}) {
|
|
74
|
+
const list = Array.isArray(campaigns) ? campaigns : [];
|
|
75
|
+
let benefit = null;
|
|
76
|
+
let promo = null;
|
|
77
|
+
let claimed = false;
|
|
78
|
+
for (const c of list) {
|
|
79
|
+
if (c?.claimStatus === "CLAIMED") claimed = true;
|
|
80
|
+
if (c?.claimStatus !== "CLAIMABLE") continue;
|
|
81
|
+
if (c?.actionType === "CLAIM_BENEFIT") benefit = benefit || c;
|
|
82
|
+
else if (allowPromo) promo = promo || c;
|
|
83
|
+
}
|
|
84
|
+
return { target: benefit || promo, claimed };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 单账号签到:返回 { ok, status: claimed|already|no_campaign|error, amount, streak, ... }
|
|
88
|
+
export async function runCheckin({ deviceToken, region, fetchImpl = compatFetch, allowPromo = false, probeOnly = false, timeoutMs } = {}) {
|
|
89
|
+
const reg = normalizeRegion(region);
|
|
90
|
+
const r = {
|
|
91
|
+
region: reg, ok: false, status: "error", message: "", amount: 0,
|
|
92
|
+
streak: 0, totalDays: 0, totalCredits: 0, reward: 0,
|
|
93
|
+
};
|
|
94
|
+
if (!deviceToken) { r.message = "无 device token(先 mslxdff -provider qoder login)"; return r; }
|
|
95
|
+
|
|
96
|
+
const st = await dailyCheckinStatus({ deviceToken, region: reg, fetchImpl, timeoutMs });
|
|
97
|
+
if (st.status === 200 && st.json?.status) {
|
|
98
|
+
r.streak = Number(st.json.currentStreakDays) || 0;
|
|
99
|
+
r.totalDays = Number(st.json.totalClaimDays) || 0;
|
|
100
|
+
r.totalCredits = Number(st.json.totalRewardCredits) || 0;
|
|
101
|
+
r.reward = Number(st.json.rewardCredits) || 0;
|
|
102
|
+
}
|
|
103
|
+
if (st.status === 401) { r.message = `device token 失效(HTTP 401):请重新 mslxdff -provider qoder login`; return r; }
|
|
104
|
+
|
|
105
|
+
// 干跑:只报状态,不发任何 claim 请求
|
|
106
|
+
if (probeOnly && st.status === 200 && st.json?.status) {
|
|
107
|
+
r.ok = true;
|
|
108
|
+
r.status = st.json.status === "CLAIMED" ? "already" : "claimable";
|
|
109
|
+
r.message = `干跑:status=${st.json.status} 每日奖励 ${r.reward}`;
|
|
110
|
+
return r;
|
|
111
|
+
}
|
|
112
|
+
// 主流程:daily-check-in(国内站)
|
|
113
|
+
if (st.status === 200 && st.json?.status) {
|
|
114
|
+
const claim = await dailyCheckinClaim({ deviceToken, region: reg, fetchImpl, timeoutMs });
|
|
115
|
+
if (claim.status === 409) { r.ok = true; r.status = "already"; r.message = "今日已领取"; return r; }
|
|
116
|
+
if (claim.status === 200) {
|
|
117
|
+
r.ok = true; r.status = "claimed";
|
|
118
|
+
r.amount = Number(claim.json?.rewardCredits) || r.reward || 0;
|
|
119
|
+
r.streak += 1; r.totalDays += 1; r.totalCredits += r.amount;
|
|
120
|
+
r.message = `签到成功 +${r.amount}`;
|
|
121
|
+
return r;
|
|
122
|
+
}
|
|
123
|
+
// 其它错误 → 保留统计,回落 campaigns
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const list = await listCampaigns({ deviceToken, region: reg, fetchImpl, timeoutMs });
|
|
127
|
+
if (list.status === 401) { r.message = "device token 失效(HTTP 401):请重新 mslxdff -provider qoder login"; return r; }
|
|
128
|
+
if (list.status !== 200) {
|
|
129
|
+
r.message = `查询活动失败 HTTP ${list.status || "-"}: ${String(list.error || list.text).slice(0, 160)}`;
|
|
130
|
+
return r;
|
|
131
|
+
}
|
|
132
|
+
const { target, claimed } = pickCampaign(list.json?.campaigns, { allowPromo });
|
|
133
|
+
if (probeOnly) {
|
|
134
|
+
r.ok = true; r.status = target ? "claimable" : "no_campaign";
|
|
135
|
+
r.message = target
|
|
136
|
+
? `干跑:可领 ${target.actionType} ${target.campaignKey || ""}`.trim()
|
|
137
|
+
: (claimed ? "干跑:今日已领取" : "干跑:无可用签到活动");
|
|
138
|
+
return r;
|
|
139
|
+
}
|
|
140
|
+
if (!target) {
|
|
141
|
+
r.ok = true; r.status = "no_campaign";
|
|
142
|
+
r.message = claimed ? "今日已领取" : "无可用签到活动";
|
|
143
|
+
return r;
|
|
144
|
+
}
|
|
145
|
+
const claim = await claimCampaign({ deviceToken, region: reg, campaignId: target.campaignId, fetchImpl, timeoutMs });
|
|
146
|
+
if (claim.status !== 200 || claim.json?.status !== "CLAIMED") {
|
|
147
|
+
r.message = `领取失败 HTTP ${claim.status || "-"}: ${String(claim.error || claim.text).slice(0, 160)}`;
|
|
148
|
+
return r;
|
|
149
|
+
}
|
|
150
|
+
r.ok = true;
|
|
151
|
+
r.amount = Number(claim.json?.benefit?.amount) || 0;
|
|
152
|
+
r.expiresAt = String(claim.json?.expiresAt || "");
|
|
153
|
+
r.campaignKey = String(target.campaignKey || "");
|
|
154
|
+
if (claim.json?.replayed) { r.status = "already"; r.message = "今日已领取(幂等返回)"; }
|
|
155
|
+
else {
|
|
156
|
+
r.status = "claimed";
|
|
157
|
+
r.message = r.amount > 0 ? `领取成功 +${r.amount} ${r.campaignKey}`.trim() : `领取成功 ${r.campaignKey}`.trim();
|
|
158
|
+
}
|
|
159
|
+
return r;
|
|
160
|
+
}
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
// qoder 上游技术常量(转译 qoder2api account/region.go,禁止改值)
|
|
2
|
-
export const OAUTH_CLIENT_ID = "e883ade2-e6e3-4d6d-adf7-f92ceff5fdcb";
|
|
3
|
-
|
|
4
|
-
// Region → 端点(global=国际站 *.qoder.sh / cn=国内站 *.qoder.com.cn)
|
|
5
|
-
export const ENDPOINTS = {
|
|
6
|
-
global: {
|
|
7
|
-
deviceLoginBase: "https://qoder.com/device/selectAccounts",
|
|
8
|
-
pollEndpoint: "https://openapi.qoder.sh/api/v1/deviceToken/poll",
|
|
9
|
-
userinfoBase: "https://openapi.qoder.sh/api/v1/userinfo",
|
|
10
|
-
chatStreamURL: "https://api1.qoder.sh/algo/api/v2/service/pro/sse/agent_chat_generation?FetchKeys=llm_model_result&AgentId=agent_common&Encode=1",
|
|
11
|
-
modelListURL: "https://api2.qoder.sh/algo/api/v2/model/list?Encode=1",
|
|
12
|
-
jobTokenURL: "https://center.qoder.sh/algo/api/v3/user/jobToken?Encode=1",
|
|
13
|
-
openapiBase: "https://openapi.qoder.sh",
|
|
14
|
-
},
|
|
15
|
-
cn: {
|
|
16
|
-
deviceLoginBase: "https://qoder.com.cn/device/selectAccounts",
|
|
17
|
-
pollEndpoint: "https://openapi.qoder.com.cn/api/v1/deviceToken/poll",
|
|
18
|
-
userinfoBase: "https://openapi.qoder.com.cn/api/v1/userinfo",
|
|
19
|
-
chatStreamURL: "https://gateway.qoder.com.cn/algo/api/v2/service/pro/sse/agent_chat_generation?FetchKeys=llm_model_result&AgentId=agent_common&Encode=1",
|
|
20
|
-
modelListURL: "https://gateway.qoder.com.cn/algo/api/v2/model/list?Encode=1",
|
|
21
|
-
jobTokenURL: "https://gateway.qoder.com.cn/algo/api/v3/user/jobToken?Encode=1",
|
|
22
|
-
openapiBase: "https://openapi.qoder.com.cn",
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export function normalizeRegion(s) {
|
|
27
|
-
const v = String(s || "").trim().toLowerCase();
|
|
28
|
-
return v === "cn" ? "cn" : "global";
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function getEndpoints(region) {
|
|
32
|
-
return ENDPOINTS[normalizeRegion(region)];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 签到/额度路径(同 base 换域名):global=openapi.qoder.sh(有 campaigns,daily-check-in 404);
|
|
36
|
-
// cn=openapi.qoder.com.cn(两者皆有;Go 版签到硬编码该域,故只服务国内号)。
|
|
37
|
-
export const EP_CAMPAIGNS = "/sash/api/v1/me/campaigns";
|
|
38
|
-
export const EP_CHECKIN_STATUS = "/sash/api/v1/me/daily-check-in/status";
|
|
39
|
-
export const EP_CHECKIN_CLAIM = "/sash/api/v1/me/daily-check-in/claim";
|
|
40
|
-
export const EP_PLAN = "/api/v2/user/plan";
|
|
41
|
-
export const EP_QUOTA = "/api/v2/quota/usage";
|
|
42
|
-
|
|
43
|
-
export function openapiBase(region) {
|
|
44
|
-
return getEndpoints(region).openapiBase;
|
|
1
|
+
// qoder 上游技术常量(转译 qoder2api account/region.go,禁止改值)
|
|
2
|
+
export const OAUTH_CLIENT_ID = "e883ade2-e6e3-4d6d-adf7-f92ceff5fdcb";
|
|
3
|
+
|
|
4
|
+
// Region → 端点(global=国际站 *.qoder.sh / cn=国内站 *.qoder.com.cn)
|
|
5
|
+
export const ENDPOINTS = {
|
|
6
|
+
global: {
|
|
7
|
+
deviceLoginBase: "https://qoder.com/device/selectAccounts",
|
|
8
|
+
pollEndpoint: "https://openapi.qoder.sh/api/v1/deviceToken/poll",
|
|
9
|
+
userinfoBase: "https://openapi.qoder.sh/api/v1/userinfo",
|
|
10
|
+
chatStreamURL: "https://api1.qoder.sh/algo/api/v2/service/pro/sse/agent_chat_generation?FetchKeys=llm_model_result&AgentId=agent_common&Encode=1",
|
|
11
|
+
modelListURL: "https://api2.qoder.sh/algo/api/v2/model/list?Encode=1",
|
|
12
|
+
jobTokenURL: "https://center.qoder.sh/algo/api/v3/user/jobToken?Encode=1",
|
|
13
|
+
openapiBase: "https://openapi.qoder.sh",
|
|
14
|
+
},
|
|
15
|
+
cn: {
|
|
16
|
+
deviceLoginBase: "https://qoder.com.cn/device/selectAccounts",
|
|
17
|
+
pollEndpoint: "https://openapi.qoder.com.cn/api/v1/deviceToken/poll",
|
|
18
|
+
userinfoBase: "https://openapi.qoder.com.cn/api/v1/userinfo",
|
|
19
|
+
chatStreamURL: "https://gateway.qoder.com.cn/algo/api/v2/service/pro/sse/agent_chat_generation?FetchKeys=llm_model_result&AgentId=agent_common&Encode=1",
|
|
20
|
+
modelListURL: "https://gateway.qoder.com.cn/algo/api/v2/model/list?Encode=1",
|
|
21
|
+
jobTokenURL: "https://gateway.qoder.com.cn/algo/api/v3/user/jobToken?Encode=1",
|
|
22
|
+
openapiBase: "https://openapi.qoder.com.cn",
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function normalizeRegion(s) {
|
|
27
|
+
const v = String(s || "").trim().toLowerCase();
|
|
28
|
+
return v === "cn" ? "cn" : "global";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getEndpoints(region) {
|
|
32
|
+
return ENDPOINTS[normalizeRegion(region)];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 签到/额度路径(同 base 换域名):global=openapi.qoder.sh(有 campaigns,daily-check-in 404);
|
|
36
|
+
// cn=openapi.qoder.com.cn(两者皆有;Go 版签到硬编码该域,故只服务国内号)。
|
|
37
|
+
export const EP_CAMPAIGNS = "/sash/api/v1/me/campaigns";
|
|
38
|
+
export const EP_CHECKIN_STATUS = "/sash/api/v1/me/daily-check-in/status";
|
|
39
|
+
export const EP_CHECKIN_CLAIM = "/sash/api/v1/me/daily-check-in/claim";
|
|
40
|
+
export const EP_PLAN = "/api/v2/user/plan";
|
|
41
|
+
export const EP_QUOTA = "/api/v2/quota/usage";
|
|
42
|
+
|
|
43
|
+
export function openapiBase(region) {
|
|
44
|
+
return getEndpoints(region).openapiBase;
|
|
45
45
|
}
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
// qoder COSY 自定义 base64 编解码(转译 qoder2api internal/cosy/encoding.go)
|
|
2
|
-
// 自定义字母表 + 1/3 尾部轮转,`$` 代 `=`
|
|
3
|
-
const CUSTOM = "_doRTgHZBKcGVjlvpC,@aFSx#DPuNJme&i*MzLOEn)sUrthbf%Y^w.(kIQyXqWA!";
|
|
4
|
-
const STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
5
|
-
const CUSTOM_PAD = "$";
|
|
6
|
-
|
|
7
|
-
const c2s = new Array(128).fill(-1);
|
|
8
|
-
const s2c = new Array(128).fill(-1);
|
|
9
|
-
|
|
10
|
-
for (let i = 0; i < 64; i++) {
|
|
11
|
-
c2s[CUSTOM.charCodeAt(i)] = STD.charCodeAt(i);
|
|
12
|
-
s2c[STD.charCodeAt(i)] = CUSTOM.charCodeAt(i);
|
|
13
|
-
}
|
|
14
|
-
c2s[CUSTOM_PAD.charCodeAt(0)] = "=".charCodeAt(0);
|
|
15
|
-
s2c["=".charCodeAt(0)] = CUSTOM_PAD.charCodeAt(0);
|
|
16
|
-
|
|
17
|
-
export function cosyEncode(plain) {
|
|
18
|
-
const std = Buffer.from(plain).toString("base64");
|
|
19
|
-
const n = std.length;
|
|
20
|
-
const a = Math.floor(n / 3);
|
|
21
|
-
const rearranged = std.slice(n - a) + std.slice(a, n - a) + std.slice(0, a);
|
|
22
|
-
const out = new Array(n);
|
|
23
|
-
for (let i = 0; i < n; i++) {
|
|
24
|
-
const c = rearranged.charCodeAt(i);
|
|
25
|
-
if (c >= 128 || s2c[c] < 0) throw new Error("char out of alphabet: " + c);
|
|
26
|
-
out[i] = String.fromCharCode(s2c[c]);
|
|
27
|
-
}
|
|
28
|
-
return out.join("");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function cosyDecode(encoded) {
|
|
32
|
-
const n = encoded.length;
|
|
33
|
-
const mapped = new Array(n);
|
|
34
|
-
for (let i = 0; i < n; i++) {
|
|
35
|
-
const c = encoded.charCodeAt(i);
|
|
36
|
-
if (c >= 128 || c2s[c] < 0) throw new Error("char out of custom alphabet: " + c);
|
|
37
|
-
mapped[i] = String.fromCharCode(c2s[c]);
|
|
38
|
-
}
|
|
39
|
-
const a = Math.floor(n / 3);
|
|
40
|
-
const std = mapped.slice(n - a).join("") + mapped.slice(a, n - a).join("") + mapped.slice(0, a).join("");
|
|
41
|
-
return Buffer.from(std, "base64");
|
|
1
|
+
// qoder COSY 自定义 base64 编解码(转译 qoder2api internal/cosy/encoding.go)
|
|
2
|
+
// 自定义字母表 + 1/3 尾部轮转,`$` 代 `=`
|
|
3
|
+
const CUSTOM = "_doRTgHZBKcGVjlvpC,@aFSx#DPuNJme&i*MzLOEn)sUrthbf%Y^w.(kIQyXqWA!";
|
|
4
|
+
const STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
5
|
+
const CUSTOM_PAD = "$";
|
|
6
|
+
|
|
7
|
+
const c2s = new Array(128).fill(-1);
|
|
8
|
+
const s2c = new Array(128).fill(-1);
|
|
9
|
+
|
|
10
|
+
for (let i = 0; i < 64; i++) {
|
|
11
|
+
c2s[CUSTOM.charCodeAt(i)] = STD.charCodeAt(i);
|
|
12
|
+
s2c[STD.charCodeAt(i)] = CUSTOM.charCodeAt(i);
|
|
13
|
+
}
|
|
14
|
+
c2s[CUSTOM_PAD.charCodeAt(0)] = "=".charCodeAt(0);
|
|
15
|
+
s2c["=".charCodeAt(0)] = CUSTOM_PAD.charCodeAt(0);
|
|
16
|
+
|
|
17
|
+
export function cosyEncode(plain) {
|
|
18
|
+
const std = Buffer.from(plain).toString("base64");
|
|
19
|
+
const n = std.length;
|
|
20
|
+
const a = Math.floor(n / 3);
|
|
21
|
+
const rearranged = std.slice(n - a) + std.slice(a, n - a) + std.slice(0, a);
|
|
22
|
+
const out = new Array(n);
|
|
23
|
+
for (let i = 0; i < n; i++) {
|
|
24
|
+
const c = rearranged.charCodeAt(i);
|
|
25
|
+
if (c >= 128 || s2c[c] < 0) throw new Error("char out of alphabet: " + c);
|
|
26
|
+
out[i] = String.fromCharCode(s2c[c]);
|
|
27
|
+
}
|
|
28
|
+
return out.join("");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function cosyDecode(encoded) {
|
|
32
|
+
const n = encoded.length;
|
|
33
|
+
const mapped = new Array(n);
|
|
34
|
+
for (let i = 0; i < n; i++) {
|
|
35
|
+
const c = encoded.charCodeAt(i);
|
|
36
|
+
if (c >= 128 || c2s[c] < 0) throw new Error("char out of custom alphabet: " + c);
|
|
37
|
+
mapped[i] = String.fromCharCode(c2s[c]);
|
|
38
|
+
}
|
|
39
|
+
const a = Math.floor(n / 3);
|
|
40
|
+
const std = mapped.slice(n - a).join("") + mapped.slice(a, n - a).join("") + mapped.slice(0, a).join("");
|
|
41
|
+
return Buffer.from(std, "base64");
|
|
42
42
|
}
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
// qoder 设备指纹派生(转译 qoder2api internal/cosy/fingerprint.go + session.go)
|
|
2
|
-
// fingerprint: 由 seed(uid 优先,无 uid 用 credential)派生 machineId/machineToken/machineType
|
|
3
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
4
|
-
|
|
5
|
-
export function fingerprintSeed(uid, credential) {
|
|
6
|
-
if (uid) return uid;
|
|
7
|
-
return "cred:" + credential;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function deriveMachineId(seed) {
|
|
11
|
-
return createHash("md5").update("machine:" + seed).digest("hex");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function deriveMachineType(seed) {
|
|
15
|
-
return createHash("md5").update("machinetype:" + seed).digest("hex").slice(0, 18);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function deriveMachineToken(seed) {
|
|
19
|
-
const sum = createHash("sha512").update("machinetoken:" + seed).digest();
|
|
20
|
-
return sum.toString("base64url").slice(0, 43);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function newUUID() {
|
|
24
|
-
const b = randomBytes(16);
|
|
25
|
-
const hex = b.toString("hex");
|
|
26
|
-
return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20,32)}`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function newRequestId() {
|
|
30
|
-
return randomBytes(12).toString("hex");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function unixSec() {
|
|
34
|
-
return Math.floor(Date.now() / 1000);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function unixMs() {
|
|
38
|
-
return Date.now();
|
|
1
|
+
// qoder 设备指纹派生(转译 qoder2api internal/cosy/fingerprint.go + session.go)
|
|
2
|
+
// fingerprint: 由 seed(uid 优先,无 uid 用 credential)派生 machineId/machineToken/machineType
|
|
3
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
4
|
+
|
|
5
|
+
export function fingerprintSeed(uid, credential) {
|
|
6
|
+
if (uid) return uid;
|
|
7
|
+
return "cred:" + credential;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function deriveMachineId(seed) {
|
|
11
|
+
return createHash("md5").update("machine:" + seed).digest("hex");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function deriveMachineType(seed) {
|
|
15
|
+
return createHash("md5").update("machinetype:" + seed).digest("hex").slice(0, 18);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function deriveMachineToken(seed) {
|
|
19
|
+
const sum = createHash("sha512").update("machinetoken:" + seed).digest();
|
|
20
|
+
return sum.toString("base64url").slice(0, 43);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function newUUID() {
|
|
24
|
+
const b = randomBytes(16);
|
|
25
|
+
const hex = b.toString("hex");
|
|
26
|
+
return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20,32)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function newRequestId() {
|
|
30
|
+
return randomBytes(12).toString("hex");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function unixSec() {
|
|
34
|
+
return Math.floor(Date.now() / 1000);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function unixMs() {
|
|
38
|
+
return Date.now();
|
|
39
39
|
}
|