dsh-qoder-connect 0.1.4 → 0.1.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/README.en.md +8 -0
- package/README.md +8 -0
- package/assets/7.png +0 -0
- package/lib/bin.js +1 -1
- package/lib/client.js +297 -4
- package/lib/index.d.ts +40 -1
- package/lib/index.js +236 -4
- package/lib/{variants-CSeJqJpR.js → variants-BEiQve5M.js} +152 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -17,6 +17,7 @@ Configure only what you use: a variant with no saved PAT shows no model group an
|
|
|
17
17
|
- **Two independent variants** — `qoder` (china) and `qoder-global` (global) keep separate credential files, routes and saved catalogs: a token minted on qoder.com does not work against the China deployment and vice versa.
|
|
18
18
|
- **Three-tier model catalog (live → saved → fallback)** — the card states where the list on screen came from: *“Model list updated …”* (fetched now), *“Showing the saved model list from …”* (this account's last successful fetch, restored after a restart or a failed fetch), or *“Showing the built-in model list (not yet updated from Qoder)”* (the roster compiled into the plugin). The reason of the most recent failed fetch is shown too, and a *Refresh model list* button sits right there.
|
|
19
19
|
- **One-toggle context window** — each variant card's *Context window* tab lists every model's capacity (the default and the largest declared window) and carries its own *“Use the largest declared context window”* preference (on by default): on, requests declare the maximum (e.g. Qwen3.8-Max at 1M); off, the default (200K). The two variants keep independent toggle state.
|
|
20
|
+
- **Daily auto check-in & logs** — automatically checks in daily at 10:00 (UTC+8) to claim 100 Credits, with independent toggles and startup catch-up protection. The card features a dedicated *Check-in log* panel with audit trails, *Check in now*, *Refresh*, and *Clear logs* quick actions.
|
|
20
21
|
- **Sidebar quota card + credit details** — the *“Qoder sidebar display”* card toggles a per-variant sidebar quota widget (off by default; each toggle needs its variant's PAT saved) with one shared refresh interval (default 5 min, minimum 1 min). Clicking the sidebar widget opens the *Qoder quota* panel: one row per credit package — *“Remaining / Total + bar | Expires”* — plus the cycle share and reset time. Click the same widget again to close; click the other to switch variants.
|
|
21
22
|
- **Rate display `x<priceFactor>`** — each model name is suffixed with the upstream-reported price multiplier (e.g. `Some Model · x0.79`, free is `x0`), spelled `x<n>` from the catalog's `price_factor`. Display only — it never changes the request; models whose rate the upstream did not report simply show no suffix.
|
|
22
23
|
|
|
@@ -58,6 +59,12 @@ Each variant card's *Context window* tab lists capacities per model under the *
|
|
|
58
59
|
|
|
59
60
|

|
|
60
61
|
|
|
62
|
+
### Daily Auto Check-In & Check-In Log
|
|
63
|
+
|
|
64
|
+
Enable daily automatic check-in in the settings card, and inspect check-in history logs, trigger manual check-in, or clear logs in the *Check-in log* tab:
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
61
68
|
## Install
|
|
62
69
|
|
|
63
70
|
Prerequisites:
|
|
@@ -109,6 +116,7 @@ Everything the plugin owns lives in one data directory: `<profile>/.dsh-qoder-co
|
|
|
109
116
|
<profile>/.dsh-qoder-connect/
|
|
110
117
|
├── .qoder-auth.json # China PAT ({version:2, pat, region, savedAt})
|
|
111
118
|
├── .qoder-global-auth.json # Global PAT
|
|
119
|
+
├── checkin-status.json # check-in status and history logs
|
|
112
120
|
└── state/
|
|
113
121
|
├── .qoder-catalog.json # China per-account saved catalog
|
|
114
122
|
├── .qoder-global-catalog.json
|
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
- **双变体独立配置**:`qoder`(china)与 `qoder-global`(global)各有自己的凭证文件、路由与已保存目录——qoder.com 生成的令牌对中国区无效,反之亦然,两套永不互串。
|
|
18
18
|
- **模型目录三级降级(live → saved → fallback)**:卡片明示当前列表来自哪里——「模型列表更新于 …」(实时拉取)、「当前显示已保存的模型列表,更新于 …」(本账号上次成功目录,重启或断网后顶上来)、「当前显示内置模型列表(尚未从 Qoder 更新)」(编译进插件的兜底名单);最近一次拉取失败时附带原因,并可在卡片上手动「刷新模型列表」。
|
|
19
19
|
- **上下文窗口一键切换**:两张变体卡片的「上下文窗口」页列出每个模型的容量(默认值与上游声明的最大值),并各自提供「使用上游声明的最大上下文窗口」开关(默认勾选):勾选按最大窗口(如 Qwen3.8-Max 的 1M)发请求,不勾选按默认窗口(200K),两版开关状态独立保存、互不影响。
|
|
20
|
+
- **每日自动签到与日志明细**:支持每天 10:00 (UTC+8) 自动签到领取 100 Credits 算力额度,配备双端开关与开机防漏补签机制;卡片常驻「签到日志」面板,提供执行流水记录与「立即签到」、「刷新」、「清空日志」快捷操作。
|
|
20
21
|
- **侧栏额度展示 + 额度明细**:「Qoder 侧栏展示」卡片可分别为两版开启侧栏底部的额度小卡(默认关,开启前需为该变体保存 PAT),共享一个刷新间隔(默认 5 分钟,最小 1 分钟)。点侧栏小卡在中间面板打开「Qoder 额度」明细:按资源包逐行给出「剩余 / 总量 + 进度条|到期时间」,合计占比与本轮重置时间一并展示;再点同一张卡关闭,点另一张切换到对应版本。
|
|
21
22
|
- **倍率显示 `x<priceFactor>`**:模型名后直接拼上游报价的价格倍率(如 `某模型 · x0.79`,免费为 `x0`),来自目录接口的 `price_factor` 字段,仅作展示、不影响请求;上游未报倍率的模型不显示后缀,不会凭空补一个数。
|
|
22
23
|
|
|
@@ -58,6 +59,12 @@
|
|
|
58
59
|
|
|
59
60
|

|
|
60
61
|
|
|
62
|
+
### 每日自动签到与签到日志
|
|
63
|
+
|
|
64
|
+
支持在设置卡片中开启每日自动签到,并在卡片底部的「签到日志」面板中查看签到流水、立即签到或清空日志:
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
61
68
|
## 安装
|
|
62
69
|
|
|
63
70
|
前置条件:
|
|
@@ -109,6 +116,7 @@ DSH → 设置 → 插件 → 对应卡片,展开后把 PAT 粘贴进密码输
|
|
|
109
116
|
<profile>/.dsh-qoder-connect/
|
|
110
117
|
├── .qoder-auth.json # 中国版 PAT({version:2, pat, region, savedAt})
|
|
111
118
|
├── .qoder-global-auth.json # 国际版 PAT
|
|
119
|
+
├── checkin-status.json # 签到状态与历史日志记录
|
|
112
120
|
└── state/
|
|
113
121
|
├── .qoder-catalog.json # 中国版按账号保存的模型目录
|
|
114
122
|
├── .qoder-global-catalog.json
|
package/assets/7.png
ADDED
|
Binary file
|
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { B as qoderCredentialIdentity, D as FALLBACK_QODER_MODELS, E as qoderCatalogPath, R as QoderCredentialStore, T as QoderCatalogStore, b as createQoderTransport, f as QODER_CONNECT_VERSION, i as variantFor, l as qoderHostHeartbeatPath, m as QoderUpstreamClient, r as QODER_VARIANTS, s as isHeartbeatProcessAlive, t as CHINA_VARIANT, u as readHostHeartbeat, y as validateApiKey } from "./variants-
|
|
2
|
+
import { B as qoderCredentialIdentity, D as FALLBACK_QODER_MODELS, E as qoderCatalogPath, R as QoderCredentialStore, T as QoderCatalogStore, b as createQoderTransport, f as QODER_CONNECT_VERSION, i as variantFor, l as qoderHostHeartbeatPath, m as QoderUpstreamClient, r as QODER_VARIANTS, s as isHeartbeatProcessAlive, t as CHINA_VARIANT, u as readHostHeartbeat, y as validateApiKey } from "./variants-BEiQve5M.js";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import { realpathSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
package/lib/client.js
CHANGED
|
@@ -238,6 +238,8 @@ window.__ModuleLoader__.load({
|
|
|
238
238
|
values: {
|
|
239
239
|
sidebarQuotaCN: false,
|
|
240
240
|
sidebarQuotaGlobal: false,
|
|
241
|
+
autoCheckInCN: false,
|
|
242
|
+
autoCheckInGlobal: false,
|
|
241
243
|
quotaPollMs: POLL_DEFAULT_MS
|
|
242
244
|
}
|
|
243
245
|
};
|
|
@@ -249,6 +251,8 @@ window.__ModuleLoader__.load({
|
|
|
249
251
|
values: {
|
|
250
252
|
sidebarQuotaCN: value.sidebarQuotaCN === true,
|
|
251
253
|
sidebarQuotaGlobal: value.sidebarQuotaGlobal === true,
|
|
254
|
+
autoCheckInCN: value.autoCheckInCN === true,
|
|
255
|
+
autoCheckInGlobal: value.autoCheckInGlobal === true,
|
|
252
256
|
quotaPollMs: typeof value.quotaPollMs === "number" ? value.quotaPollMs : POLL_DEFAULT_MS
|
|
253
257
|
}
|
|
254
258
|
};
|
|
@@ -272,13 +276,15 @@ window.__ModuleLoader__.load({
|
|
|
272
276
|
values: {
|
|
273
277
|
sidebarQuotaCN: false,
|
|
274
278
|
sidebarQuotaGlobal: false,
|
|
279
|
+
autoCheckInCN: false,
|
|
280
|
+
autoCheckInGlobal: false,
|
|
275
281
|
quotaPollMs: POLL_DEFAULT_MS
|
|
276
282
|
}
|
|
277
283
|
};
|
|
278
284
|
function stableProject(scope) {
|
|
279
285
|
if (scope === void 0) return UNAVAILABLE;
|
|
280
286
|
const next = project(scope);
|
|
281
|
-
if (cachedProjection === void 0 || cachedScope !== scope || cachedProjection.status !== next.status || cachedProjection.writable !== next.writable || cachedProjection.values.sidebarQuotaCN !== next.values.sidebarQuotaCN || cachedProjection.values.sidebarQuotaGlobal !== next.values.sidebarQuotaGlobal || cachedProjection.values.quotaPollMs !== next.values.quotaPollMs) {
|
|
287
|
+
if (cachedProjection === void 0 || cachedScope !== scope || cachedProjection.status !== next.status || cachedProjection.writable !== next.writable || cachedProjection.values.sidebarQuotaCN !== next.values.sidebarQuotaCN || cachedProjection.values.sidebarQuotaGlobal !== next.values.sidebarQuotaGlobal || cachedProjection.values.autoCheckInCN !== next.values.autoCheckInCN || cachedProjection.values.autoCheckInGlobal !== next.values.autoCheckInGlobal || cachedProjection.values.quotaPollMs !== next.values.quotaPollMs) {
|
|
282
288
|
cachedScope = scope;
|
|
283
289
|
cachedProjection = next;
|
|
284
290
|
}
|
|
@@ -369,6 +375,8 @@ window.__ModuleLoader__.load({
|
|
|
369
375
|
const write = (field, value) => {
|
|
370
376
|
if (field === "sidebarQuotaCN" && value === true && !signed.cn) return;
|
|
371
377
|
if (field === "sidebarQuotaGlobal" && value === true && !signed.global) return;
|
|
378
|
+
if (field === "autoCheckInCN" && value === true && !signed.cn) return;
|
|
379
|
+
if (field === "autoCheckInGlobal" && value === true && !signed.global) return;
|
|
372
380
|
scope?.set(field, value);
|
|
373
381
|
};
|
|
374
382
|
const minutes = Math.max(POLL_MIN_MS / 6e4, Math.round(projection.values.quotaPollMs / 6e4));
|
|
@@ -395,6 +403,22 @@ window.__ModuleLoader__.load({
|
|
|
395
403
|
disabledHint: t("quotaSignInRequired"),
|
|
396
404
|
onToggle: (next) => write("sidebarQuotaGlobal", next)
|
|
397
405
|
}),
|
|
406
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
407
|
+
label: t("autoCheckInCN"),
|
|
408
|
+
hint: t("autoCheckInHintCN"),
|
|
409
|
+
checked: projection.values.autoCheckInCN,
|
|
410
|
+
disabled: !signed.cn,
|
|
411
|
+
disabledHint: t("quotaSignInRequired"),
|
|
412
|
+
onToggle: (next) => write("autoCheckInCN", next)
|
|
413
|
+
}),
|
|
414
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
415
|
+
label: t("autoCheckInGlobal"),
|
|
416
|
+
hint: t("autoCheckInHintGlobal"),
|
|
417
|
+
checked: projection.values.autoCheckInGlobal,
|
|
418
|
+
disabled: !signed.global,
|
|
419
|
+
disabledHint: t("quotaSignInRequired"),
|
|
420
|
+
onToggle: (next) => write("autoCheckInGlobal", next)
|
|
421
|
+
}),
|
|
398
422
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
399
423
|
style: {
|
|
400
424
|
...rowStyle$1,
|
|
@@ -1040,6 +1064,136 @@ window.__ModuleLoader__.load({
|
|
|
1040
1064
|
if (tokens >= 1e3 && tokens % 1e3 === 0) return `${tokens / 1e3}K`;
|
|
1041
1065
|
return String(tokens);
|
|
1042
1066
|
}
|
|
1067
|
+
function CheckInLogTable({ logs = [], t, onCheckIn, onRefresh, onClear, busy, checkingIn, clearing, disabled, notice }) {
|
|
1068
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1069
|
+
style: quotaListStyle,
|
|
1070
|
+
children: [
|
|
1071
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1072
|
+
style: {
|
|
1073
|
+
display: "flex",
|
|
1074
|
+
alignItems: "center",
|
|
1075
|
+
justifyContent: "space-between",
|
|
1076
|
+
flexWrap: "wrap",
|
|
1077
|
+
gap: 8
|
|
1078
|
+
},
|
|
1079
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1080
|
+
style: quotaTitleStyle,
|
|
1081
|
+
children: t("tabCheckIn")
|
|
1082
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1083
|
+
style: {
|
|
1084
|
+
display: "flex",
|
|
1085
|
+
alignItems: "center",
|
|
1086
|
+
gap: 6
|
|
1087
|
+
},
|
|
1088
|
+
children: [
|
|
1089
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1090
|
+
type: "button",
|
|
1091
|
+
style: buttonStyle$1,
|
|
1092
|
+
disabled: disabled || busy || checkingIn,
|
|
1093
|
+
onClick: onCheckIn,
|
|
1094
|
+
children: checkingIn ? t("checkInChecking") : t("checkInNow")
|
|
1095
|
+
}),
|
|
1096
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1097
|
+
type: "button",
|
|
1098
|
+
style: buttonStyle$1,
|
|
1099
|
+
disabled: busy || checkingIn,
|
|
1100
|
+
onClick: onRefresh,
|
|
1101
|
+
children: busy ? t("checkInRefreshing") : t("checkInRefresh")
|
|
1102
|
+
}),
|
|
1103
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1104
|
+
type: "button",
|
|
1105
|
+
style: buttonStyle$1,
|
|
1106
|
+
disabled: busy || clearing || !logs || logs.length === 0,
|
|
1107
|
+
onClick: onClear,
|
|
1108
|
+
children: clearing ? t("checkInClearing") : t("checkInClear")
|
|
1109
|
+
})
|
|
1110
|
+
]
|
|
1111
|
+
})]
|
|
1112
|
+
}),
|
|
1113
|
+
notice === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1114
|
+
style: bodyStyle,
|
|
1115
|
+
children: notice
|
|
1116
|
+
}),
|
|
1117
|
+
!logs || logs.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1118
|
+
style: descriptionStyle,
|
|
1119
|
+
children: t("checkInLogEmpty")
|
|
1120
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1121
|
+
style: {
|
|
1122
|
+
display: "flex",
|
|
1123
|
+
flexDirection: "column",
|
|
1124
|
+
gap: 6
|
|
1125
|
+
},
|
|
1126
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1127
|
+
style: {
|
|
1128
|
+
display: "flex",
|
|
1129
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2, rgba(127,127,127,0.15))",
|
|
1130
|
+
paddingBottom: 6,
|
|
1131
|
+
fontSize: 12,
|
|
1132
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
1133
|
+
},
|
|
1134
|
+
children: [
|
|
1135
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1136
|
+
style: { flex: 2 },
|
|
1137
|
+
children: t("checkInLogTime")
|
|
1138
|
+
}),
|
|
1139
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1140
|
+
style: { flex: 3 },
|
|
1141
|
+
children: t("checkInLogResult")
|
|
1142
|
+
}),
|
|
1143
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1144
|
+
style: {
|
|
1145
|
+
flex: 1,
|
|
1146
|
+
textAlign: "right"
|
|
1147
|
+
},
|
|
1148
|
+
children: t("checkInLogAmount")
|
|
1149
|
+
})
|
|
1150
|
+
]
|
|
1151
|
+
}), logs.map((log) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1152
|
+
style: {
|
|
1153
|
+
display: "flex",
|
|
1154
|
+
alignItems: "center",
|
|
1155
|
+
padding: "6px 0",
|
|
1156
|
+
fontSize: 13,
|
|
1157
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2, rgba(127,127,127,0.08))"
|
|
1158
|
+
},
|
|
1159
|
+
children: [
|
|
1160
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1161
|
+
style: {
|
|
1162
|
+
flex: 2,
|
|
1163
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
1164
|
+
},
|
|
1165
|
+
children: formatTime(log.timestamp)
|
|
1166
|
+
}),
|
|
1167
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1168
|
+
style: {
|
|
1169
|
+
flex: 3,
|
|
1170
|
+
display: "flex",
|
|
1171
|
+
alignItems: "center",
|
|
1172
|
+
gap: 6
|
|
1173
|
+
},
|
|
1174
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
|
|
1175
|
+
width: 6,
|
|
1176
|
+
height: 6,
|
|
1177
|
+
borderRadius: "50%",
|
|
1178
|
+
flexShrink: 0,
|
|
1179
|
+
background: log.status === "claimed" ? "var(--dsw-alias-status-success, #52c41a)" : log.status === "already-claimed" ? "var(--dsw-alias-status-info, #1890ff)" : log.status === "no-campaign" ? "var(--dsw-alias-label-tertiary, #999)" : "var(--dsw-alias-status-error, #f5222d)"
|
|
1180
|
+
} }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: log.status === "claimed" ? t("autoCheckInStatusClaimed", { amount: log.amount ?? 100 }) : log.status === "already-claimed" ? t("autoCheckInStatusAlready") : log.status === "no-campaign" ? t("autoCheckInStatusNoCampaign") : t("autoCheckInStatusError", { message: log.message ?? "" }) })]
|
|
1181
|
+
}),
|
|
1182
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1183
|
+
style: {
|
|
1184
|
+
flex: 1,
|
|
1185
|
+
textAlign: "right",
|
|
1186
|
+
fontWeight: 600,
|
|
1187
|
+
color: log.amount ? "var(--dsw-alias-brand-primary)" : "inherit"
|
|
1188
|
+
},
|
|
1189
|
+
children: log.amount ? `+${log.amount}` : "-"
|
|
1190
|
+
})
|
|
1191
|
+
]
|
|
1192
|
+
}, log.id))]
|
|
1193
|
+
})
|
|
1194
|
+
]
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1043
1197
|
/** Render Qoder PAT state, quota, catalog, and context capacities as one expandable card. */
|
|
1044
1198
|
function QoderPluginCard(props) {
|
|
1045
1199
|
const { t, scope, signedIn, variant, unified } = props;
|
|
@@ -1062,6 +1216,9 @@ window.__ModuleLoader__.load({
|
|
|
1062
1216
|
const [patNotice, setPatNotice] = (0, react.useState)();
|
|
1063
1217
|
const patInput = (0, react.useRef)(null);
|
|
1064
1218
|
const [tab, setTab] = (0, react.useState)("status");
|
|
1219
|
+
const [checkingIn, setCheckingIn] = (0, react.useState)(false);
|
|
1220
|
+
const [clearingLogs, setClearingLogs] = (0, react.useState)(false);
|
|
1221
|
+
const [checkInNotice, setCheckInNotice] = (0, react.useState)();
|
|
1065
1222
|
const mounted = (0, react.useRef)(true);
|
|
1066
1223
|
const readSeq = (0, react.useRef)(0);
|
|
1067
1224
|
const manualControllers = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
@@ -1202,6 +1359,83 @@ window.__ModuleLoader__.load({
|
|
|
1202
1359
|
t,
|
|
1203
1360
|
trackController
|
|
1204
1361
|
]);
|
|
1362
|
+
const manualCheckIn = (0, react.useCallback)(async () => {
|
|
1363
|
+
const key = status?.status === "signed-in" ? status.probeKey : void 0;
|
|
1364
|
+
if (key === void 0) return;
|
|
1365
|
+
setCheckingIn(true);
|
|
1366
|
+
setCheckInNotice(void 0);
|
|
1367
|
+
const controller = trackController();
|
|
1368
|
+
try {
|
|
1369
|
+
const response = await fetch(currentVariant.probePath, {
|
|
1370
|
+
method: "POST",
|
|
1371
|
+
headers: {
|
|
1372
|
+
"Content-Type": "application/json",
|
|
1373
|
+
"X-Qoder-Probe-Key": key
|
|
1374
|
+
},
|
|
1375
|
+
credentials: "same-origin",
|
|
1376
|
+
signal: controller.signal,
|
|
1377
|
+
body: JSON.stringify({ action: "checkin" })
|
|
1378
|
+
});
|
|
1379
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1380
|
+
const result = await response.json();
|
|
1381
|
+
if (result.state === "claimed") setCheckInNotice(t("autoCheckInStatusClaimed", { amount: result.amount ?? 100 }));
|
|
1382
|
+
else if (result.state === "already-claimed") setCheckInNotice(t("autoCheckInStatusAlready"));
|
|
1383
|
+
else if (result.state === "no-campaign") setCheckInNotice(t("autoCheckInStatusNoCampaign"));
|
|
1384
|
+
else if (result.reason) setCheckInNotice(t("autoCheckInStatusError", { message: result.reason }));
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
if (mounted.current && controller.signal.aborted !== true) setCheckInNotice(error instanceof Error ? error.message : t("requestFailed"));
|
|
1387
|
+
} finally {
|
|
1388
|
+
manualControllers.current.delete(controller);
|
|
1389
|
+
if (mounted.current) setCheckingIn(false);
|
|
1390
|
+
}
|
|
1391
|
+
try {
|
|
1392
|
+
await refresh(controller.signal);
|
|
1393
|
+
} finally {
|
|
1394
|
+
manualControllers.current.delete(controller);
|
|
1395
|
+
}
|
|
1396
|
+
}, [
|
|
1397
|
+
currentVariant.probePath,
|
|
1398
|
+
refresh,
|
|
1399
|
+
status,
|
|
1400
|
+
t,
|
|
1401
|
+
trackController
|
|
1402
|
+
]);
|
|
1403
|
+
const clearCheckInLogs = (0, react.useCallback)(async () => {
|
|
1404
|
+
const key = status?.status === "signed-in" ? status.probeKey : void 0;
|
|
1405
|
+
if (key === void 0) return;
|
|
1406
|
+
setClearingLogs(true);
|
|
1407
|
+
setCheckInNotice(void 0);
|
|
1408
|
+
const controller = trackController();
|
|
1409
|
+
try {
|
|
1410
|
+
const response = await fetch(currentVariant.probePath, {
|
|
1411
|
+
method: "POST",
|
|
1412
|
+
headers: {
|
|
1413
|
+
"Content-Type": "application/json",
|
|
1414
|
+
"X-Qoder-Probe-Key": key
|
|
1415
|
+
},
|
|
1416
|
+
credentials: "same-origin",
|
|
1417
|
+
signal: controller.signal,
|
|
1418
|
+
body: JSON.stringify({ action: "clear-checkin-logs" })
|
|
1419
|
+
});
|
|
1420
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
if (mounted.current && controller.signal.aborted !== true) setCheckInNotice(error instanceof Error ? error.message : t("requestFailed"));
|
|
1423
|
+
} finally {
|
|
1424
|
+
manualControllers.current.delete(controller);
|
|
1425
|
+
if (mounted.current) setClearingLogs(false);
|
|
1426
|
+
}
|
|
1427
|
+
try {
|
|
1428
|
+
await refresh(controller.signal);
|
|
1429
|
+
} finally {
|
|
1430
|
+
manualControllers.current.delete(controller);
|
|
1431
|
+
}
|
|
1432
|
+
}, [
|
|
1433
|
+
currentVariant.probePath,
|
|
1434
|
+
refresh,
|
|
1435
|
+
status,
|
|
1436
|
+
t,
|
|
1437
|
+
trackController
|
|
1438
|
+
]);
|
|
1205
1439
|
/**
|
|
1206
1440
|
* Run one probe-route control action and refresh the card's state afterwards.
|
|
1207
1441
|
*
|
|
@@ -1622,7 +1856,8 @@ window.__ModuleLoader__.load({
|
|
|
1622
1856
|
children: [
|
|
1623
1857
|
"status",
|
|
1624
1858
|
"context",
|
|
1625
|
-
"details"
|
|
1859
|
+
"details",
|
|
1860
|
+
"checkin"
|
|
1626
1861
|
].map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1627
1862
|
type: "button",
|
|
1628
1863
|
role: "tab",
|
|
@@ -1634,7 +1869,7 @@ window.__ModuleLoader__.load({
|
|
|
1634
1869
|
...tabStyle,
|
|
1635
1870
|
...tab === id ? tabActiveStyle : {}
|
|
1636
1871
|
},
|
|
1637
|
-
children: t(id === "status" ? "tabStatus" : id === "context" ? "tabContext" : "tabDetails")
|
|
1872
|
+
children: t(id === "status" ? "tabStatus" : id === "context" ? "tabContext" : id === "details" ? "tabDetails" : "tabCheckIn")
|
|
1638
1873
|
}, id))
|
|
1639
1874
|
}),
|
|
1640
1875
|
tab === "status" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1672,7 +1907,7 @@ window.__ModuleLoader__.load({
|
|
|
1672
1907
|
});
|
|
1673
1908
|
}
|
|
1674
1909
|
})
|
|
1675
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1910
|
+
}) : tab === "details" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1676
1911
|
style: tabPanelStyle,
|
|
1677
1912
|
children: status.credits === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1678
1913
|
style: quotaListStyle,
|
|
@@ -1688,6 +1923,26 @@ window.__ModuleLoader__.load({
|
|
|
1688
1923
|
t
|
|
1689
1924
|
}, `${account.packageName}-${String(index)}`))]
|
|
1690
1925
|
})
|
|
1926
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1927
|
+
style: tabPanelStyle,
|
|
1928
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckInLogTable, {
|
|
1929
|
+
logs: status.checkIn?.logs,
|
|
1930
|
+
t,
|
|
1931
|
+
busy,
|
|
1932
|
+
checkingIn,
|
|
1933
|
+
clearing: clearingLogs,
|
|
1934
|
+
disabled: status.status !== "signed-in",
|
|
1935
|
+
...checkInNotice === void 0 ? {} : { notice: checkInNotice },
|
|
1936
|
+
onCheckIn: () => {
|
|
1937
|
+
manualCheckIn();
|
|
1938
|
+
},
|
|
1939
|
+
onRefresh: () => {
|
|
1940
|
+
manualRefresh();
|
|
1941
|
+
},
|
|
1942
|
+
onClear: () => {
|
|
1943
|
+
clearCheckInLogs();
|
|
1944
|
+
}
|
|
1945
|
+
})
|
|
1691
1946
|
})
|
|
1692
1947
|
] }) : null,
|
|
1693
1948
|
status?.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -2958,6 +3213,17 @@ window.__ModuleLoader__.load({
|
|
|
2958
3213
|
tabStatus: "Status",
|
|
2959
3214
|
tabContext: "Context window",
|
|
2960
3215
|
tabDetails: "Credit details",
|
|
3216
|
+
tabCheckIn: "Check-in log",
|
|
3217
|
+
checkInLogTime: "Check-in time",
|
|
3218
|
+
checkInLogResult: "Result",
|
|
3219
|
+
checkInLogAmount: "Credits",
|
|
3220
|
+
checkInLogEmpty: "No check-in logs recorded yet.",
|
|
3221
|
+
checkInNow: "Check in now",
|
|
3222
|
+
checkInChecking: "Checking in…",
|
|
3223
|
+
checkInRefresh: "Refresh",
|
|
3224
|
+
checkInRefreshing: "Refreshing…",
|
|
3225
|
+
checkInClear: "Clear logs",
|
|
3226
|
+
checkInClearing: "Clearing…",
|
|
2961
3227
|
creditsDetailHeading: "By package",
|
|
2962
3228
|
creditsUsed: "Used this cycle: {percent}%",
|
|
2963
3229
|
creditsTotalUnlimited: "Total: Unlimited",
|
|
@@ -3020,6 +3286,14 @@ window.__ModuleLoader__.load({
|
|
|
3020
3286
|
quotaToggleCN: "Show China credit card",
|
|
3021
3287
|
quotaToggleGlobal: "Show global credit card",
|
|
3022
3288
|
quotaToggleHint: "Show this account’s remaining credit in the sidebar footer.",
|
|
3289
|
+
autoCheckInCN: "Qoder (China) daily auto check-in (100 Credits)",
|
|
3290
|
+
autoCheckInGlobal: "Qoder Global (International) daily auto check-in",
|
|
3291
|
+
autoCheckInHintCN: "Automatically check in daily at 10:00 (UTC+8) to claim 100 Credits for China account.",
|
|
3292
|
+
autoCheckInHintGlobal: "Automatically check in daily at 10:00 (UTC+8) to claim available perks for Global account.",
|
|
3293
|
+
autoCheckInStatusClaimed: "Checked in today (+{amount} Credits)",
|
|
3294
|
+
autoCheckInStatusAlready: "Already checked in today",
|
|
3295
|
+
autoCheckInStatusNoCampaign: "No active benefit campaign today",
|
|
3296
|
+
autoCheckInStatusError: "Auto check-in error: {message}",
|
|
3023
3297
|
quotaSignInRequired: "Save a PAT for this variant first to enable this card.",
|
|
3024
3298
|
quotaPollLabel: "Refresh interval",
|
|
3025
3299
|
quotaPollHint: "Applies to both quota cards. Longer is kinder to the billing endpoint.",
|
|
@@ -3092,6 +3366,17 @@ window.__ModuleLoader__.load({
|
|
|
3092
3366
|
tabStatus: "状态",
|
|
3093
3367
|
tabContext: "上下文窗口",
|
|
3094
3368
|
tabDetails: "额度明细",
|
|
3369
|
+
tabCheckIn: "签到日志",
|
|
3370
|
+
checkInLogTime: "签到时间",
|
|
3371
|
+
checkInLogResult: "签到结果",
|
|
3372
|
+
checkInLogAmount: "获得额度",
|
|
3373
|
+
checkInLogEmpty: "暂无签到日志记录。",
|
|
3374
|
+
checkInNow: "立即签到",
|
|
3375
|
+
checkInChecking: "正在签到…",
|
|
3376
|
+
checkInRefresh: "刷新",
|
|
3377
|
+
checkInRefreshing: "正在刷新…",
|
|
3378
|
+
checkInClear: "清空日志",
|
|
3379
|
+
checkInClearing: "正在清空…",
|
|
3095
3380
|
creditsDetailHeading: "按套餐",
|
|
3096
3381
|
creditsUsed: "本轮已用 {percent}%",
|
|
3097
3382
|
creditsTotalUnlimited: "合计:不限额",
|
|
@@ -3154,6 +3439,14 @@ window.__ModuleLoader__.load({
|
|
|
3154
3439
|
quotaToggleCN: "展示国内版额度",
|
|
3155
3440
|
quotaToggleGlobal: "展示国际版额度",
|
|
3156
3441
|
quotaToggleHint: "在侧栏底部展示该账号的剩余额度。",
|
|
3442
|
+
autoCheckInCN: "Qoder(国内版)每日自动签到",
|
|
3443
|
+
autoCheckInGlobal: "Qoder Global(国际版)每日自动签到",
|
|
3444
|
+
autoCheckInHintCN: "每天 10:00 (UTC+8) 自动签到领取 100 Credits 算力额度。",
|
|
3445
|
+
autoCheckInHintGlobal: "每天 10:00 (UTC+8) 自动检测并领取国际版可用福利额度。",
|
|
3446
|
+
autoCheckInStatusClaimed: "今日已自动签到(+{amount} Credits)",
|
|
3447
|
+
autoCheckInStatusAlready: "今日已完成签到",
|
|
3448
|
+
autoCheckInStatusNoCampaign: "今日无可用签到福利活动",
|
|
3449
|
+
autoCheckInStatusError: "自动签到出错:{message}",
|
|
3157
3450
|
quotaSignInRequired: "请先为该变体保存 PAT,再开启此开关。",
|
|
3158
3451
|
quotaPollLabel: "刷新间隔",
|
|
3159
3452
|
quotaPollHint: "对两张额度卡片同时生效。间隔越长对计费接口越友好。",
|
package/lib/index.d.ts
CHANGED
|
@@ -141,7 +141,7 @@ interface QoderProbeAction {
|
|
|
141
141
|
* All are writes, which is why they share this route's in-process key
|
|
142
142
|
* and loopback guards rather than the read-only status GET.
|
|
143
143
|
*/
|
|
144
|
-
action: 'probe' | 'clear' | 'refresh' | 'set-maximum-context-window';
|
|
144
|
+
action: 'probe' | 'clear' | 'refresh' | 'set-maximum-context-window' | 'clear-checkin-logs' | 'checkin';
|
|
145
145
|
/** Target model id; required for `probe`. */
|
|
146
146
|
model?: string;
|
|
147
147
|
/** Requested value for `set-maximum-context-window`. */
|
|
@@ -259,6 +259,25 @@ type QoderWebStatus = {
|
|
|
259
259
|
* loopback guard); it is never persisted and rotates per process.
|
|
260
260
|
*/
|
|
261
261
|
probeKey?: string;
|
|
262
|
+
/**
|
|
263
|
+
* Daily check-in status record for this variant.
|
|
264
|
+
*/
|
|
265
|
+
checkIn?: {
|
|
266
|
+
lastDate: string;
|
|
267
|
+
lastAt: number;
|
|
268
|
+
status: 'claimed' | 'already-claimed' | 'no-campaign' | 'error';
|
|
269
|
+
amount?: number | undefined;
|
|
270
|
+
message?: string | undefined;
|
|
271
|
+
logs?: readonly {
|
|
272
|
+
id: string;
|
|
273
|
+
date: string;
|
|
274
|
+
timestamp: number;
|
|
275
|
+
status: 'claimed' | 'already-claimed' | 'no-campaign' | 'error';
|
|
276
|
+
amount?: number | undefined;
|
|
277
|
+
campaignKey?: string | undefined;
|
|
278
|
+
message?: string | undefined;
|
|
279
|
+
}[] | undefined;
|
|
280
|
+
};
|
|
262
281
|
/**
|
|
263
282
|
* In-process key authorizing PAT writes, including clearing. Travels with
|
|
264
283
|
* the document for the same reason `probeKey` does.
|
|
@@ -692,6 +711,17 @@ interface QoderAccountInfo {
|
|
|
692
711
|
updatedAt: string;
|
|
693
712
|
}
|
|
694
713
|
//#endregion
|
|
714
|
+
//#region src/qoder/transport/checkin.d.ts
|
|
715
|
+
interface QoderCheckInResult {
|
|
716
|
+
variantId: string;
|
|
717
|
+
date: string;
|
|
718
|
+
timestamp: number;
|
|
719
|
+
status: 'claimed' | 'already-claimed' | 'no-campaign' | 'error';
|
|
720
|
+
amount?: number | undefined;
|
|
721
|
+
campaignKey?: string | undefined;
|
|
722
|
+
message?: string | undefined;
|
|
723
|
+
}
|
|
724
|
+
//#endregion
|
|
695
725
|
//#region src/qoder/transport/index.d.ts
|
|
696
726
|
interface QoderTransport {
|
|
697
727
|
stream(options: GenerateOptions, model?: QoderCatalogModel): AsyncIterable<StreamChunk>;
|
|
@@ -700,6 +730,7 @@ interface QoderTransport {
|
|
|
700
730
|
force?: boolean | undefined;
|
|
701
731
|
signal?: AbortSignal | undefined;
|
|
702
732
|
}): Promise<QoderAccountInfo>;
|
|
733
|
+
checkIn(signal?: AbortSignal): Promise<QoderCheckInResult>;
|
|
703
734
|
}
|
|
704
735
|
//#endregion
|
|
705
736
|
//#region src/upstream.d.ts
|
|
@@ -840,6 +871,10 @@ declare class QoderUpstreamClient {
|
|
|
840
871
|
* account's "unlimited" state.
|
|
841
872
|
*/
|
|
842
873
|
fetchCredits(signal?: AbortSignal): Promise<QoderCredits>;
|
|
874
|
+
/**
|
|
875
|
+
* Run daily benefit check-in for this client's variant.
|
|
876
|
+
*/
|
|
877
|
+
checkIn(signal?: AbortSignal): Promise<QoderCheckInResult>;
|
|
843
878
|
/**
|
|
844
879
|
* Stream one translated chat-completions request.
|
|
845
880
|
*
|
|
@@ -1345,6 +1380,10 @@ interface Config {
|
|
|
1345
1380
|
sidebarQuotaCN?: boolean;
|
|
1346
1381
|
/** Show the global variant's sidebar quota card. */
|
|
1347
1382
|
sidebarQuotaGlobal?: boolean;
|
|
1383
|
+
/** Automatically check in daily at 10:00 (UTC+8) to claim credits for China variant. */
|
|
1384
|
+
autoCheckInCN?: boolean;
|
|
1385
|
+
/** Automatically check in daily at 10:00 (UTC+8) to claim credits for Global variant. */
|
|
1386
|
+
autoCheckInGlobal?: boolean;
|
|
1348
1387
|
/**
|
|
1349
1388
|
* Sidebar quota refresh interval in milliseconds. One shared value (both
|
|
1350
1389
|
* cards poll on it) because the two widgets hit the same rate-limited
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as QODER_GLOBAL_AUTH_PATH, B as qoderCredentialIdentity, C as probeModel, D as FALLBACK_QODER_MODELS, E as qoderCatalogPath, F as QODER_AUTH_FILENAME, G as qoderPluginDataDir, H as QODER_DATA_DIR_ENV, I as QODER_PAT_ENV_CN, K as qoderStateDir, L as QODER_PAT_ENV_GLOBAL, M as QODER_GLOBAL_STATUS_PATH, N as QODER_PROBE_PATH, O as QoderCatalog, P as QODER_STATUS_PATH, R as QoderCredentialStore, S as PROBE_EFFORT_CANDIDATES, T as QoderCatalogStore, U as QODER_DATA_DIR_NAME, V as qoderOwnAuthPath, W as qoderMachineIdPath, _ as modelInfoOf, a as QODER_HOST_HEARTBEAT_FILENAME, b as createQoderTransport, c as processStartTimeMs, d as writeHostHeartbeat, f as QODER_CONNECT_VERSION, g as kindFromQoderFailure, h as classifyUpstreamError, i as variantFor, j as QODER_GLOBAL_PROBE_PATH, k as QODER_AUTH_PATH, l as qoderHostHeartbeatPath, m as QoderUpstreamClient, n as GLOBAL_VARIANT, o as clearHostHeartbeat, p as KIND_STATUS, r as QODER_VARIANTS, s as isHeartbeatProcessAlive, t as CHINA_VARIANT, u as readHostHeartbeat, v as normalizeCredits, w as randomSentinel, x as getMachineId, y as validateApiKey, z as patTail } from "./variants-
|
|
1
|
+
import { A as QODER_GLOBAL_AUTH_PATH, B as qoderCredentialIdentity, C as probeModel, D as FALLBACK_QODER_MODELS, E as qoderCatalogPath, F as QODER_AUTH_FILENAME, G as qoderPluginDataDir, H as QODER_DATA_DIR_ENV, I as QODER_PAT_ENV_CN, K as qoderStateDir, L as QODER_PAT_ENV_GLOBAL, M as QODER_GLOBAL_STATUS_PATH, N as QODER_PROBE_PATH, O as QoderCatalog, P as QODER_STATUS_PATH, R as QoderCredentialStore, S as PROBE_EFFORT_CANDIDATES, T as QoderCatalogStore, U as QODER_DATA_DIR_NAME, V as qoderOwnAuthPath, W as qoderMachineIdPath, _ as modelInfoOf, a as QODER_HOST_HEARTBEAT_FILENAME, b as createQoderTransport, c as processStartTimeMs, d as writeHostHeartbeat, f as QODER_CONNECT_VERSION, g as kindFromQoderFailure, h as classifyUpstreamError, i as variantFor, j as QODER_GLOBAL_PROBE_PATH, k as QODER_AUTH_PATH, l as qoderHostHeartbeatPath, m as QoderUpstreamClient, n as GLOBAL_VARIANT, o as clearHostHeartbeat, p as KIND_STATUS, r as QODER_VARIANTS, s as isHeartbeatProcessAlive, t as CHINA_VARIANT, u as readHostHeartbeat, v as normalizeCredits, w as randomSentinel, x as getMachineId, y as validateApiKey, z as patTail } from "./variants-BEiQve5M.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -1056,15 +1056,20 @@ async function qoderWebStatus(deps) {
|
|
|
1056
1056
|
...probed,
|
|
1057
1057
|
jobTokenRefreshedAt: refreshedAt
|
|
1058
1058
|
};
|
|
1059
|
+
const checkInRecord = deps.checkIn?.();
|
|
1060
|
+
const withCheckIn = checkInRecord === void 0 ? withRefreshNotice : {
|
|
1061
|
+
...withRefreshNotice,
|
|
1062
|
+
checkIn: checkInRecord
|
|
1063
|
+
};
|
|
1059
1064
|
try {
|
|
1060
1065
|
const credits = await deps.client.fetchCredits();
|
|
1061
1066
|
return {
|
|
1062
|
-
...
|
|
1067
|
+
...withCheckIn,
|
|
1063
1068
|
credits
|
|
1064
1069
|
};
|
|
1065
1070
|
} catch (error) {
|
|
1066
1071
|
return {
|
|
1067
|
-
...
|
|
1072
|
+
...withCheckIn,
|
|
1068
1073
|
creditsError: safeMessage(error)
|
|
1069
1074
|
};
|
|
1070
1075
|
}
|
|
@@ -1169,6 +1174,8 @@ function parseAction(text) {
|
|
|
1169
1174
|
const wrapped = parsed;
|
|
1170
1175
|
const action = wrapped["action"];
|
|
1171
1176
|
if (action === "clear") return { action: "clear" };
|
|
1177
|
+
if (action === "clear-checkin-logs") return { action: "clear-checkin-logs" };
|
|
1178
|
+
if (action === "checkin") return { action: "checkin" };
|
|
1172
1179
|
if (action === "refresh") return { action: "refresh" };
|
|
1173
1180
|
if (action === "set-maximum-context-window") return typeof wrapped["enabled"] === "boolean" ? {
|
|
1174
1181
|
action: "set-maximum-context-window",
|
|
@@ -1212,6 +1219,19 @@ function qoderProbeHandler(deps, key) {
|
|
|
1212
1219
|
return;
|
|
1213
1220
|
}
|
|
1214
1221
|
try {
|
|
1222
|
+
if (action.action === "clear-checkin-logs") {
|
|
1223
|
+
deps.clearCheckInLogs?.();
|
|
1224
|
+
json(res, 200, { state: "cleared" });
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
if (action.action === "checkin") {
|
|
1228
|
+
if (deps.checkIn === void 0) {
|
|
1229
|
+
json(res, 404, { error: "checkin-not-supported" });
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
json(res, 200, await deps.checkIn());
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1215
1235
|
if (action.action === "clear") {
|
|
1216
1236
|
deps.clear();
|
|
1217
1237
|
json(res, 200, { state: "cleared" });
|
|
@@ -1254,6 +1274,162 @@ function registerQoderProbeRoute(ctx, deps, key) {
|
|
|
1254
1274
|
}, "dsh-qoder-connect: probe control route");
|
|
1255
1275
|
}
|
|
1256
1276
|
//#endregion
|
|
1277
|
+
//#region src/checkin-scheduler.ts
|
|
1278
|
+
/** Scheduling and catch-up orchestration for daily 10:00 (UTC+8) check-in. */
|
|
1279
|
+
var JsonFileCheckInStore = class {
|
|
1280
|
+
filePath;
|
|
1281
|
+
constructor(filePath) {
|
|
1282
|
+
this.filePath = filePath ?? join(qoderPluginDataDir(), "checkin-status.json");
|
|
1283
|
+
}
|
|
1284
|
+
readAll() {
|
|
1285
|
+
try {
|
|
1286
|
+
if (!existsSync(this.filePath)) return {};
|
|
1287
|
+
const raw = readFileSync(this.filePath, "utf-8");
|
|
1288
|
+
return JSON.parse(raw);
|
|
1289
|
+
} catch {
|
|
1290
|
+
return {};
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
read(variantId) {
|
|
1294
|
+
return this.readAll()[variantId];
|
|
1295
|
+
}
|
|
1296
|
+
clearLogs(variantId) {
|
|
1297
|
+
try {
|
|
1298
|
+
const all = this.readAll();
|
|
1299
|
+
if (all[variantId]) {
|
|
1300
|
+
all[variantId] = {
|
|
1301
|
+
...all[variantId],
|
|
1302
|
+
logs: []
|
|
1303
|
+
};
|
|
1304
|
+
mkdirSync(dirname(this.filePath), { recursive: true });
|
|
1305
|
+
writeFileSync(this.filePath, JSON.stringify(all, null, 2), "utf-8");
|
|
1306
|
+
}
|
|
1307
|
+
} catch {}
|
|
1308
|
+
}
|
|
1309
|
+
write(variantId, record) {
|
|
1310
|
+
try {
|
|
1311
|
+
const all = this.readAll();
|
|
1312
|
+
const existingLogs = all[variantId]?.logs ?? [];
|
|
1313
|
+
const newLog = {
|
|
1314
|
+
id: `${record.lastDate}-${record.lastAt}`,
|
|
1315
|
+
date: record.lastDate,
|
|
1316
|
+
timestamp: record.lastAt,
|
|
1317
|
+
status: record.status,
|
|
1318
|
+
...record.amount === void 0 ? {} : { amount: record.amount },
|
|
1319
|
+
...record.message === void 0 ? {} : { message: record.message }
|
|
1320
|
+
};
|
|
1321
|
+
const updatedLogs = [newLog, ...existingLogs.filter((l) => l.id !== newLog.id)].slice(0, 30);
|
|
1322
|
+
all[variantId] = {
|
|
1323
|
+
...record,
|
|
1324
|
+
logs: updatedLogs
|
|
1325
|
+
};
|
|
1326
|
+
mkdirSync(dirname(this.filePath), { recursive: true });
|
|
1327
|
+
writeFileSync(this.filePath, JSON.stringify(all, null, 2), "utf-8");
|
|
1328
|
+
} catch {}
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
/**
|
|
1332
|
+
* Returns the current date in YYYY-MM-DD standardized on UTC+8 (Beijing Time).
|
|
1333
|
+
*/
|
|
1334
|
+
function getUtc8DateString(nowMs = Date.now()) {
|
|
1335
|
+
const d = new Date(nowMs);
|
|
1336
|
+
const utc8 = new Date(d.getTime() + (d.getTimezoneOffset() + 480) * 6e4);
|
|
1337
|
+
return `${utc8.getFullYear()}-${String(utc8.getMonth() + 1).padStart(2, "0")}-${String(utc8.getDate()).padStart(2, "0")}`;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Calculates milliseconds until the next 10:00:05 AM in UTC+8.
|
|
1341
|
+
*/
|
|
1342
|
+
function msUntilNext10amUtc8(nowMs = Date.now()) {
|
|
1343
|
+
const d = new Date(nowMs);
|
|
1344
|
+
const utc8Time = new Date(d.getTime() + (d.getTimezoneOffset() + 480) * 6e4);
|
|
1345
|
+
const targetUtc8 = new Date(utc8Time.getTime());
|
|
1346
|
+
targetUtc8.setHours(10, 0, 5, 0);
|
|
1347
|
+
let diff = targetUtc8.getTime() - utc8Time.getTime();
|
|
1348
|
+
if (diff <= 0) {
|
|
1349
|
+
targetUtc8.setDate(targetUtc8.getDate() + 1);
|
|
1350
|
+
diff = targetUtc8.getTime() - utc8Time.getTime();
|
|
1351
|
+
}
|
|
1352
|
+
return diff;
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Checks whether catch-up is needed today:
|
|
1356
|
+
* Current time is past today's 10:00:00 AM (UTC+8) and today has not yet settled a check-in.
|
|
1357
|
+
*/
|
|
1358
|
+
function shouldCatchUp(today, lastDate, nowMs = Date.now()) {
|
|
1359
|
+
if (lastDate === today) return false;
|
|
1360
|
+
const d = new Date(nowMs);
|
|
1361
|
+
return new Date(d.getTime() + (d.getTimezoneOffset() + 480) * 6e4).getHours() >= 10;
|
|
1362
|
+
}
|
|
1363
|
+
var CheckInScheduler = class {
|
|
1364
|
+
targets;
|
|
1365
|
+
isEnabled;
|
|
1366
|
+
store;
|
|
1367
|
+
onResult;
|
|
1368
|
+
now;
|
|
1369
|
+
timer;
|
|
1370
|
+
disposed = false;
|
|
1371
|
+
constructor(options) {
|
|
1372
|
+
this.targets = options.targets;
|
|
1373
|
+
this.isEnabled = options.isEnabled;
|
|
1374
|
+
this.store = options.store ?? new JsonFileCheckInStore();
|
|
1375
|
+
this.onResult = options.onResult;
|
|
1376
|
+
this.now = options.now ?? Date.now;
|
|
1377
|
+
}
|
|
1378
|
+
start() {
|
|
1379
|
+
if (this.disposed) return;
|
|
1380
|
+
this.sweepAll(true);
|
|
1381
|
+
this.armNextTimer();
|
|
1382
|
+
}
|
|
1383
|
+
dispose() {
|
|
1384
|
+
this.disposed = true;
|
|
1385
|
+
if (this.timer) {
|
|
1386
|
+
clearTimeout(this.timer);
|
|
1387
|
+
this.timer = void 0;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
armNextTimer() {
|
|
1391
|
+
if (this.disposed) return;
|
|
1392
|
+
const delay = msUntilNext10amUtc8(this.now());
|
|
1393
|
+
this.timer = setTimeout(() => {
|
|
1394
|
+
this.sweepAll(false);
|
|
1395
|
+
this.armNextTimer();
|
|
1396
|
+
}, delay);
|
|
1397
|
+
this.timer.unref?.();
|
|
1398
|
+
}
|
|
1399
|
+
async sweepAll(isCatchUp) {
|
|
1400
|
+
if (this.disposed) return;
|
|
1401
|
+
const nowMs = this.now();
|
|
1402
|
+
const today = getUtc8DateString(nowMs);
|
|
1403
|
+
for (const target of this.targets) {
|
|
1404
|
+
if (!this.isEnabled(target.variantId)) continue;
|
|
1405
|
+
const record = this.store.read(target.variantId);
|
|
1406
|
+
if (isCatchUp && !shouldCatchUp(today, record?.lastDate, nowMs)) continue;
|
|
1407
|
+
if (!isCatchUp && record?.lastDate === today && (record.status === "claimed" || record.status === "already-claimed")) continue;
|
|
1408
|
+
let pat;
|
|
1409
|
+
try {
|
|
1410
|
+
pat = await target.getPat();
|
|
1411
|
+
} catch {
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
if (!pat) continue;
|
|
1415
|
+
try {
|
|
1416
|
+
const result = await target.service.checkIn(pat);
|
|
1417
|
+
if (result.status !== "error") {
|
|
1418
|
+
this.store.write(target.variantId, {
|
|
1419
|
+
lastDate: result.date,
|
|
1420
|
+
lastAt: result.timestamp,
|
|
1421
|
+
status: result.status,
|
|
1422
|
+
amount: result.amount,
|
|
1423
|
+
message: result.message
|
|
1424
|
+
});
|
|
1425
|
+
if (result.status === "claimed") target.onClaimed?.();
|
|
1426
|
+
}
|
|
1427
|
+
this.onResult?.(result);
|
|
1428
|
+
} catch {}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
//#endregion
|
|
1257
1433
|
//#region src/job-token-hint.ts
|
|
1258
1434
|
/** The name recorded on the hint row (and therefore its displayed title). */
|
|
1259
1435
|
const JOB_TOKEN_HINT_NAME = "qoder";
|
|
@@ -1423,6 +1599,8 @@ const MAXIMUM_CONTEXT_WINDOW_CN_FIELD = z.boolean().default(true).description("U
|
|
|
1423
1599
|
const MODEL_CONTEXT_WINDOWS_FIELD = z.dict(z.number().min(1), z.string()).description("Per-model context-window overrides for Qoder Global (model id → tokens)");
|
|
1424
1600
|
/** Sidebar quota toggle (one per variant; both live on the shared quota card). */
|
|
1425
1601
|
const QUOTA_TOGGLE_FIELD = z.boolean().default(false).description("Show this variant’s remaining-credit card in the sidebar footer (off by default)");
|
|
1602
|
+
/** Automatic check-in toggle. */
|
|
1603
|
+
const AUTO_CHECK_IN_FIELD = z.boolean().default(false).description("每天 10:00 (UTC+8) 自动签到领取算力额度(默认关闭)");
|
|
1426
1604
|
/**
|
|
1427
1605
|
* Quota poll interval: default 5 minutes, floor 1 minute. The status route
|
|
1428
1606
|
* performs a live upstream billing call per request with no cache, so an
|
|
@@ -1441,6 +1619,8 @@ const Config = z.object({
|
|
|
1441
1619
|
modelContextWindowsCN: MODEL_CONTEXT_WINDOWS_FIELD,
|
|
1442
1620
|
sidebarQuotaCN: QUOTA_TOGGLE_FIELD,
|
|
1443
1621
|
sidebarQuotaGlobal: QUOTA_TOGGLE_FIELD,
|
|
1622
|
+
autoCheckInCN: AUTO_CHECK_IN_FIELD,
|
|
1623
|
+
autoCheckInGlobal: AUTO_CHECK_IN_FIELD,
|
|
1444
1624
|
quotaPollMs: QUOTA_POLL_FIELD
|
|
1445
1625
|
});
|
|
1446
1626
|
/**
|
|
@@ -1472,6 +1652,8 @@ const GLOBAL_SECTION = z.object({
|
|
|
1472
1652
|
const QUOTA_SECTION = z.object({
|
|
1473
1653
|
sidebarQuotaCN: QUOTA_TOGGLE_FIELD,
|
|
1474
1654
|
sidebarQuotaGlobal: QUOTA_TOGGLE_FIELD,
|
|
1655
|
+
autoCheckInCN: AUTO_CHECK_IN_FIELD,
|
|
1656
|
+
autoCheckInGlobal: AUTO_CHECK_IN_FIELD,
|
|
1475
1657
|
quotaPollMs: QUOTA_POLL_FIELD
|
|
1476
1658
|
});
|
|
1477
1659
|
/** Stable identity key used by credentials, probe records, and catalog entries. */
|
|
@@ -1740,6 +1922,24 @@ function apply(ctx, config) {
|
|
|
1740
1922
|
*/
|
|
1741
1923
|
const lastIdentities = /* @__PURE__ */ new Map();
|
|
1742
1924
|
const runtimes = QODER_VARIANTS.map((variant) => createVariantRuntime(ctx, config, variant, () => current(), (id) => lastIdentities.get(id)));
|
|
1925
|
+
const checkInStore = new JsonFileCheckInStore();
|
|
1926
|
+
const checkInScheduler = new CheckInScheduler({
|
|
1927
|
+
targets: runtimes.map((runtime) => ({
|
|
1928
|
+
variantId: runtime.variant.id,
|
|
1929
|
+
service: runtime.transport.checkInService,
|
|
1930
|
+
getPat: async () => runtime.store.patPromise(),
|
|
1931
|
+
onClaimed: () => {
|
|
1932
|
+
runtime.client.fetchCredits().catch(() => void 0);
|
|
1933
|
+
}
|
|
1934
|
+
})),
|
|
1935
|
+
isEnabled: (variantId) => {
|
|
1936
|
+
const cfg = current();
|
|
1937
|
+
if (variantId === CHINA_VARIANT.id) return cfg.autoCheckInCN === true;
|
|
1938
|
+
return cfg.autoCheckInGlobal === true;
|
|
1939
|
+
},
|
|
1940
|
+
store: checkInStore
|
|
1941
|
+
});
|
|
1942
|
+
checkInScheduler.start();
|
|
1743
1943
|
installJobTokenHint(ctx);
|
|
1744
1944
|
const probeKey = createProbeKey();
|
|
1745
1945
|
/**
|
|
@@ -1823,7 +2023,8 @@ function apply(ctx, config) {
|
|
|
1823
2023
|
probeKey,
|
|
1824
2024
|
authKey,
|
|
1825
2025
|
...runtime.variant.id === CHINA_VARIANT.id ? { useMaximumContextWindow: () => current().useMaximumContextWindowCN === true } : { useMaximumContextWindow: () => current().useMaximumContextWindow === true },
|
|
1826
|
-
jobTokenRefreshedAt: runtime.jobTokenRefreshedAt
|
|
2026
|
+
jobTokenRefreshedAt: runtime.jobTokenRefreshedAt,
|
|
2027
|
+
checkIn: () => checkInStore.read(runtime.variant.id)
|
|
1827
2028
|
});
|
|
1828
2029
|
registerQoderAuthRoute(webCtx, {
|
|
1829
2030
|
path: runtime.variant.authPath,
|
|
@@ -1886,6 +2087,36 @@ function apply(ctx, config) {
|
|
|
1886
2087
|
reason: runtime.catalogError
|
|
1887
2088
|
};
|
|
1888
2089
|
},
|
|
2090
|
+
clearCheckInLogs: () => {
|
|
2091
|
+
checkInStore.clearLogs(runtime.variant.id);
|
|
2092
|
+
},
|
|
2093
|
+
checkIn: async () => {
|
|
2094
|
+
let pat;
|
|
2095
|
+
try {
|
|
2096
|
+
pat = await runtime.store.patPromise();
|
|
2097
|
+
} catch {
|
|
2098
|
+
return {
|
|
2099
|
+
state: "failed",
|
|
2100
|
+
reason: "No PAT available"
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
const result = await runtime.transport.checkInService.checkIn(pat);
|
|
2104
|
+
if (result.status !== "error") {
|
|
2105
|
+
checkInStore.write(runtime.variant.id, {
|
|
2106
|
+
lastDate: result.date,
|
|
2107
|
+
lastAt: result.timestamp,
|
|
2108
|
+
status: result.status,
|
|
2109
|
+
...result.amount === void 0 ? {} : { amount: result.amount },
|
|
2110
|
+
...result.message === void 0 ? {} : { message: result.message }
|
|
2111
|
+
});
|
|
2112
|
+
if (result.status === "claimed") runtime.client.fetchCredits().catch(() => void 0);
|
|
2113
|
+
}
|
|
2114
|
+
return {
|
|
2115
|
+
state: result.status,
|
|
2116
|
+
...result.amount === void 0 ? {} : { amount: result.amount },
|
|
2117
|
+
...result.message === void 0 ? {} : { reason: result.message }
|
|
2118
|
+
};
|
|
2119
|
+
},
|
|
1889
2120
|
...runtime.variant.id === CHINA_VARIANT.id ? { setMaximumContextWindow: async (enabled) => {
|
|
1890
2121
|
if (setMaximumContextWindowCN === void 0) return {
|
|
1891
2122
|
state: "failed",
|
|
@@ -1966,6 +2197,7 @@ function apply(ctx, config) {
|
|
|
1966
2197
|
});
|
|
1967
2198
|
ctx.effect(() => () => {
|
|
1968
2199
|
stopped = true;
|
|
2200
|
+
checkInScheduler.dispose();
|
|
1969
2201
|
for (const timer of timers) clearInterval(timer);
|
|
1970
2202
|
timers.length = 0;
|
|
1971
2203
|
detach(ctx, clearHostHeartbeat(), "host heartbeat cleanup");
|
|
@@ -876,6 +876,14 @@ function getQoderUserStatusUrl(region = "global") {
|
|
|
876
876
|
const { openApiUrl } = resolveQoderEndpoints(region);
|
|
877
877
|
return `${openApiUrl}/api/v3/user/status`;
|
|
878
878
|
}
|
|
879
|
+
function getQoderCampaignsUrl(region = "global") {
|
|
880
|
+
const { openApiUrl } = resolveQoderEndpoints(region);
|
|
881
|
+
return `${openApiUrl}/sash/api/v1/me/campaigns`;
|
|
882
|
+
}
|
|
883
|
+
function getQoderClaimCampaignUrl(region = "global", campaignId) {
|
|
884
|
+
const { openApiUrl } = resolveQoderEndpoints(region);
|
|
885
|
+
return `${openApiUrl}/sash/api/v1/me/campaigns/${encodeURIComponent(campaignId)}/claim`;
|
|
886
|
+
}
|
|
879
887
|
//#endregion
|
|
880
888
|
//#region src/qoder/errors.ts
|
|
881
889
|
/**
|
|
@@ -2408,6 +2416,131 @@ var QoderUsageReader = class {
|
|
|
2408
2416
|
}
|
|
2409
2417
|
};
|
|
2410
2418
|
//#endregion
|
|
2419
|
+
//#region src/qoder/transport/checkin.ts
|
|
2420
|
+
const defaultCheckInTimeoutMs = 15e3;
|
|
2421
|
+
var QoderCheckInService = class {
|
|
2422
|
+
authService;
|
|
2423
|
+
fetchImpl;
|
|
2424
|
+
region;
|
|
2425
|
+
variantId;
|
|
2426
|
+
logger;
|
|
2427
|
+
timeoutMs;
|
|
2428
|
+
constructor(options) {
|
|
2429
|
+
this.authService = options.authService;
|
|
2430
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
2431
|
+
this.region = options.region ?? "china";
|
|
2432
|
+
this.variantId = options.variantId ?? "qoder";
|
|
2433
|
+
this.logger = options.logger;
|
|
2434
|
+
this.timeoutMs = options.timeoutMs ?? defaultCheckInTimeoutMs;
|
|
2435
|
+
}
|
|
2436
|
+
async fetchCampaigns(token, signal) {
|
|
2437
|
+
const url = getQoderCampaignsUrl(this.region);
|
|
2438
|
+
const data = await openApiJsonRequest(this.fetchImpl, {
|
|
2439
|
+
url,
|
|
2440
|
+
token,
|
|
2441
|
+
signal,
|
|
2442
|
+
timeoutMs: this.timeoutMs,
|
|
2443
|
+
logger: this.logger,
|
|
2444
|
+
operation: "Campaigns",
|
|
2445
|
+
logCategory: "campaigns.list"
|
|
2446
|
+
});
|
|
2447
|
+
return Array.isArray(data?.campaigns) ? data.campaigns : [];
|
|
2448
|
+
}
|
|
2449
|
+
async claimCampaign(token, campaignId, signal) {
|
|
2450
|
+
const url = getQoderClaimCampaignUrl(this.region, campaignId);
|
|
2451
|
+
const { openApiUrl } = resolveQoderEndpoints(this.region);
|
|
2452
|
+
return openApiJsonRequest(this.fetchImpl, {
|
|
2453
|
+
url,
|
|
2454
|
+
method: "POST",
|
|
2455
|
+
token,
|
|
2456
|
+
headers: { origin: openApiUrl },
|
|
2457
|
+
signal,
|
|
2458
|
+
timeoutMs: this.timeoutMs,
|
|
2459
|
+
logger: this.logger,
|
|
2460
|
+
operation: "ClaimCampaign",
|
|
2461
|
+
logCategory: "campaigns.claim"
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
getTodayDateString() {
|
|
2465
|
+
const now = /* @__PURE__ */ new Date();
|
|
2466
|
+
const utc8Time = new Date(now.getTime() + (now.getTimezoneOffset() + 480) * 6e4);
|
|
2467
|
+
return `${utc8Time.getFullYear()}-${String(utc8Time.getMonth() + 1).padStart(2, "0")}-${String(utc8Time.getDate()).padStart(2, "0")}`;
|
|
2468
|
+
}
|
|
2469
|
+
async checkIn(pat, signal) {
|
|
2470
|
+
const today = this.getTodayDateString();
|
|
2471
|
+
const nowMs = Date.now();
|
|
2472
|
+
if (!pat || typeof pat !== "string" || pat.trim() === "") return {
|
|
2473
|
+
variantId: this.variantId,
|
|
2474
|
+
date: today,
|
|
2475
|
+
timestamp: nowMs,
|
|
2476
|
+
status: "error",
|
|
2477
|
+
message: "No PAT available"
|
|
2478
|
+
};
|
|
2479
|
+
const executeWithToken = async (authToken) => {
|
|
2480
|
+
const benefitCampaign = (await this.fetchCampaigns(authToken, signal)).find((c) => c.actionType === "CLAIM_BENEFIT");
|
|
2481
|
+
if (!benefitCampaign) return {
|
|
2482
|
+
variantId: this.variantId,
|
|
2483
|
+
date: today,
|
|
2484
|
+
timestamp: nowMs,
|
|
2485
|
+
status: "no-campaign",
|
|
2486
|
+
message: "No claimable benefit campaign found for this region"
|
|
2487
|
+
};
|
|
2488
|
+
if (benefitCampaign.claimStatus === "CLAIMED") return {
|
|
2489
|
+
variantId: this.variantId,
|
|
2490
|
+
date: today,
|
|
2491
|
+
timestamp: nowMs,
|
|
2492
|
+
status: "already-claimed",
|
|
2493
|
+
campaignKey: benefitCampaign.campaignKey,
|
|
2494
|
+
amount: benefitCampaign.benefit?.amount ?? 100,
|
|
2495
|
+
message: "Already claimed today"
|
|
2496
|
+
};
|
|
2497
|
+
const claimResult = await this.claimCampaign(authToken, benefitCampaign.campaignId, signal);
|
|
2498
|
+
const isClaimed = claimResult.status === "CLAIMED";
|
|
2499
|
+
const replayed = Boolean(claimResult.replayed);
|
|
2500
|
+
const amount = claimResult.benefit?.amount ?? benefitCampaign.benefit?.amount ?? 100;
|
|
2501
|
+
if (isClaimed) return {
|
|
2502
|
+
variantId: this.variantId,
|
|
2503
|
+
date: today,
|
|
2504
|
+
timestamp: nowMs,
|
|
2505
|
+
status: replayed ? "already-claimed" : "claimed",
|
|
2506
|
+
amount,
|
|
2507
|
+
campaignKey: benefitCampaign.campaignKey,
|
|
2508
|
+
message: replayed ? "Already claimed today" : `Successfully claimed ${amount} credits`
|
|
2509
|
+
};
|
|
2510
|
+
return {
|
|
2511
|
+
variantId: this.variantId,
|
|
2512
|
+
date: today,
|
|
2513
|
+
timestamp: nowMs,
|
|
2514
|
+
status: "error",
|
|
2515
|
+
campaignKey: benefitCampaign.campaignKey,
|
|
2516
|
+
message: `Claim returned status ${claimResult.status ?? "unknown"}`
|
|
2517
|
+
};
|
|
2518
|
+
};
|
|
2519
|
+
try {
|
|
2520
|
+
const creds = await this.authService.getCredentials(pat, signal);
|
|
2521
|
+
try {
|
|
2522
|
+
return await executeWithToken(creds.authToken);
|
|
2523
|
+
} catch (innerError) {
|
|
2524
|
+
if (isQoderAuthRejection(innerError)) {
|
|
2525
|
+
this.logger?.warn?.(`[Qoder CheckIn] Auth token rejected, retrying with fresh exchange`);
|
|
2526
|
+
return await executeWithToken((await this.authService.exchangeFresh(pat, signal)).authToken);
|
|
2527
|
+
}
|
|
2528
|
+
throw innerError;
|
|
2529
|
+
}
|
|
2530
|
+
} catch (error) {
|
|
2531
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2532
|
+
this.logger?.error?.(`[Qoder CheckIn] Check-in failed: ${message}`);
|
|
2533
|
+
return {
|
|
2534
|
+
variantId: this.variantId,
|
|
2535
|
+
date: today,
|
|
2536
|
+
timestamp: nowMs,
|
|
2537
|
+
status: "error",
|
|
2538
|
+
message
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
};
|
|
2543
|
+
//#endregion
|
|
2411
2544
|
//#region src/qoder/transport/wire/encoding.ts
|
|
2412
2545
|
/**
|
|
2413
2546
|
* Qoder WAF Body encoding algorithm.
|
|
@@ -3058,6 +3191,7 @@ var DefaultQoderTransport = class {
|
|
|
3058
3191
|
metadataTimeoutMs;
|
|
3059
3192
|
auth;
|
|
3060
3193
|
usage;
|
|
3194
|
+
checkInService;
|
|
3061
3195
|
modelFlights = new SingleFlight();
|
|
3062
3196
|
attachments;
|
|
3063
3197
|
imageUploader;
|
|
@@ -3119,6 +3253,13 @@ var DefaultQoderTransport = class {
|
|
|
3119
3253
|
region: this.region,
|
|
3120
3254
|
timeoutMs: this.metadataTimeoutMs
|
|
3121
3255
|
});
|
|
3256
|
+
this.checkInService = new QoderCheckInService({
|
|
3257
|
+
authService: this.auth,
|
|
3258
|
+
fetch: this.fetchImpl,
|
|
3259
|
+
logger: this.logger,
|
|
3260
|
+
region: this.region,
|
|
3261
|
+
timeoutMs: this.metadataTimeoutMs
|
|
3262
|
+
});
|
|
3122
3263
|
}
|
|
3123
3264
|
stream(options, model) {
|
|
3124
3265
|
return this.generate(options, model);
|
|
@@ -3184,6 +3325,10 @@ var DefaultQoderTransport = class {
|
|
|
3184
3325
|
signal: options?.signal
|
|
3185
3326
|
});
|
|
3186
3327
|
}
|
|
3328
|
+
async checkIn(signal) {
|
|
3329
|
+
const pat = await this.requirePat(signal);
|
|
3330
|
+
return this.checkInService.checkIn(pat, signal);
|
|
3331
|
+
}
|
|
3187
3332
|
async requirePat(signal) {
|
|
3188
3333
|
if (signal?.aborted) throw aborted("Qoder request was aborted.");
|
|
3189
3334
|
const pat = (await this.resolvePat()).trim();
|
|
@@ -3848,6 +3993,12 @@ var QoderUpstreamClient = class {
|
|
|
3848
3993
|
};
|
|
3849
3994
|
}
|
|
3850
3995
|
/**
|
|
3996
|
+
* Run daily benefit check-in for this client's variant.
|
|
3997
|
+
*/
|
|
3998
|
+
async checkIn(signal) {
|
|
3999
|
+
return this.transport.checkIn(signal);
|
|
4000
|
+
}
|
|
4001
|
+
/**
|
|
3851
4002
|
* Stream one translated chat-completions request.
|
|
3852
4003
|
*
|
|
3853
4004
|
* The first transport chunk is pulled before the response is constructed:
|
|
@@ -4082,7 +4233,7 @@ const DEFAULT_MAX_TOKENS = 32768;
|
|
|
4082
4233
|
const DEFAULT_CONTEXT_WINDOW = 18e4;
|
|
4083
4234
|
//#endregion
|
|
4084
4235
|
//#region src/version.ts
|
|
4085
|
-
const QODER_CONNECT_VERSION = "0.1.
|
|
4236
|
+
const QODER_CONNECT_VERSION = "0.1.5";
|
|
4086
4237
|
//#endregion
|
|
4087
4238
|
//#region src/host-heartbeat.ts
|
|
4088
4239
|
/**
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-qoder-connect",
|
|
3
3
|
"displayName": "DSH Qoder Connect",
|
|
4
4
|
"description": "将 Qoder(国内版 / 国际版)的模型接入 DeepSeek Harness — bring Qoder (CN & international) models into DeepSeek Harness.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.5",
|
|
6
6
|
"author": "corrinehu",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"dsh-plugin",
|