dsh-xdpool 2.6.1 → 2.6.3
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/CHANGELOG.md +110 -6
- package/lib/bin.js +271 -18
- package/lib/client.js +1496 -33
- package/lib/index.d.ts +86 -2
- package/lib/index.js +874 -20
- package/package.json +1 -1
- package/vendor/channel-pack/pack.js +91 -2
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-xdpool",
|
|
3
3
|
"displayName": "DSH XD Pool",
|
|
4
4
|
"description": "One DSH model pool for every free quota you have: merge all locally signed-in WorkBuddy accounts with auto-failover, sign in to 13 vendors by QR/web, aggregate 76 vendors' free tiers, plus a usage dashboard, model routing, a local OpenAI-compatible gateway and a points-automation suite.",
|
|
5
|
-
"version": "2.6.
|
|
5
|
+
"version": "2.6.3",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "XDTrees",
|
|
8
8
|
"homepage": "https://github.com/XDTrees/dsh-xdpool#readme",
|
|
@@ -1221,7 +1221,22 @@ async function requestToken(body, keyPair, fetcher = fetch) {
|
|
|
1221
1221
|
if (!response.ok || !data?.credentials) {
|
|
1222
1222
|
const message2 = `CodeArts token request failed: ${response.status}${data ? ` ${JSON.stringify(data)}` : ""}`;
|
|
1223
1223
|
const errorCode = String(data?.error_code ?? "");
|
|
1224
|
-
if (data?.error === "invalid_grant" || errorCode.includes("ExpiredRefreshToken")
|
|
1224
|
+
if (data?.error === "invalid_grant" || errorCode.includes("ExpiredRefreshToken")
|
|
1225
|
+
// [xdpool-local] a consumed refresh token is a dead credential.
|
|
1226
|
+
//
|
|
1227
|
+
// Huawei STS refresh tokens are SINGLE-USE: presenting one returns a
|
|
1228
|
+
// new one, and the old value is marked used. "Has been used" is
|
|
1229
|
+
// therefore terminal — either two refreshes raced and this is the
|
|
1230
|
+
// loser, or a successful refresh failed to persist its replacement.
|
|
1231
|
+
// Reporting it as an ordinary error left the account in the pool, so
|
|
1232
|
+
// every later request paid for another hopeless refresh and the user
|
|
1233
|
+
// saw an opaque 400 instead of "sign in again".
|
|
1234
|
+
//
|
|
1235
|
+
// Scoped here rather than in the shared predicate on purpose:
|
|
1236
|
+
// STS5.* is Huawei's namespace, and another vendor could reuse a
|
|
1237
|
+
// similar code for something recoverable.
|
|
1238
|
+
|| errorCode.includes("STS5.1806")
|
|
1239
|
+
|| /refresh token has been used/iu.test(String(data?.error_msg ?? ""))) {
|
|
1225
1240
|
throw new RefreshTokenExpiredError(message2);
|
|
1226
1241
|
}
|
|
1227
1242
|
throw new Error(message2);
|
|
@@ -58010,6 +58025,21 @@ function parseCreditBalance(metrics) {
|
|
|
58010
58025
|
);
|
|
58011
58026
|
return { total, packages, expiredTotal: 0 };
|
|
58012
58027
|
}
|
|
58028
|
+
// [xdpool-local] Whether this CodeArts credential's own STS expiry has passed.
|
|
58029
|
+
//
|
|
58030
|
+
// Huawei mints these AK/SK/security-token triples with a two-hour life, so a
|
|
58031
|
+
// stored credential very often IS expired while its refresh_token still has
|
|
58032
|
+
// weeks left. The gateway answers that with
|
|
58033
|
+
// `APIG.0602 Bad request: the security token has expired` — accurate, but it
|
|
58034
|
+
// reads like a sign-in failure and sends people off to re-scan a QR code they
|
|
58035
|
+
// never needed to re-scan. Callers use this to name the condition, and to
|
|
58036
|
+
// decide whether a re-mint is worth trying before reporting it at all.
|
|
58037
|
+
function codeartsCredentialExpired(credential) {
|
|
58038
|
+
const raw = credential === null || credential === void 0 ? "" : credential.expires_at;
|
|
58039
|
+
if (typeof raw !== "string" || raw.length === 0) return false;
|
|
58040
|
+
const parsed = Date.parse(raw);
|
|
58041
|
+
return Number.isFinite(parsed) ? parsed <= Date.now() : false;
|
|
58042
|
+
}
|
|
58013
58043
|
async function fetchCodeArtsAccountInfoDetailed(credential, fetcher = fetch) {
|
|
58014
58044
|
const url = `${CODEARTS_SNAP_ENGINE_URL}${CODEARTS_PACKAGE_INFO_PATH}`;
|
|
58015
58045
|
const result = await signedSnapRequest("GET", url, credential, void 0, fetcher);
|
|
@@ -60762,6 +60792,34 @@ async function collectClaimResults(accounts, product, deps) {
|
|
|
60762
60792
|
}
|
|
60763
60793
|
return { results, summary: computeClaimSummary(outcomes) };
|
|
60764
60794
|
}
|
|
60795
|
+
// [xdpool-local] Re-mint any CodeArts credential whose own STS expiry has
|
|
60796
|
+
// already passed, before the caller reports it as a failure.
|
|
60797
|
+
//
|
|
60798
|
+
// Best-effort by design: every failure is logged and swallowed. A refresh that
|
|
60799
|
+
// was rejected (`invalid_grant` — the token was rotated away by another client)
|
|
60800
|
+
// must still let the caller run, because the resulting balance error is the only
|
|
60801
|
+
// place that can tell the user to sign in again.
|
|
60802
|
+
//
|
|
60803
|
+
// It goes through `refreshAccountCredential`, which is the same per-ref serial
|
|
60804
|
+
// queue the 30-minute scheduler uses, so an on-demand heal can never race the
|
|
60805
|
+
// scheduler into consuming one refresh_token twice.
|
|
60806
|
+
async function healExpiredCodeArtsCredentials(accounts, pool, auth, resolve, warn) {
|
|
60807
|
+
if (auth === void 0 || pool === void 0) return;
|
|
60808
|
+
for (const entry of accounts) {
|
|
60809
|
+
if (entry.enabled === false) continue;
|
|
60810
|
+
try {
|
|
60811
|
+
const resolved = await resolve(credentialRef13(entry.credentialRef));
|
|
60812
|
+
if (resolved === void 0) continue;
|
|
60813
|
+
const credential = JSON.parse(resolved.value);
|
|
60814
|
+
if (!codeartsCredentialExpired(credential)) continue;
|
|
60815
|
+
if (!isCodeArtsRefreshable(credential)) continue;
|
|
60816
|
+
warn?.(`[channel-pack] codearts 账号 ${entry.id} 的临时凭证已过期,先自动续期`);
|
|
60817
|
+
await auth.refreshAccountCredential(entry.credentialRef, pool, entry.id);
|
|
60818
|
+
} catch (error) {
|
|
60819
|
+
warn?.(`[channel-pack] codearts 账号 ${entry.id} 自动续期失败(仍继续查询):${String(error)}`);
|
|
60820
|
+
}
|
|
60821
|
+
}
|
|
60822
|
+
}
|
|
60765
60823
|
async function collectCreditBalances(accounts, product, deps) {
|
|
60766
60824
|
const fetcher = deps.fetcher ?? fetch;
|
|
60767
60825
|
const fetchBalance = deps.fetchBalance ?? (async (credential, product2) => fetchCreditBalance(
|
|
@@ -61116,6 +61174,30 @@ function registerChannelPackEndpoints(ctx, pool, codearts, buddy, workbuddy, lob
|
|
|
61116
61174
|
};
|
|
61117
61175
|
}
|
|
61118
61176
|
async function handleMethod(method, payload, _signal) {
|
|
61177
|
+
// [xdpool-local] One heal for every CodeArts entry point, before the switch.
|
|
61178
|
+
//
|
|
61179
|
+
// A CodeArts credential lives two hours; its refresh_token behind it lives
|
|
61180
|
+
// ~30 days. Three separate RPC families read that credential and each of
|
|
61181
|
+
// them can be the FIRST call after it has run out: `credits.balances` (the
|
|
61182
|
+
// panel opening), `credits.status` and `credits.claimAll` (the 09:00 daily
|
|
61183
|
+
// check-in, which then fails silently in a scheduler log nobody reads).
|
|
61184
|
+
// Patching one branch would have left the other two reporting a dead
|
|
61185
|
+
// sign-in for a credential that one refresh fixes, so the heal sits here,
|
|
61186
|
+
// on the path they all share. It is idempotent and cheap when healthy:
|
|
61187
|
+
// nothing is refreshed unless the credential's own stamp is already past.
|
|
61188
|
+
if (typeof payload === "object" && payload !== null && payload.provider === "codearts") {
|
|
61189
|
+
try {
|
|
61190
|
+
await healExpiredCodeArtsCredentials(
|
|
61191
|
+
await pool.listAccounts("codearts"),
|
|
61192
|
+
pool,
|
|
61193
|
+
codearts,
|
|
61194
|
+
(ref) => ctx.credentials.resolve(ref),
|
|
61195
|
+
(msg) => ctx.logger?.warn?.(msg)
|
|
61196
|
+
);
|
|
61197
|
+
} catch (error) {
|
|
61198
|
+
ctx.logger?.warn?.(`[channel-pack] codearts 凭证对账失败(继续处理该请求):${String(error)}`);
|
|
61199
|
+
}
|
|
61200
|
+
}
|
|
61119
61201
|
if (method.startsWith("opencode.")) {
|
|
61120
61202
|
if (config.disableOpencode === true) {
|
|
61121
61203
|
return { ok: false, error: { code: "provider-disabled", message: "OpenCode account integration is disabled in this host" } };
|
|
@@ -62219,7 +62301,14 @@ function registerChannelPackEndpoints(ctx, pool, codearts, buddy, workbuddy, lob
|
|
|
62219
62301
|
resolve: (ref) => ctx.credentials.resolve(ref),
|
|
62220
62302
|
fetchBalanceDetailed: async (credential) => {
|
|
62221
62303
|
const result = await fetchCodeArtsAccountInfoDetailed(credential);
|
|
62222
|
-
if (!result.ok)
|
|
62304
|
+
if (!result.ok) {
|
|
62305
|
+
// [xdpool-local] An expired STS token is the commonest CodeArts
|
|
62306
|
+
// failure, and the raw APIG message does not say what to do about
|
|
62307
|
+
// it. Name the condition, then name the one action that fixes it.
|
|
62308
|
+
// Only when it IS expired: a 5xx keeps its original wording.
|
|
62309
|
+
const advice = codeartsCredentialExpired(credential) ? "\uFF08\u4E34\u65F6\u51ED\u8BC1\u5DF2\u8FC7\u671F\uFF0C\u70B9\u300C\u5237\u65B0\u51ED\u636E\u300D\u5373\u53EF\u6362\u65B0\uFF0C\u65E0\u9700\u91CD\u65B0\u767B\u5F55\uFF09" : "";
|
|
62310
|
+
return { balance: null, error: `\u8D26\u6237\u4FE1\u606F\u67E5\u8BE2\u5931\u8D25${advice}\uFF1A${result.message}` };
|
|
62311
|
+
}
|
|
62223
62312
|
const info = result.info;
|
|
62224
62313
|
if (!info.isCreditPackage) {
|
|
62225
62314
|
return {
|