@eddyskywalker/dsh-chatgpt-subscription 0.29.0 → 0.30.0
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 +27 -0
- package/README.md +11 -2
- package/lib/client.js +18 -16
- package/lib/client.js.map +1 -1
- package/lib/index.js +569 -136
- package/lib/types/client/minimax-code/MinimaxCodeSection.d.ts +8 -5
- package/lib/types/client/minimax-code/MinimaxCodeSection.d.ts.map +1 -1
- package/lib/types/client/minimax-code/locales.d.ts +6 -6
- package/lib/types/host/common/context-overflow.d.ts.map +1 -1
- package/lib/types/host/minimax-code/adapter.d.ts +14 -0
- package/lib/types/host/minimax-code/adapter.d.ts.map +1 -1
- package/lib/types/host/minimax-code/checkin-gateway.d.ts +36 -0
- package/lib/types/host/minimax-code/checkin-gateway.d.ts.map +1 -1
- package/lib/types/host/minimax-code/checkin.d.ts +60 -2
- package/lib/types/host/minimax-code/checkin.d.ts.map +1 -1
- package/lib/types/host/minimax-code/mapper.d.ts +51 -19
- package/lib/types/host/minimax-code/mapper.d.ts.map +1 -1
- package/lib/types/host/minimax-code/model-catalog.d.ts +40 -15
- package/lib/types/host/minimax-code/model-catalog.d.ts.map +1 -1
- package/lib/types/host/minimax-code/token-store.d.ts.map +1 -1
- package/lib/types/host/minimax-code/types.d.ts.map +1 -1
- package/lib/types/host/proxy-manager.d.ts.map +1 -1
- package/lib/types/shared/minimax-code-contracts.d.ts +17 -3
- package/lib/types/shared/minimax-code-contracts.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.30.0 - 2026-10-20
|
|
6
|
+
- **[minimax-code] 请求的模型与实际回答的模型不一致时,这一轮立刻失败——而不是替用户按最贵档付费**
|
|
7
|
+
- **现象**:这条路由**不拒绝**它路由不了的模型 id。2026-10-20 实测:乱写串与空串都回 HTTP 200,实际由 `MiniMax-M3` 代答;同一个乱写 id 带 `reasoningEffort:'none'` 重发,回答的 M3 真的少了 thinking 块——是**真替换**,不是把名字写回去的壳。
|
|
8
|
+
- **为什么要紧**:我们的模型目录是硬编码的(`/v1/models` 对订阅流量是 503 `direct_route_not_configured`,那是路由裁决不是路径拼错)。若 MiniMax 下架了 M2.7 而用户仍选着它,请求**不会报错**,会被 M3 代跑——用户以为在用便宜模型,实际按最贵档计费(我们自己的实测:M3 的 max 档思考量约 3 倍、延迟约 4 倍)。**这是钱包问题,不是可用性问题**,而且**没有错误报文可依**。
|
|
9
|
+
- **改法**:[mapper.ts](../src/host/minimax-code/mapper.ts) 读流头的 `message_start.message.model`(此前只取 usage 与 stop_reason);[adapter.ts](../src/host/minimax-code/adapter.ts) 把它与**真正上线的那串 id** 比对(从即将发送的请求体读回,不是目录解析名),不一致则抛 `MODEL_SUBSTITUTED`。
|
|
10
|
+
- **判定点与代价**:判定在 `processMinimaxStreamLine` 之后、**任何 `yield` 之前**——`message_start` 是流的第一个事件,排在所有 `content_block_start` 之前,所以**没有任何内容块送达调用方**,无需回收、不会有重试重复输出。新码**不在** `retryableCodes` 里:重发同一 body 只会再被替换一次,并按替身计费。
|
|
11
|
+
- **验证**:`npm run typecheck` 0 错误;全量 **250 passed | 1 skipped(251 文件)、4050 passed | 7 skipped、0 failed**;`npm run build` 通过。新增 5 条守卫(一致 / 不一致且零内容块 / 无 model 字段 / 大小写双向 / mapper 层单测)。
|
|
12
|
+
- **[minimax-code] 每日签到锁定在它开始的那一个账号上:池行被重绑就重新认,attempt 中途换号就拒绝**
|
|
13
|
+
- **现象**:签到读面板用 A 的 `realUserID`、领取用 B 的凭据是可能的——网关把 `accessToken` 与 `realUserID` 当两个独立入参,彼此没有绑定校验,而 `realUserID` 是按池行缓存的。
|
|
14
|
+
- **两个真实窗口**:① 状态文件钉着 A、池里那一行已被换成 B;② 401 强制续期后 `renewCredential` 返回**另一个账号**的凭据,重试即以别人的账号领取。
|
|
15
|
+
- **两者性质不同,处理也不同**:① **重绑是合法的状态变化**——一行 = 插件替用户管理的一个账号,跟着走才是对的,所以**用当前凭据重新解析身份、重新钉住、照常签到**(老状态文件也会在第一次运行时自愈);② **attempt 中途的漂移与 401 续期换号必须硬拒绝**(`MinimaxCodeCheckinAccountMismatchError`),不自愈。
|
|
16
|
+
- **记一个容易骗人的地方**:`native:<region>:<recordKey>` 里的 `recordKey` 是**记录槽位**键,**不是账号 id**——用户在桌面端把另一个账号签进同一槽位时它不变。所以它只是「尽力而为的护栏」,这条线账号的**权威标识**是从身份接口解析出来的 `realUserID`。
|
|
17
|
+
- **验证**:签到相关 5 套件 **113 passed**;两次**变异验证**(关掉钉校验 → 两条用例红;关掉漂移守卫 → 漂移用例红),均已还原。
|
|
18
|
+
- **[minimax-code] 额度耗尽改按上游数字码判定,措辞表留作兜底;并纠正我们文档里的过度声称**
|
|
19
|
+
- **改法**:[adapter.ts](../src/host/minimax-code/adapter.ts) 的 `statusCodeIn` 读三处信封(`base_resp.status_code` → `errorCode` → **顶层 `code`**),命中上游的 `2056`/`2067` 即判账号级额度耗尽;403 与 429 两个入口读**同一个**谓词、汇进**同一个**判决,形状天然一致。**2013 明确排除**(通用「参数非法」码,本仓库在 reset-card 与 Files API 上另有语义)。
|
|
20
|
+
- **实测订正**:Messages 路由的错误信封**没有 `base_resp`**——400 是 `{type,error{type,message}}`(无任何数字字段),401 是 `{code,message}`。所以这条路由上**当前真正 operative 的仍是措辞表**,数字码臂是**跨信封的前向兼容臂**。README 里那段把它写成首要判据的话已改正。
|
|
21
|
+
- **验证**:新增 8 条(数字码 / 措辞 / 两者同形 / 2013 不判 / 顶层 401 不判 / 流内继承),邻近 6 套件 133 passed。
|
|
22
|
+
- **[minimax-code] 令牌时长从「约 24 小时」纠正为「约 1 小时」,设置卡改用分钟显示**
|
|
23
|
+
- **现象**:卡片上写着访问令牌约 24 小时有效,而实测是**一小时**(`types.ts`)——一句用户会读到的错话;余量又以「小时」报,一小时寿命的令牌因此看不出 Host 何时会续期。
|
|
24
|
+
- **改法**:`remainingHours` → `remainingMinutes`,单位改分钟(恒定一位小数,不足一分钟视为已过期),中英文案同步改为「约 1 小时」。
|
|
25
|
+
- **[minimax-code] 能力说明与上游差分归档:七处过期表述 + 活体探针 + 差分文档**
|
|
26
|
+
- **代码早于文档**:Files API 早就实现了(上传 / 12 小时 TTL / 账号隔离 / 去重缓存),视频也早已声明,而 `model-catalog.ts` 的三处注释与 README 仍在描述实现之前的世界——这会让人误判这条线路的能力边界,包括下一次做差分分析的人。
|
|
27
|
+
- **新产物**:`scripts/probe-minimax-capabilities.probe.ts`(16 个 case 的活体探针)与 `docs/minimax-code-probe-capabilities.md`(原始证据),以及 `docs/minimax-code-upstream-delta-v2.md`(本轮上游差分、明确否掉的项、待办)。
|
|
28
|
+
- **顺带量出**:服务侧 `max_tokens` 真阈值是 **524288**(128000 / 512000 都接受,999999999 回 400)——我们写的 128000 是保守值(与上游兜底表一致),**数值未改**;并确认那条 400 文案**不会**误触发上下文超限检测,不存在「压缩→重试→再压缩」的死循环。
|
|
29
|
+
- **[Codex 搜索] 读出活体搜索真正的答案字段,并给代理一个连接超时**
|
|
30
|
+
- **现象**:活体 `/alpha/search` 的回答在顶层 `output` 字段,而 `readContent` 只看 `content` / `output_text` / `summary`——于是每次真实搜索都**没有答案正文**,工具只报一串来源,读起来像「搜索能用,只是没搜到东西」。
|
|
31
|
+
- **代理**:undici 默认 10s 连接超时覆盖的是到**源站**的 TLS 握手而非到代理的那一跳;实测某条到 chatgpt.com 的慢路由仅握手就 1.2s–18s、请求整体 31s,默认值把本该几秒后成功的请求杀掉,而且是以一句不点名的 `fetch failed` 杀掉。代理 agent 改为 60s 连接超时,并用 undici 真正接受的 options 对象形式传入(第二个位置参数会被静默丢弃)。
|
|
5
32
|
## 0.29.0 - 2026-10-19
|
|
6
33
|
- **[Antigravity] 429 不再被当成「配额耗尽」:裸 429 不冷却账号、不换账号,也不再把上游原因藏起来**
|
|
7
34
|
- **现象**:账号配额还很充足(实测 Gemini 5h 桶剩余 95.95%、周桶剩余 84.67%,Claude/GPT 100%),插件却报「账号配额已耗尽」,并把该账号的 Gemini 配额族冷却 15 分钟;此后每次重试都被账号池直接挡下,连一次网络请求都不发。
|
package/README.md
CHANGED
|
@@ -191,8 +191,17 @@
|
|
|
191
191
|
- 该端点的 `GET /v1/models` **未对订阅流量开放**(503 `"{\"errorCode\":50115,\"errorReason\":\"direct_route_not_configured\"}"`),所以任何「实时目录」都只会是一个必然失败的请求;
|
|
192
192
|
- 四款模型:M2.7 / M2.7 HighSpeed / M3 取自本机 `~/.minimax/config.yaml` 的模型表;**M3.1 Flash 是 2026-10-19 换上的正式版 id**(`MiniMax-M3.1-Flash`,去掉了 `-Preview` 后缀),取自 MiniMax 自己的 Model Invocation 页 Supported Models 表并在该订阅端点上实测(正式 id、`output_config.effort` 的 low/max、内联图片)。无论哪一条,**都不从不存在的接口推断**;
|
|
193
193
|
- **思考形态按模型表三态实现,且一律走 `output_config`,不发 `thinking` 对象**(该字段从未被文档确认,发出去只是一种形状的猜测):M2.7 系列**恒定开启且没有档位**(不发送任何字段);M3 是**开关二态**(关闭时 `output_config.effort: 'none'`);M3.1 Flash **强制开启并可指定档位**(`output_config.effort` 取 low/medium/high/xhigh/max,省略即 max)。未知或越档的取值一律回落到该模型目录声明的默认档,不会把模型没有声明的档位发出去;
|
|
194
|
-
-
|
|
195
|
-
-
|
|
194
|
+
- **视频已在能力声明里,并且真的能发出去**:M3 与 M3.1 Flash 的 `inputModalities` 都是 `['text', 'image', 'video']`(`model-catalog.ts`),线上形状取自实测——`{ type: 'video', source: { type: 'base64', … } }` 被该端点接受并解码(同一段实测也否掉了 data-URL 字符串那种封装,它会在 `:` 处按裸 base64 解码失败)。**能不能读到字节是宿主注入的接缝**:`AttachmentVideoReader`(`adapter.ts:372`,类型在 `src/host/common/video-request.ts`)由宿主提供,DSH 自己的附件服务只存图片,所以视频只能来自本部署里某个真实的生产者;宿主不提供读取器、或某一条读不出来时,`resolveRequestVideos` 把它解析成 `unavailable`,映射器(`mapper.ts:416`)才退化成一段说明文本——`modalities.ts` 的 `videoOmissionText` 按 4 种 reason 给出各自不同的措辞(`unsupported-model` / `unsupported-container` / `unreadable` / `unsupported-wire`),让模型知道「有这段视频、为什么没带上」,而不是对着空白回答。**降级发生在单个片段上,不发生在声明上**:声明的依据是接缝存在且线上形状实测可用,不是「任何情况下都一定能读到」;
|
|
195
|
+
- **失败按 8 级判决分类,判词决定重试与号池动作**(`classifyMinimaxFailure`,`adapter.ts:388-470`;读的是**原始正文**而不是 `summarizeFailureBody` 抽出的那一个字段,因为同一个判词在两种信封里放的位置不同):
|
|
196
|
+
- **上下文超限**——`isHttpContextOverflow` 先判,`CONTEXT_OVERFLOW_CODE`,终局、不归账号;
|
|
197
|
+
- **账号级额度耗尽**——**`403` 或 `429`** 都可能落到这一类,判据是正文**点名了一个用尽的窗口**,与状态码无关:`isSpentWindow403`(`adapter.ts:355`)管 403,`429` 分支(`adapter.ts:425-431`)用同一个谓词问同一个问题,两条都返回**同一个** `quotaExhaustedFailure`(`adapter.ts:368`:`PROVIDER_ERROR` / `retryable: false` / `accountScoped: true`)。**判据本身是两个臂**(先问上游用量限额的**数字码**、再问**措辞表**兜底;这个先后只是求值顺序,不代表这条路由上真正会开火的是前者):`namesUsageLimitStatusCode`(`adapter.ts:311`)读两种信封里的上游码(平台信封 `base_resp.status_code`、客户端信封 `errorCode`,并额外往流内错误体 `{ error: … }` 里多读一层),命中 `USAGE_LIMIT_UPSTREAM_STATUS_CODES = {2056, 2067}` 即算;非 JSON 或没有该字段的正文回落到措辞表 `QUOTA_EXHAUSTED_PATTERNS`。两个臂汇进同一个判决 `namesSpentWindow = namesUsageLimitStatusCode || 措辞表`(`adapter.ts:334`)——它们不是两套规则,而是同一个问题的两种承载方式:一句中文没有码,一个裸码没有句子。**但要把定位说准**:数字码臂在这条 Messages 路由上目前**不会开火**,它是**跨信封的前向兼容臂**(哪天服务真的在 Messages 路由上带出 `2056`/`2067`/`errorCode`,它就会生效),**不是这条路由当前的主要判据**。2026-10-20 的活体探针(`scripts/probe-minimax-capabilities.probe.ts`,原始证据 `docs/minimax-code-probe-capabilities.md`)把这条路由的错误信封量了个遍:端点上只有**两种**信封,**没有一种带 `base_resp`**——① 请求被拒 `400` 是 `{"type":"error","error":{"type":"invalid_request_error","message":"…"}}`(参数类错误上多一个 `request_id`),**没有任何数字字段**;② 认证被拒 `401` 是 `{"code":401,"message":"auth failed"}`,**顶层 `code` 是这条路由唯一的数字字段**。`base_resp.status_code` 那个信封属于这条线路的**别的**路由(配额 `coding_plan/remains`、Files API、`reset-card`),不属于 Messages。连探针抓到的 `2013` 也**不是字段**:它只以文本形式嵌在 `error.message` 的括号里(`… does not support max tokens > 524288 (2013)`),而且是**通用「参数非法」码**——超限的 `max_tokens`、空 body、畸形 JSON 全都归它;本仓库在 `reset-card` 与 Files API 上另有语义,**绝不拿它当额度判决**。所以在 Messages 路由上**当前真正 operative 的那一条是措辞表**:数字码臂留在这里,是为了不让一个将来可能带上数字码的信封被当成措辞去猜,而不是因为这条路由现在会给码。它必须排在下面的凭据分支**之前**,因为 `401`/`403` 的正文在这条线上是同一句 `token is required`,光看状态码分不出「窗口用尽」与「凭据被拒」。号池据此冷却到窗口重置(未命中周窗口词冷却 5 小时,命中 `WEEKLY_QUOTA_WINDOW_PATTERN` 冷却 7 天,见 `adapter.ts:172-184`),且**刻意不采信 Retry-After**——等 N 秒不会让一个用尽的窗口重新可用;
|
|
198
|
+
- **凭据失效**——`401`/`403` 里不属于上面的,`INVALID_CREDENTIAL`,终局且归账号,但走 `markAuthFailed` 而不是 `markCooldown`;这条线的 401 正文在「没发凭据」和「凭据被拒」两种情况下是同一句 `token is required`,所以提示直接指向重新登录;
|
|
199
|
+
- **纯背压 429**——**没有**点名用尽窗口的 429(数字码与措辞两个臂都没命中),`RATE_LIMIT`,**可重试**且**不归账号**(同一套餐后面的每个账号都会被同样拒绝,冷却它只会把号池抽干);
|
|
200
|
+
- **5xx**——`SERVER`,可重试、不归账号;
|
|
201
|
+
- **400**——`PROVIDER_ERROR`,终局:请求本身不被接受,重发一次也不会被接受;
|
|
202
|
+
- **其他状态码**——`PROVIDER_ERROR`,终局,正文为空时提示「无响应」;
|
|
203
|
+
- **401 另有一次性补救**(合并下方一条):第一次 401 会**强制续期一次并原样重试同一个请求体**,重试再被拒才是终局;
|
|
204
|
+
- 可重试的四类(`RATE_LIMIT` / `SERVER` / `TIMEOUT` / `TRANSPORT`)走 DSH retry policy 的有界退避:最多 3 次,1.5s 起步、15s 上限、0.2 抖动;**终局类不重试**——重试只会推迟用户真正需要看到的那句提示;
|
|
196
205
|
- **401 会强制续期一次再重试**:本地时钟看着还有效、服务端却拒收的令牌(在桌面端被撤销、时钟偏移、桌面端已轮换)与「真的需要重新登录」在状态码上无法区分,所以第一次 401 会**强制刷新一次并原样重试同一个请求体**;重试再被拒才是终局;
|
|
197
206
|
- **刷新令牌是单次使用的,因此续期提前 5 分钟、并全进程共用一次轮换**:
|
|
198
207
|
- 这是「每小时自己掉线、然后要求重新登录」的根因。access token 实测只有 **1 小时**,而原先的续期阈值是「到期前 60 秒」——等于每次都在令牌已经会被拒收的那一瞬间才去轮换;
|
package/lib/client.js
CHANGED
|
@@ -36,10 +36,9 @@ window.__ModuleLoader__.load({
|
|
|
36
36
|
* 设置卡片只渲染这一份列表:档位是线路级的词汇表(`isMinimaxCodeReasoningEffort`
|
|
37
37
|
* 就是按它校验的),而不是单个模型的能力表。某个模型实际接受哪些档位由
|
|
38
38
|
* `MinimaxCodeModelOption.reasoningEfforts` 单独给出——把两者合成一份会让卡片
|
|
39
|
-
* 在给一个只认
|
|
39
|
+
* 在给一个只认 high 的模型显示 max 时无从判断是模型不支持还是文案没写。
|
|
40
40
|
*/
|
|
41
41
|
const MINIMAX_CODE_REASONING_EFFORTS = [
|
|
42
|
-
"default",
|
|
43
42
|
"low",
|
|
44
43
|
"medium",
|
|
45
44
|
"high",
|
|
@@ -6800,9 +6799,9 @@ window.__ModuleLoader__.load({
|
|
|
6800
6799
|
regionCn: "中国大陆 (minimax.cn)",
|
|
6801
6800
|
regionGlobal: "国际 (minimax.io)",
|
|
6802
6801
|
expiresAt: "令牌到期",
|
|
6803
|
-
expiresRemaining: "剩余 {
|
|
6802
|
+
expiresRemaining: "剩余 {minutes} 分钟",
|
|
6804
6803
|
expiresExpired: "已过期",
|
|
6805
|
-
expiresHint: "访问令牌约
|
|
6804
|
+
expiresHint: "访问令牌约 1 小时有效;Host 会在令牌过期或上游返回 401 时自动刷新。刷新被拒绝时请回到 MiniMax Code 重新登录。",
|
|
6806
6805
|
signIn: "登录 MiniMax Code",
|
|
6807
6806
|
signingIn: "正在申请设备码...",
|
|
6808
6807
|
signOut: "登出",
|
|
@@ -6910,9 +6909,9 @@ window.__ModuleLoader__.load({
|
|
|
6910
6909
|
regionCn: "Mainland China (minimax.cn)",
|
|
6911
6910
|
regionGlobal: "Global (minimax.io)",
|
|
6912
6911
|
expiresAt: "Token expires",
|
|
6913
|
-
expiresRemaining: "{
|
|
6912
|
+
expiresRemaining: "{minutes} min left",
|
|
6914
6913
|
expiresExpired: "Expired",
|
|
6915
|
-
expiresHint: "The access token lives about
|
|
6914
|
+
expiresHint: "The access token lives about 1 hour; the Host refreshes it once it expires or the upstream answers 401. When a refresh is rejected, sign in again from MiniMax Code.",
|
|
6916
6915
|
signIn: "Sign in to MiniMax Code",
|
|
6917
6916
|
signingIn: "Requesting a device code...",
|
|
6918
6917
|
signOut: "Sign out",
|
|
@@ -7600,17 +7599,20 @@ window.__ModuleLoader__.load({
|
|
|
7600
7599
|
return t("thinkingToggle");
|
|
7601
7600
|
}
|
|
7602
7601
|
/**
|
|
7603
|
-
*
|
|
7602
|
+
* Minutes still left on the access token, or null once it has run out.
|
|
7604
7603
|
*
|
|
7605
|
-
* MiniMax Code's token lives about
|
|
7606
|
-
*
|
|
7607
|
-
*
|
|
7604
|
+
* MiniMax Code's token lives about ONE HOUR — measured, not the day an earlier
|
|
7605
|
+
* note assumed — so minutes are the unit the card reports: hours would turn the
|
|
7606
|
+
* whole remaining life into a single digit and hide the point at which the Host
|
|
7607
|
+
* renews the credential. One decimal is kept, so a countdown that is running
|
|
7608
|
+
* out reads as 0.4 rather than as nothing; below one minute the token is
|
|
7609
|
+
* treated as already gone.
|
|
7608
7610
|
*/
|
|
7609
|
-
function
|
|
7611
|
+
function remainingMinutes(expiresAtMs) {
|
|
7610
7612
|
if (expiresAtMs === void 0 || !Number.isFinite(expiresAtMs) || expiresAtMs <= 0) return null;
|
|
7611
|
-
const
|
|
7612
|
-
if (
|
|
7613
|
-
return
|
|
7613
|
+
const minutes = (expiresAtMs - Date.now()) / 6e4;
|
|
7614
|
+
if (minutes < 1) return null;
|
|
7615
|
+
return minutes.toFixed(1);
|
|
7614
7616
|
}
|
|
7615
7617
|
function MinimaxCodeSection({ onModelChange }) {
|
|
7616
7618
|
const { state, derived, actions } = useMinimaxCodeSection({ onModelChange });
|
|
@@ -7625,7 +7627,7 @@ window.__ModuleLoader__.load({
|
|
|
7625
7627
|
children: t("loading")
|
|
7626
7628
|
})
|
|
7627
7629
|
});
|
|
7628
|
-
const
|
|
7630
|
+
const minutes = remainingMinutes(account?.expiresAtMs);
|
|
7629
7631
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7630
7632
|
className: "dsha-page",
|
|
7631
7633
|
children: [
|
|
@@ -7748,7 +7750,7 @@ window.__ModuleLoader__.load({
|
|
|
7748
7750
|
children: t("expiresAt")
|
|
7749
7751
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7750
7752
|
className: "dsha-value",
|
|
7751
|
-
children: [formatDate$1(account?.expiresAtMs),
|
|
7753
|
+
children: [formatDate$1(account?.expiresAtMs), minutes === null ? ` · ${t("expiresExpired")}` : ` · ${t("expiresRemaining").replace("{minutes}", minutes)}`]
|
|
7752
7754
|
})]
|
|
7753
7755
|
})
|
|
7754
7756
|
] }),
|