dsh-codearts 1.2.1 → 1.2.2
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 +15 -0
- package/lib/bin.mjs +2 -2
- package/lib/client/index.js +53 -53
- package/lib/index.d.mts +8 -1
- package/lib/index.mjs +38 -3
- package/lib/{usage-05PgtUIP.mjs → usage-DztQaswb.mjs} +89 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
本文件记录本插件的重要改动。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
4
4
|
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
5
5
|
|
|
6
|
+
## [1.2.2] - 2026-11-02
|
|
7
|
+
|
|
8
|
+
### 修复
|
|
9
|
+
|
|
10
|
+
- **`invalid_iam_token` 只会干等**:这类错误以**流内首帧**(HTTP 200)回来,既绕过
|
|
11
|
+
错误分类、也不会触发换号;而 `ensureFresh()` 只在距 `expiration` 不足 5 分钟时才
|
|
12
|
+
续期,token 被提前作废(另一个客户端续期 / 上游轮换)后每条消息都会撞同一堵墙。
|
|
13
|
+
现在首帧识别 `invalid_iam_token` / `Incorrect IAM authentication` / `APIG.0301`
|
|
14
|
+
等措辞,**强制续期一次**并让同一个账号重试;续期仍失败就冷却该账号,不再把
|
|
15
|
+
原始英文串甩给用户。HTTP 400 响应体里的同类错误走同一分支。
|
|
16
|
+
- **上游流内的「模型不存在 / 无权限」和「不吃图」都翻成人话**:
|
|
17
|
+
`InferHub.ModelArts.81003.401` 与 `InferHub.001001020.406` 以前会原样透传,
|
|
18
|
+
用户只看到两句英文。现在分别提示「模型不存在或当前账号没有权限(换号没用)」
|
|
19
|
+
与「该模型不支持图片输入」。识图能力本身仍以上游发现结果为准,不做本地覆盖。
|
|
20
|
+
|
|
6
21
|
## [1.2.1] - 2026-10-28
|
|
7
22
|
|
|
8
23
|
### 新增
|
package/lib/bin.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { B as codeartsAccountsPath, I as isBenefitModel, L as CodeArtsAccountStore, d as fetchCallerIdentity, n as codeartsUsagePath, r as discoverModels, t as CodeArtsUsageTracker } from "./usage-DztQaswb.mjs";
|
|
3
3
|
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
//#region src/version.ts
|
|
5
|
-
const CODEARTS_VERSION = "1.2.
|
|
5
|
+
const CODEARTS_VERSION = "1.2.2";
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/bin.ts
|
|
8
8
|
/**
|
package/lib/client/index.js
CHANGED
|
@@ -33,70 +33,70 @@ if (typeof document !== "undefined" && document.querySelector("style[data-plugin
|
|
|
33
33
|
document.head.appendChild(tag);
|
|
34
34
|
}
|
|
35
35
|
var CodeArtsPluginCard_module_css_default = {
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"field": "xoziZG_field",
|
|
39
|
-
"statusLine": "xoziZG_statusLine",
|
|
40
|
-
"meta": "xoziZG_meta",
|
|
41
|
-
"manualForm": "xoziZG_manualForm",
|
|
42
|
-
"usageBar": "xoziZG_usageBar",
|
|
43
|
-
"check": "xoziZG_check",
|
|
44
|
-
"tabs": "xoziZG_tabs",
|
|
45
|
-
"modelTrack": "xoziZG_modelTrack",
|
|
46
|
-
"modelFill": "xoziZG_modelFill",
|
|
47
|
-
"balance": "xoziZG_balance",
|
|
48
|
-
"creditLink": "xoziZG_creditLink",
|
|
49
|
-
"actions": "xoziZG_actions",
|
|
50
|
-
"accountName": "xoziZG_accountName",
|
|
51
|
-
"area": "xoziZG_area",
|
|
52
|
-
"sectionHead": "xoziZG_sectionHead",
|
|
53
|
-
"accountHint": "xoziZG_accountHint",
|
|
54
|
-
"expiryRow": "xoziZG_expiryRow",
|
|
36
|
+
"accountToolbar": "xoziZG_accountToolbar",
|
|
37
|
+
"choices": "xoziZG_choices",
|
|
55
38
|
"bar": "xoziZG_bar",
|
|
39
|
+
"modelTokens": "xoziZG_modelTokens",
|
|
40
|
+
"modelRequests": "xoziZG_modelRequests",
|
|
41
|
+
"tabPanel": "xoziZG_tabPanel",
|
|
56
42
|
"accountCard": "xoziZG_accountCard",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"link": "xoziZG_link",
|
|
62
|
-
"manual": "xoziZG_manual",
|
|
63
|
-
"accountNameRow": "xoziZG_accountNameRow",
|
|
64
|
-
"fieldLabel": "xoziZG_fieldLabel",
|
|
65
|
-
"creditFooter": "xoziZG_creditFooter",
|
|
43
|
+
"check": "xoziZG_check",
|
|
44
|
+
"sectionHead": "xoziZG_sectionHead",
|
|
45
|
+
"hint": "xoziZG_hint",
|
|
46
|
+
"modelShare": "xoziZG_modelShare",
|
|
66
47
|
"creditBlock": "xoziZG_creditBlock",
|
|
67
|
-
"
|
|
68
|
-
"creditRow": "xoziZG_creditRow",
|
|
69
|
-
"modelRow": "xoziZG_modelRow",
|
|
48
|
+
"manualForm": "xoziZG_manualForm",
|
|
70
49
|
"updated": "xoziZG_updated",
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
50
|
+
"accountRowActions": "xoziZG_accountRowActions",
|
|
51
|
+
"name": "xoziZG_name",
|
|
52
|
+
"creditRows": "xoziZG_creditRows",
|
|
53
|
+
"usageFill": "xoziZG_usageFill",
|
|
54
|
+
"modelName": "xoziZG_modelName",
|
|
55
|
+
"statusLine": "xoziZG_statusLine",
|
|
74
56
|
"count": "xoziZG_count",
|
|
57
|
+
"modelFill": "xoziZG_modelFill",
|
|
75
58
|
"accountInfo": "xoziZG_accountInfo",
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
59
|
+
"avatar": "xoziZG_avatar",
|
|
60
|
+
"headText": "xoziZG_headText",
|
|
61
|
+
"card": "xoziZG_card",
|
|
62
|
+
"expiryLabel": "xoziZG_expiryLabel",
|
|
63
|
+
"sectionLabel": "xoziZG_sectionLabel",
|
|
64
|
+
"desc": "xoziZG_desc",
|
|
79
65
|
"body": "xoziZG_body",
|
|
80
66
|
"claimAllRow": "xoziZG_claimAllRow",
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"tabPanel": "xoziZG_tabPanel",
|
|
84
|
-
"desc": "xoziZG_desc",
|
|
85
|
-
"card": "xoziZG_card",
|
|
67
|
+
"modelTrack": "xoziZG_modelTrack",
|
|
68
|
+
"manual": "xoziZG_manual",
|
|
86
69
|
"accountEntry": "xoziZG_accountEntry",
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
70
|
+
"creditLink": "xoziZG_creditLink",
|
|
71
|
+
"accountRow": "xoziZG_accountRow",
|
|
72
|
+
"creditHint": "xoziZG_creditHint",
|
|
73
|
+
"header": "xoziZG_header",
|
|
74
|
+
"accountName": "xoziZG_accountName",
|
|
75
|
+
"modelChart": "xoziZG_modelChart",
|
|
76
|
+
"creditFooter": "xoziZG_creditFooter",
|
|
90
77
|
"section": "xoziZG_section",
|
|
91
|
-
"
|
|
92
|
-
"name": "xoziZG_name",
|
|
93
|
-
"creditRows": "xoziZG_creditRows",
|
|
94
|
-
"modelShare": "xoziZG_modelShare",
|
|
95
|
-
"modelRequests": "xoziZG_modelRequests",
|
|
96
|
-
"choices": "xoziZG_choices",
|
|
78
|
+
"fieldLabel": "xoziZG_fieldLabel",
|
|
97
79
|
"manualSummary": "xoziZG_manualSummary",
|
|
98
|
-
"
|
|
99
|
-
"
|
|
80
|
+
"modelRow": "xoziZG_modelRow",
|
|
81
|
+
"accountHint": "xoziZG_accountHint",
|
|
82
|
+
"expiryRow": "xoziZG_expiryRow",
|
|
83
|
+
"error": "xoziZG_error",
|
|
84
|
+
"actions": "xoziZG_actions",
|
|
85
|
+
"meta": "xoziZG_meta",
|
|
86
|
+
"balanceRow": "xoziZG_balanceRow",
|
|
87
|
+
"tabs": "xoziZG_tabs",
|
|
88
|
+
"link": "xoziZG_link",
|
|
89
|
+
"choiceRow": "xoziZG_choiceRow",
|
|
90
|
+
"balanceUnit": "xoziZG_balanceUnit",
|
|
91
|
+
"creditRow": "xoziZG_creditRow",
|
|
92
|
+
"field": "xoziZG_field",
|
|
93
|
+
"accountNameRow": "xoziZG_accountNameRow",
|
|
94
|
+
"balance": "xoziZG_balance",
|
|
95
|
+
"usageBar": "xoziZG_usageBar",
|
|
96
|
+
"balanceMeta": "xoziZG_balanceMeta",
|
|
97
|
+
"barFill": "xoziZG_barFill",
|
|
98
|
+
"area": "xoziZG_area",
|
|
99
|
+
"danger": "xoziZG_danger"
|
|
100
100
|
};
|
|
101
101
|
//#endregion
|
|
102
102
|
//#region src/client/CodeArtsPluginCard.tsx
|
package/lib/index.d.mts
CHANGED
|
@@ -870,7 +870,7 @@ interface OAuthToken {
|
|
|
870
870
|
//#endregion
|
|
871
871
|
//#region src/upstream.d.ts
|
|
872
872
|
declare const SNAP_HOST = "https://snap-access.cn-north-4.myhuaweicloud.com";
|
|
873
|
-
type UpstreamErrorKind = 'hard_credit' | 'soft_rate' | 'session_dead' | 'not_found' | 'server' | 'client';
|
|
873
|
+
type UpstreamErrorKind = 'hard_credit' | 'soft_rate' | 'session_dead' | 'iam_stale' | 'not_found' | 'server' | 'client';
|
|
874
874
|
interface ChatResultOk {
|
|
875
875
|
ok: true;
|
|
876
876
|
/** 已转成 OpenAI 增量 SSE 的 ReadableStream(文本,data: ...\n\n)。 */
|
|
@@ -1117,6 +1117,13 @@ declare class CodeArtsAccountStore {
|
|
|
1117
1117
|
clear(): Promise<void>;
|
|
1118
1118
|
/** 给 shim 用:解析某账号的签名材料(内部含续期)。 */
|
|
1119
1119
|
resolve(id: string): Promise<ResolvedCredential | undefined>;
|
|
1120
|
+
/**
|
|
1121
|
+
* 上游报 IAM token 无效时强制续期。
|
|
1122
|
+
*
|
|
1123
|
+
* `seenToken` 是这次请求实际用的 token:若别的请求已经换过凭证,就别再刷一次
|
|
1124
|
+
* (refresh_token 会轮换,白刷一次就多一次失败风险)。
|
|
1125
|
+
*/
|
|
1126
|
+
forceRefresh(id: string, seenToken?: string): Promise<CodeArtsCredential | undefined>;
|
|
1120
1127
|
}
|
|
1121
1128
|
//#endregion
|
|
1122
1129
|
//#region src/account-pool.d.ts
|
package/lib/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as CodeArtsCatalog, B as codeartsAccountsPath, C as isWelfareClaimedStatus, D as rateLimitFrame, E as prepareChatBody, F as isBenefitBlocked, H as startLogin, I as isBenefitModel, L as CodeArtsAccountStore, M as clearBenefitBlocked, N as creditRatioLabel, O as signedGet, P as filterEnabledModels, R as CodeArtsCredentialStore, S as isTpmLimit, T as pickDailyWelfareCampaign, V as codeartsAuthPath, _ as isIamTokenError, a as SNAP_HOST, b as isParamInvalidFrame, c as claimDailyWelfare, d as fetchCallerIdentity, f as fetchModels, g as isConcurrencyCap, h as firstFrameError, i as refreshCatalog, j as FALLBACK_CODEARTS_MODELS, k as upstreamErrorMessage, l as clearQueueStatus, m as fetchWelfareCampaigns, n as codeartsUsagePath, o as canonicalModel, p as fetchQueueStatus, r as discoverModels, s as chatStream, t as CodeArtsUsageTracker, u as degradeTokenCeiling, v as isModelNotAccessible, w as maxTokensCeilingFor, x as isQueueableRate, y as isNotMultimodal, z as MANUAL_ACCOUNT_ID } from "./usage-DztQaswb.mjs";
|
|
2
2
|
import { i as lazyApi, r as createProvider } from "./models-CgKCibpk.mjs";
|
|
3
3
|
import z from "@deepseek-ai/schemastery";
|
|
4
4
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
@@ -853,6 +853,7 @@ const KIND_STATUS = {
|
|
|
853
853
|
hard_credit: 402,
|
|
854
854
|
soft_rate: 429,
|
|
855
855
|
session_dead: 401,
|
|
856
|
+
iam_stale: 401,
|
|
856
857
|
not_found: 502,
|
|
857
858
|
server: 502,
|
|
858
859
|
client: 400
|
|
@@ -1091,7 +1092,8 @@ function createCodeArtsShim(options) {
|
|
|
1091
1092
|
const reader = result.stream.getReader();
|
|
1092
1093
|
const first = await reader.read();
|
|
1093
1094
|
const head = first.done ? void 0 : first.value;
|
|
1094
|
-
const
|
|
1095
|
+
const headText = head === void 0 ? void 0 : new TextDecoder().decode(head);
|
|
1096
|
+
const rate = headText === void 0 ? void 0 : rateLimitFrame(headText);
|
|
1095
1097
|
if (rate !== void 0) {
|
|
1096
1098
|
reader.releaseLock();
|
|
1097
1099
|
await result.stream.cancel().catch(() => {});
|
|
@@ -1106,7 +1108,26 @@ function createCodeArtsShim(options) {
|
|
|
1106
1108
|
}
|
|
1107
1109
|
};
|
|
1108
1110
|
}
|
|
1109
|
-
const
|
|
1111
|
+
const upstreamError = headText === void 0 ? void 0 : firstFrameError(headText);
|
|
1112
|
+
if (upstreamError !== void 0) {
|
|
1113
|
+
const detail = `${upstreamError.code}: ${upstreamError.message}`.replace(/^: /, "");
|
|
1114
|
+
if (isIamTokenError(detail) || isModelNotAccessible(detail) || isNotMultimodal(detail)) {
|
|
1115
|
+
reader.releaseLock();
|
|
1116
|
+
await result.stream.cancel().catch(() => {});
|
|
1117
|
+
logger?.warn(`dsh-codearts: ${prepared.model} 首帧报上游错误(${detail.slice(0, 160)})`);
|
|
1118
|
+
const kind = isIamTokenError(detail) ? "iam_stale" : isModelNotAccessible(detail) ? "not_found" : "client";
|
|
1119
|
+
return {
|
|
1120
|
+
prepared,
|
|
1121
|
+
result: {
|
|
1122
|
+
ok: false,
|
|
1123
|
+
kind,
|
|
1124
|
+
status: KIND_STATUS[kind],
|
|
1125
|
+
message: detail
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
const shouldHeal = heal < MAX_CEILING_HEALS && headText !== void 0 && isParamInvalidFrame(headText);
|
|
1110
1131
|
reader.releaseLock();
|
|
1111
1132
|
if (!shouldHeal) return {
|
|
1112
1133
|
prepared,
|
|
@@ -1202,6 +1223,8 @@ function createCodeArtsShim(options) {
|
|
|
1202
1223
|
}
|
|
1203
1224
|
pool.sync(ids);
|
|
1204
1225
|
const tried = /* @__PURE__ */ new Set();
|
|
1226
|
+
/** 已经为它强制续期过一次的账号:同一个号只重试一次,避免把 refresh_token 连烧。 */
|
|
1227
|
+
const refreshed = /* @__PURE__ */ new Set();
|
|
1205
1228
|
let cappedAccount;
|
|
1206
1229
|
let tpmAccount;
|
|
1207
1230
|
let queueWaitedMs = 0;
|
|
@@ -1257,6 +1280,18 @@ function createCodeArtsShim(options) {
|
|
|
1257
1280
|
tpmAccount = id;
|
|
1258
1281
|
break;
|
|
1259
1282
|
}
|
|
1283
|
+
if (result.kind === "iam_stale") {
|
|
1284
|
+
if (!refreshed.has(id)) {
|
|
1285
|
+
refreshed.add(id);
|
|
1286
|
+
logger?.warn(`dsh-codearts: ${id} 的 IAM token 被判失效,强制续期后重试`);
|
|
1287
|
+
if (await store.forceRefresh(id, credential.token) !== void 0) {
|
|
1288
|
+
tried.delete(id);
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
pool.cooldown(id, SERVER_COOLDOWN_MS, result.message);
|
|
1293
|
+
continue;
|
|
1294
|
+
}
|
|
1260
1295
|
if (result.kind === "session_dead") {
|
|
1261
1296
|
pool.disable(id, result.message);
|
|
1262
1297
|
continue;
|
|
@@ -561,6 +561,19 @@ var AccountSlot = class {
|
|
|
561
561
|
if (!cred.refreshToken || !cred.dpopPrivateKey || !cred.clientId) return cred;
|
|
562
562
|
const exp = cred.expiration ? Date.parse(cred.expiration) : NaN;
|
|
563
563
|
if (!(Number.isNaN(exp) ? false : exp - Date.now() < REFRESH_SKEW_MS)) return cred;
|
|
564
|
+
return this.forceRefresh();
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* 不管还剩多久,立刻续期一次。
|
|
568
|
+
*
|
|
569
|
+
* 给上游回 `invalid_iam_token` 时用:token 可能被提前作废(另一个客户端续期、
|
|
570
|
+
* 上游轮换),而 `expiration` 还写着几小时之后,`ensureFresh` 根本不会动。
|
|
571
|
+
* 仍然走单飞,避免并发请求把一次性的 refresh_token 烧掉。
|
|
572
|
+
*/
|
|
573
|
+
async forceRefresh() {
|
|
574
|
+
const cred = this._cred;
|
|
575
|
+
if (cred.stale === true) return cred;
|
|
576
|
+
if (!cred.refreshToken || !cred.dpopPrivateKey || !cred.clientId) return cred;
|
|
564
577
|
return this._inflight ??= this.refresh(cred, {
|
|
565
578
|
refreshToken: cred.refreshToken,
|
|
566
579
|
dpopPrivateKey: cred.dpopPrivateKey,
|
|
@@ -815,6 +828,19 @@ var CodeArtsAccountStore = class {
|
|
|
815
828
|
securityToken: cred.token
|
|
816
829
|
};
|
|
817
830
|
}
|
|
831
|
+
/**
|
|
832
|
+
* 上游报 IAM token 无效时强制续期。
|
|
833
|
+
*
|
|
834
|
+
* `seenToken` 是这次请求实际用的 token:若别的请求已经换过凭证,就别再刷一次
|
|
835
|
+
* (refresh_token 会轮换,白刷一次就多一次失败风险)。
|
|
836
|
+
*/
|
|
837
|
+
async forceRefresh(id, seenToken) {
|
|
838
|
+
await this.loaded();
|
|
839
|
+
const slot = this._slots.get(id);
|
|
840
|
+
if (slot === void 0) return void 0;
|
|
841
|
+
if (seenToken !== void 0 && slot.credential.token !== seenToken) return slot.credential;
|
|
842
|
+
return slot.forceRefresh();
|
|
843
|
+
}
|
|
818
844
|
};
|
|
819
845
|
//#endregion
|
|
820
846
|
//#region src/catalog.ts
|
|
@@ -1238,8 +1264,12 @@ var upstream_exports = /* @__PURE__ */ __exportAll({
|
|
|
1238
1264
|
fetchModels: () => fetchModels,
|
|
1239
1265
|
fetchQueueStatus: () => fetchQueueStatus,
|
|
1240
1266
|
fetchWelfareCampaigns: () => fetchWelfareCampaigns,
|
|
1267
|
+
firstFrameError: () => firstFrameError,
|
|
1241
1268
|
isBenefitMissing: () => isBenefitMissing,
|
|
1242
1269
|
isConcurrencyCap: () => isConcurrencyCap,
|
|
1270
|
+
isIamTokenError: () => isIamTokenError,
|
|
1271
|
+
isModelNotAccessible: () => isModelNotAccessible,
|
|
1272
|
+
isNotMultimodal: () => isNotMultimodal,
|
|
1243
1273
|
isParamInvalidFrame: () => isParamInvalidFrame,
|
|
1244
1274
|
isQueueableRate: () => isQueueableRate,
|
|
1245
1275
|
isResignRefusal: () => isResignRefusal,
|
|
@@ -1597,6 +1627,9 @@ function upstreamErrorMessage(message, status = 0) {
|
|
|
1597
1627
|
if (MODEL_RATE_CODE.test(message) || MODEL_RATE_TEXT.test(message)) return "华为侧该模型当前请求过多(共享 TPM 超限),不是你的账号或插件问题。稍后重试,或临时改用 GLM-5.2 / GLM-5.3。";
|
|
1598
1628
|
if (message.includes("TM.00001041")) return "CodeArts 免费档同时只允许 3 个会话,当前槽位一直占满(被打断的会话约 1 分钟才释放)。请稍后重试。";
|
|
1599
1629
|
if (isBenefitMissing("", message)) return "这个模型属于限时福利,当前福利通道不可用(不是账号问题)。可以刷新模型列表,改用非福利模型。";
|
|
1630
|
+
if (isIamTokenError(message)) return "上游判定本次登录凭据(IAM token)已失效。插件已尝试自动续期;若反复出现,请到设置卡片重新登录。";
|
|
1631
|
+
if (isModelNotAccessible(message)) return "上游说这个模型不存在或当前账号没有权限(可能已下线或改名)。请刷新模型列表后换一个模型;换号没有用,这是模型级的限制。";
|
|
1632
|
+
if (isNotMultimodal(message)) return "这个模型不支持图片输入(上游 406)。请移除消息里的图片,或改用支持识图的模型。";
|
|
1600
1633
|
if (message.toLowerCase().includes("model is not registered")) return "上游没有注册这个模型,它可能已经下线;请刷新模型列表后重选。";
|
|
1601
1634
|
return `codearts 上游错误 (http ${status}): ${message.slice(0, 400)}`;
|
|
1602
1635
|
}
|
|
@@ -1613,6 +1646,59 @@ function isBenefitMissing(code, message) {
|
|
|
1613
1646
|
return code === "InferHub.4004.200" || message.toLowerCase().includes("benefit not found");
|
|
1614
1647
|
}
|
|
1615
1648
|
/**
|
|
1649
|
+
* 上游判定本次请求的 IAM 凭据无效。
|
|
1650
|
+
*
|
|
1651
|
+
* 2026-11-02 实测:它会以**流内首帧**(HTTP 200)的形式回来,内容只有一句
|
|
1652
|
+
* `invalid_iam_token`,所以既不走 classify() 也不会触发换号 —— 之前只能干等到
|
|
1653
|
+
* 凭证自然过期(距过期不足 5 分钟才刷新),期间每条消息都撞同一堵墙。
|
|
1654
|
+
* 这些写法都见过:IAM 自身 / APIG 网关 / 非法 Token 三条链路各有一套措辞。
|
|
1655
|
+
*/
|
|
1656
|
+
const IAM_TOKEN_ERROR = /invalid_iam_token|incorrect iam authentication|apig\.0301|iam token is invalid|iam token (?:has )?expired|decrypt token fail/i;
|
|
1657
|
+
function isIamTokenError(message) {
|
|
1658
|
+
return IAM_TOKEN_ERROR.test(message);
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* 「模型不存在或当前账号没权限」。
|
|
1662
|
+
*
|
|
1663
|
+
* 上游一边把它列进模型发现(`builtin` / `agent` 两路都有),一边在聊天时回
|
|
1664
|
+
* `InferHub.ModelArts.81003.401`。2026-11-02 实测 `deepseek-v4.1-flash`
|
|
1665
|
+
* 当天就是这种状态:列表里有、调用必拒,且换号也一样 —— 所以换号无意义。
|
|
1666
|
+
*/
|
|
1667
|
+
function isModelNotAccessible(message) {
|
|
1668
|
+
return message.includes("InferHub.ModelArts.81003.401") || message.toLowerCase().includes("the model does not exist or you do not have access to it");
|
|
1669
|
+
}
|
|
1670
|
+
/** 上游实际不吃图(模型级检查,与图片格式无关)。 */
|
|
1671
|
+
function isNotMultimodal(message) {
|
|
1672
|
+
return message.includes("InferHub.001001020.406") || message.toLowerCase().includes("the request model is not multimodal");
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* 从**首帧**(已转成 OpenAI 形状)里取出上游错误码与文本;没有错误返回 undefined。
|
|
1676
|
+
*
|
|
1677
|
+
* 只用于「还没产出任何内容」的判断:此时重发是安全的。帧可能被 TCP 切断,解析
|
|
1678
|
+
* 不出来就当没有错误,绝不影响转发。
|
|
1679
|
+
*/
|
|
1680
|
+
function firstFrameError(text) {
|
|
1681
|
+
for (const line of text.split("\n")) {
|
|
1682
|
+
const trimmed = line.trim();
|
|
1683
|
+
if (!trimmed.startsWith("data:")) continue;
|
|
1684
|
+
const payload = trimmed.slice(5).trim();
|
|
1685
|
+
if (payload === "" || payload === "[DONE]") continue;
|
|
1686
|
+
let ev;
|
|
1687
|
+
try {
|
|
1688
|
+
ev = JSON.parse(payload);
|
|
1689
|
+
} catch {
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
const code = String(ev.error?.code ?? ev.error_code ?? "");
|
|
1693
|
+
const message = String(ev.error?.message ?? ev.error_msg ?? "");
|
|
1694
|
+
if (code === "" && message === "") continue;
|
|
1695
|
+
return {
|
|
1696
|
+
code,
|
|
1697
|
+
message
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
1616
1702
|
* 本地代理「拒签」标记:billion-context 这类宿主层代理拦截出站请求做上下文压缩,
|
|
1617
1703
|
* 碰到 SDK-HMAC-SHA256 签名请求要用 CodeArts 凭据重签;找不到凭据时在**本地**回
|
|
1618
1704
|
* 403(根本没出网),body 里带这两个标记。这不是凭据失效 —— 归 session_dead 会把
|
|
@@ -1624,6 +1710,8 @@ function isResignRefusal(message) {
|
|
|
1624
1710
|
function classify(status, message) {
|
|
1625
1711
|
const m = message.toLowerCase();
|
|
1626
1712
|
if (isResignRefusal(message)) return "client";
|
|
1713
|
+
if (isIamTokenError(message)) return "iam_stale";
|
|
1714
|
+
if (isModelNotAccessible(message)) return "not_found";
|
|
1627
1715
|
if (status === 401 || status === 403) return "session_dead";
|
|
1628
1716
|
if (status === 402) return "hard_credit";
|
|
1629
1717
|
if (status === 429) return "soft_rate";
|
|
@@ -2776,4 +2864,4 @@ function sumOf(store) {
|
|
|
2776
2864
|
return out;
|
|
2777
2865
|
}
|
|
2778
2866
|
//#endregion
|
|
2779
|
-
export {
|
|
2867
|
+
export { CodeArtsCatalog as A, codeartsAccountsPath as B, isWelfareClaimedStatus as C, rateLimitFrame as D, prepareChatBody as E, isBenefitBlocked as F, startLogin as H, isBenefitModel as I, CodeArtsAccountStore as L, clearBenefitBlocked as M, creditRatioLabel as N, signedGet as O, filterEnabledModels as P, CodeArtsCredentialStore as R, isTpmLimit as S, pickDailyWelfareCampaign as T, codeartsAuthPath as V, isIamTokenError as _, SNAP_HOST as a, isParamInvalidFrame as b, claimDailyWelfare as c, fetchCallerIdentity as d, fetchModels as f, isConcurrencyCap as g, firstFrameError as h, refreshCatalog as i, FALLBACK_CODEARTS_MODELS as j, upstreamErrorMessage as k, clearQueueStatus as l, fetchWelfareCampaigns as m, codeartsUsagePath as n, canonicalModel as o, fetchQueueStatus as p, discoverModels as r, chatStream as s, CodeArtsUsageTracker as t, degradeTokenCeiling as u, isModelNotAccessible as v, maxTokensCeilingFor as w, isQueueableRate as x, isNotMultimodal as y, MANUAL_ACCOUNT_ID as z };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-codearts",
|
|
3
3
|
"displayName": "DSH CodeArts Connect",
|
|
4
4
|
"description": "把华为云 CodeArts 接入 DeepSeek Harness —— 内置 OAuth2 登录(PKCE + DPoP),登录后即可在 DSH 对话窗口使用 CodeArts 的模型。",
|
|
5
|
-
"version": "1.2.
|
|
5
|
+
"version": "1.2.2",
|
|
6
6
|
"packageManager": "pnpm@11.24.0",
|
|
7
7
|
"author": "elegant01",
|
|
8
8
|
"keywords": [
|