dsh-connect-modelscope-token-plan 0.6.3 → 0.6.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/CHANGELOG.md +47 -0
- package/README.md +71 -64
- package/assets/modelscope-usage-capsule.png +0 -0
- package/client.js +17 -3
- package/cordis.patch.yml +4 -3
- package/lib/host-config.js +1 -1
- package/lib/index.js +111 -17
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +47 -16
- package/screenshots.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -26,6 +26,53 @@
|
|
|
26
26
|
> **发版时**:顶部开 `## [X.Y.Z] — YYYY-MM-DD`,完整流程见
|
|
27
27
|
> [RELEASING.md](RELEASING.md) §3(本文开头这段是**写作者规范**,那是**发布操作流程**)。
|
|
28
28
|
|
|
29
|
+
## [0.6.5] — 2026-10-21
|
|
30
|
+
|
|
31
|
+
**出图:被拒的图片地址不再原样回显**
|
|
32
|
+
|
|
33
|
+
- 输入的图片地址校验不过时,此前原样回显你给的地址——它可能带着签名串或令牌,
|
|
34
|
+
于是凭据进日志也进面板。现在先脱敏再截断:诊断信息(协议、主机、路径)留着,
|
|
35
|
+
凭据那部分显示为 [REDACTED]。
|
|
36
|
+
|
|
37
|
+
**面板:计数写盘失败不再悄悄冻住**
|
|
38
|
+
|
|
39
|
+
- 本地计数的写入失败此前被吞掉,面板继续显示上一次成功写下的数字——数字看着在涨,
|
|
40
|
+
其实早就冻住,且没有半点痕迹。现在写入失败会记一条异常送进面板(并留一条日志),
|
|
41
|
+
只有真正落盘的写入才算数。
|
|
42
|
+
|
|
43
|
+
**工程:依赖范围放宽**
|
|
44
|
+
|
|
45
|
+
- 插件声明的 AI 运行时依赖范围从 `^0.85.1` 放宽到 `>=0.85.0 <2.0.0`,与当前宿主
|
|
46
|
+
随附的版本对齐,装上时不再被标成「超出上界」。
|
|
47
|
+
|
|
48
|
+
## [0.6.4] — 2026-10-19
|
|
49
|
+
|
|
50
|
+
**出图:上游边缘抖动不再杀死任务**
|
|
51
|
+
|
|
52
|
+
- 轮询遇到 5xx / 429 / 408 不再判失败:与传输层抖动同一纪律,在本次等待预算内
|
|
53
|
+
继续等,等不到才以 `pending` + `jobId` 收场(带 jobId 续查即可,不必重新出图)。
|
|
54
|
+
此前同一种 CDN 边缘抖动只因表现为 503 就杀作业并进 60s 冷却。4xx 仍是终局。
|
|
55
|
+
- 出图作业表淘汰时跳过仍在续查窗内的作业(越过总放弃线的才优先丢)——能续查的
|
|
56
|
+
作业被杀,两段式就残缺了;并发 pending 再多也不会杀到活作业。
|
|
57
|
+
- 余额缓存键改用完整令牌指纹(此前只取末 6 位,极小概率把一个身份的余额当成
|
|
58
|
+
另一个的返回)。无界面变化。
|
|
59
|
+
|
|
60
|
+
**面板:本地计数的依赖说在明面上**
|
|
61
|
+
|
|
62
|
+
- 未开启「接入为 DSH 模型」时,「本地」区块现在给出一条提示说明计数从接入后
|
|
63
|
+
开始记录——此前新装用户看到的是恒为 0/空的四块计数,与「插件坏了」不可区分;
|
|
64
|
+
开了但没注册成功时提示改说注册状态;provider 状态读不到时不出这条,不替
|
|
65
|
+
Host 断言。
|
|
66
|
+
|
|
67
|
+
**卡片介绍:中英文同源**
|
|
68
|
+
|
|
69
|
+
- 卡片介绍(`locale/zh.json`、`locale/en.json`、`package.json` 的 `description`)此前是三份
|
|
70
|
+
不同步的文本:中文里「默认关」挂在上下文窗口上(开关关的是模型接入,不是自动填入的窗口),
|
|
71
|
+
英文另有一段与中文对不上的译文。现在三处与投稿拟稿逐字相同,中文为准、英文逐句对应。
|
|
72
|
+
|
|
73
|
+
> **实施过程(病灶取证、判据、为何两处不改)在
|
|
74
|
+
> [docs/archive/2026-10-19-0.6.4-process.md](docs/archive/2026-10-19-0.6.4-process.md)**。
|
|
75
|
+
|
|
29
76
|
## [0.6.3] — 2026-10-19
|
|
30
77
|
|
|
31
78
|
**插件卡头部收敛:「魔搭模型接入」不再在同一屏出现两次**
|
package/README.md
CHANGED
|
@@ -1,70 +1,66 @@
|
|
|
1
1
|
# dsh-connect-modelscope-token-plan
|
|
2
2
|
|
|
3
|
-
把魔搭社区(modelscope.cn)**API-Inference
|
|
3
|
+
把魔搭社区(modelscope.cn)**API-Inference 免费额度**的面板接进 DeepSeek Harness 的 **Plugins 页**(插件卡内联,三个 tab:额度 / 模型 / 接入),并可选把魔搭模型注册成 **DSH 的 LLM provider**(id `modelscope-token-plan`,OpenAI 兼容)——注册后魔搭模型进 DSH 模型选择器,可直接对话调用;还能开**出图工具**做文生图 / 图生图。
|
|
4
4
|
|
|
5
|
-
姊妹插件:`dsh-connect-sensenova-token-plan`、`dsh-connect-agnes-token-plan
|
|
5
|
+
姊妹插件:`dsh-connect-sensenova-token-plan`、`dsh-connect-agnes-token-plan`(同族结构)。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> **三条先知道(省得装完才发现)**
|
|
8
|
+
>
|
|
9
|
+
> 1. **只要一枚 API Key,不要登录**:魔搭访问令牌(形如 `ms-…`,在魔搭「个人中心 > 访问令牌」生成),无 OAuth、无密码、无刷新。`MODELSCOPE_API_KEY` 环境变量只在 DSH 启动时读一次、且优先级高于面板保存值——所以**最省事是直接在面板「接入」tab 保存令牌**(立即生效,不依赖重启)。
|
|
10
|
+
> 2. **面板头条是官方魔粒余额(真实值)**;本地计数只统计**经本插件的调用**,且**按 profile(客户端)分段**——桌面端与 Web 端各记各的账、互不可见(`npm run doctor` 并排列出各 profile)。它不是跨端总量。
|
|
11
|
+
> 3. **接入 provider 与出图工具都默认关**:不打开时插件就是个额度看板;打开任一项失败,面板照常用、只是该模块缺席。
|
|
8
12
|
|
|
9
13
|
## 面板长什么样
|
|
10
14
|
|
|
11
15
|
三个 tab:**额度 / 模型 / 接入**。
|
|
12
16
|
|
|
13
|
-
**「额度」tab
|
|
17
|
+
**「额度」tab**——头条是官方魔粒余额(真实值),下面接本地调用分布与趋势图(竖向柱状,可切**显示天数**:默认 / 7 / 14 / 30 / 90):
|
|
14
18
|
|
|
15
19
|

|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
- 本地计数只回答官方余额答不了的问题——按模型分布、本地趋势、429 事件流;它**只读不到时画「—」**,绝不会把「不知道」画成 0。
|
|
22
|
+
- 本地计数**按 profile 分段**(本客户端口径):桌面端与 Web 端是两个 profile,各有各的状态目录,互不可见;直连魔搭的其它客户端不计入。
|
|
18
23
|
|
|
19
|
-
**「模型」tab
|
|
24
|
+
**「模型」tab**——模型按厂商分组、勾选即启用,顶部可搜索;开关「接入为 DSH 模型」即把魔搭注册为 DSH provider,模型进入 DSH 选择器:
|
|
20
25
|
|
|
21
26
|

|
|
22
27
|
|
|
23
|
-
同一 tab
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
计费与耗时口径)见 [docs/SPIKE-image-gen.md](docs/SPIKE-image-gen.md)。
|
|
28
|
-
|
|
29
|
-
工具本身也是**两段式**的(v0.4 起):一次调用只等 `drawWaitMs`(默认 90 秒)——
|
|
30
|
-
图好了直接返回,没好就返回 `status: pending` 与一个 `jobId`,**带上它再调用即可
|
|
31
|
-
继续查询**(只查询、不重复出图)。上游排队实测 1–15 分钟,此前一次调用会一直
|
|
32
|
-
await 到出图为止,整轮对话都得陪着等。
|
|
33
|
-
|
|
34
|
-

|
|
35
|
-
|
|
36
|
-
**卡片最底**另有**「上下文窗口覆盖」**选择器(不覆盖 / 128K / 256K / 512K / 1M):
|
|
37
|
-
魔搭目录 `/v1/models` 不返回窗口字段,Host 侧按四级真值链给每个模型定窗口——
|
|
38
|
-
① 目录真声明了就用目录值(不受任何下层影响);② 用户在下面选一个档位(保存即
|
|
39
|
-
重注册);③ 没设覆盖才查内置策展表(`context-catalog.ts`,来自 models.dev 各厂 host
|
|
40
|
-
交叉比对的同 id 主流窗口,2026-10-16 核对,含 DeepSeek-V4 全系 1M、
|
|
41
|
-
Qwen3.5/Qwen3.8-27B/Step 3.x 256K、GLM-5.2 1M 等 19 个当前目录 id);④ 都没有才落
|
|
42
|
-
128K 兜底。**覆盖压过策展表**——插件自带的那份是第三方交叉真值,用户要改就改得动。
|
|
43
|
-
档位落盘在 `provider.json` 的 `contextWindow` 键(v1 形状的新增可选键,旧构建宽容读),
|
|
44
|
-
roster 每行标出该模型当前生效的窗口,数字按**上游写出来的进制**显示(目录写 `128000`
|
|
45
|
-
就是 `128K`、档位写 `131072` 也是 `128K`,不做进制换算;策展表来的值带 `*` 号与悬停
|
|
46
|
-
说明——平台未亲口声明)。
|
|
28
|
+
- 同一 tab 下方有**「出图工具」**卡:开启后支持文生图、图生图与多图编辑。它与「接入为 DSH 模型」是两条独立开关,开关值立即保存、**工具的挂载发生在下次 Host 启动**。出图是异步的:一次没出好会返回 `status: pending` 与 `jobId`,带上它再调一次即可续查(不必重出)。协议细节见 [docs/SPIKE-image-gen.md](docs/SPIKE-image-gen.md)(2026-10-10 实测,v0.2 起已实现)。
|
|
29
|
+
- 卡片底部有**「上下文窗口覆盖」**选择器(不覆盖 / 128K / 256K / 512K / 1M):魔搭目录不返回窗口字段,插件按「目录声明 → 你选的档位 → 内置策展表(models.dev 交叉真值)→ 128K 兜底」四级兜底;覆盖压过策展表。档位来源与「为什么不给输出上限内置策展表」见 [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md)。
|
|
30
|
+
|
|
31
|
+
**「接入」tab**——粘贴一枚 `ms-…` 访问令牌即完成凭据配置(令牌永不入日志、永不进插件目录)。没有令牌时面板会提示你到这里来填。
|
|
47
32
|
|
|
48
33
|
## 对话框胶囊(v0.2 起)
|
|
49
34
|
|
|
50
|
-
注册 provider
|
|
35
|
+
注册 provider 之后,输入框工具栏会多一枚用量胶囊,不用打开 Plugins 页也能看到额度:
|
|
51
36
|
|
|
52
37
|
```
|
|
53
38
|
魔搭 · 143 魔粒 · 今日本地 7 次
|
|
54
39
|
```
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
41
|
+
实际长这样(输入框工具栏,不用开 Plugins 页):
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
- 前半是官方魔粒余额(与面板头条同一个数);后半是**今日本地调用次数**(只统计经本插件的调用)——两个数字来源不同,所以后段必须带「本地」。
|
|
46
|
+
- **只在当前选中的 provider 是 `modelscope-token-plan` 时出现**,切到别的 provider 整块消失、不发任何请求。
|
|
47
|
+
- **没有百分比、没有黄色档**:魔粒是存量、官方没有发布上限或低额阈值,造进度条 / 阈值都是给沒人公布过的分母配数。三档只有绿(正)/ 红(官方确为 0)/ 灰(读不到)。
|
|
48
|
+
- **读不到 ≠ 0**:读不到时该段渲染「—」,0 只在官方真的给 0 时出现并走告警色;读失败保留上次数值并标「上次数据」。
|
|
49
|
+
|
|
50
|
+
## 安装
|
|
60
51
|
|
|
61
|
-
|
|
52
|
+
1. 在 DSH「插件」页搜索 `dsh-connect-modelscope-token-plan` 点击安装,或运行:
|
|
62
53
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
54
|
+
```bash
|
|
55
|
+
dsh plugin --profile web add dsh-connect-modelscope-token-plan # Web 端
|
|
56
|
+
dsh plugin --profile desktop add dsh-connect-modelscope-token-plan # 桌面端
|
|
57
|
+
```
|
|
66
58
|
|
|
67
|
-
|
|
59
|
+
2. **完全退出 DSH(含托盘)再启动**。
|
|
60
|
+
|
|
61
|
+
3. 打开 DSH 的 **Plugins 页**,找到本插件的插件卡(面板是页内联卡片,**不在侧边栏**)。
|
|
62
|
+
|
|
63
|
+
> 装到 npm 的那一份**不含 `docs/`**(`package.json` 的 `files` 白名单只带 `lib/` / `client.js` / `assets/` / `README.md` / `CHANGELOG.md` / `LICENSE` 等),下文指向 `docs/*` 的链接在已安装副本上打不开,请到 GitHub 仓库看。
|
|
68
64
|
|
|
69
65
|
## 免费额度怎么算(2026-10 快照,以官方为准)
|
|
70
66
|
|
|
@@ -73,32 +69,43 @@ roster 每行标出该模型当前生效的窗口,数字按**上游写出来
|
|
|
73
69
|
- 额度按天重置;耗尽返回 429。
|
|
74
70
|
- `/v1/models` 免认证可读,**不消耗额度**。
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
「额度怎么算」,不参与任何计算**——面板不消费次数上限,「还剩几次」算不出来(见下节)。
|
|
72
|
+
上面这些数字来自社区公开信息、非官方数据,官方调整后需手动更新本节;**它们只用于解释「额度怎么算」,不参与任何计算**——面板不消费次数上限,「还剩几次」算不出来。
|
|
78
73
|
|
|
79
|
-
##
|
|
74
|
+
## 运维诊断:这台机器现在开没开 provider?
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
| v0.2.0 的十二轮修复细节(已冻结,非入门路径) | [docs/archive/2026-10-05-closeout.md](docs/archive/2026-10-05-closeout.md) |
|
|
88
|
-
| 上游端点全景 / 第三方方案对比 | [docs/REFERENCES.md](docs/REFERENCES.md)、[docs/REFERENCE-modelsdev.md](docs/REFERENCE-modelsdev.md) |
|
|
89
|
-
| v1 要做什么、做到哪了 | [docs/ROADMAP.md](docs/ROADMAP.md) |
|
|
90
|
-
| Host/Client 两半怎么分 | [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md)(§14 红线)+ [docs/IMPLEMENTATION.md](docs/IMPLEMENTATION.md);本仓库暂无独立的 `ARCHITECTURE.md` |
|
|
91
|
-
| 开发者校验工具(变异测试 / 覆盖率) | [tools/dev/README.md](tools/dev/README.md);发布流程见 [RELEASING.md](RELEASING.md) |
|
|
76
|
+
provider / 出图 / 上下文窗口的生效值存在插件私有状态文件里,不在任何配置或路由上,按 profile 分段。查「到底开没开」用 doctor,它只读状态文件、不碰凭据,Host 没起也能跑:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm run doctor # 人读:每个 profile 的 provider / draw 开关与模型清单
|
|
80
|
+
npm run doctor:json # 机器读:JSON(可进你的巡检 / 工单脚本)
|
|
81
|
+
```
|
|
92
82
|
|
|
93
83
|
## 诚实声明
|
|
94
84
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
- 429
|
|
98
|
-
- **单模型「额度耗尽」是推断,不是读数**:官方只有账户级魔粒余额,没有 per-model
|
|
99
|
-
-
|
|
100
|
-
- **provider 默认关**(opt-in
|
|
101
|
-
- **已知限制:`reasoning` 恒为 false
|
|
102
|
-
- **输出上限默认是 harness 的 32768
|
|
103
|
-
-
|
|
104
|
-
-
|
|
85
|
+
- 面板头条是**官方魔粒余额**(真实值);本地计数只统计**本客户端(本 profile)内经本插件的调用**——它回答分布/趋势/429 事件流,不是余额,且桌面端与 Web 端各记各的、互不可见(迁移窗口期旧共享布局以 `shared (legacy)` 报出)。读不到时渲染「—」,不是 0。
|
|
86
|
+
- **本地计数与 harness 全局账本刻意不合并**:DSH 自己的 `@linxin666/dsh-usage` 只记成功调用的 token、且失败一次都不记;本插件只补它答不了的两件事——① 经本插件的分布与趋势;② **429 / 错误事件流**(官方只给总额度余额,不给「哪个模型刚才在限频」,这是本插件的独家数据)。
|
|
87
|
+
- 429 / 错误事件读的是 `llm-error-fix` 重分类之后的 code;观察层套在重分类层之外,顺序不可反。
|
|
88
|
+
- **单模型「额度耗尽」是推断,不是读数**:官方只有账户级魔粒余额,没有 per-model 额度端点;最近一次真耗尽落在推断窗口内时 picker 不再提供它、面板灰显禁勾选,事件移出窗口即恢复(恢复不可观测是已知代价)。判定见 [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md) §15。
|
|
89
|
+
- 计数口径:每次流结束记一次 call,**含失败、含用户中断**;token 数上游没给时是 `null` 而非 0。重试各记一次(魔搭按次数计费,每次重发都真打了一次上游;完整论述见 `src/host/usage-observer.ts`)。
|
|
90
|
+
- **provider 默认关**(opt-in):面板「模型」tab 的「接入为 DSH 模型」开关翻转即生效,面板保存值优先于 `cordis.patch.yml` 的默认。
|
|
91
|
+
- **已知限制:`reasoning` 恒为 false**:本插件保守默认不发 `reasoning_effort`(模型用自己的默认),也不提供思考强度选择器——值不被校验、且无法区分「生效」与「静默忽略」。完整论证见 [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md) §4。
|
|
92
|
+
- **输出上限默认是 harness 的 32768,不是魔搭声明的数**:魔搭 `/v1/models` 从不返回输出上限字段,本插件策略是「目录声明了才声明」,所以每个模型的 descriptor 都不带 `maxTokens`,harness 用缺省 32768 兜底;面板「最大输出」徽章显示生效值,**没带徽章 = 正在吃 32768 兜底**。精确覆盖在 `cordis.patch.yml` 的 `maxTokensOverrides`。为什么不像上下文窗口那样内置策展表:平台从不声明、也没逐模型实测,策展一张「猜能吐多少」的表就是在编数字(详见 [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md))。
|
|
93
|
+
- 次数口径的「推算剩余 / 参考上限」已从面板移除(官方改魔粒计费后两个单位并排是误导),配置里的 `dailyQuotaTotal` / `dailyQuotaPerModel` 两个社区快照常数也一并删除——面板不消费它们,留着只会让人以为能算「还剩几次」。
|
|
94
|
+
- 429 响应形状未实测,解析按 OpenAI 惯例兼容,漂移时面板给 `shapeWarnings`。
|
|
95
|
+
- 面板「模型」tab 的每个模型外链到其魔搭详情页(单价只在网页展示、不在官方 API),所以只是**人工核对用的跳转,不是数据源**。
|
|
96
|
+
|
|
97
|
+
## 文档
|
|
98
|
+
|
|
99
|
+
- [docs/SPIKE.md](docs/SPIKE.md) — 上游到底有没有额度信号(实测记录 + 结论)
|
|
100
|
+
- [docs/SPIKE-image-gen.md](docs/SPIKE-image-gen.md) — 出图协议实测(2026-10-10;v0.2 起已实现)
|
|
101
|
+
- [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md) — provider 接入实现契约(设计裁定都在这里)
|
|
102
|
+
- [docs/IMPLEMENTATION.md](docs/IMPLEMENTATION.md) — 这版怎么做出来的、踩过什么坑
|
|
103
|
+
- [docs/REFERENCES.md](docs/REFERENCES.md)、[docs/REFERENCE-modelsdev.md](docs/REFERENCE-modelsdev.md) — 上游端点全景 / 第三方方案对比
|
|
104
|
+
- [docs/ROADMAP.md](docs/ROADMAP.md) — v1 要做什么、做到哪了
|
|
105
|
+
- [docs/archive/](docs/archive/) — 已冻结的逐版修复档案
|
|
106
|
+
|
|
107
|
+
AI 协作会话请先读 [AGENTS.md](AGENTS.md)(验证入口、红线与文档地图);开发者校验工具(变异测试 / 覆盖率)见 [tools/dev/README.md](tools/dev/README.md);发布流程见 [RELEASING.md](RELEASING.md)。
|
|
108
|
+
|
|
109
|
+
## 许可证
|
|
110
|
+
|
|
111
|
+
MIT License,全文见 [LICENSE](LICENSE)。
|
|
Binary file
|
package/client.js
CHANGED
|
@@ -174,6 +174,8 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
174
174
|
"quota.rowTokens": "{tokens} token",
|
|
175
175
|
"quota.note": "本地口径:只统计本客户端经本插件的调用(桌面端与 Web 端各记各的);直连魔搭的其它客户端不计入。消耗总量以官方魔粒余额为准。",
|
|
176
176
|
"quota.unavailable": "本地计数本次读不到(原因见上方提示)",
|
|
177
|
+
"quota.countingOff": "本地计数只统计接入后的调用:当前未开启「接入为 DSH 模型」(开关在「模型」tab),开启后开始记录。",
|
|
178
|
+
"quota.countingPending": "「接入为 DSH 模型」已开启但尚未注册成功(见「模型」tab 状态),注册生效后本地计数开始记录。",
|
|
177
179
|
"probe.validity": "验令牌(零额度)",
|
|
178
180
|
"probe.validOk": "令牌有效(HTTP {status})",
|
|
179
181
|
"probe.fail": "失败:{error}",
|
|
@@ -326,6 +328,8 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
326
328
|
"quota.rowTokens": "{tokens} tokens",
|
|
327
329
|
"quota.note": "Local scope: only calls this client made through this plugin are counted (desktop and web keep separate ledgers); clients calling ModelScope directly are not. Total consumption is governed by the official Magicube balance.",
|
|
328
330
|
"quota.unavailable": "Local counters could not be read this time (reason in the notices above)",
|
|
331
|
+
"quota.countingOff": "Local counts come from calls made through this plugin's provider. \"Access as DSH models\" is off in the Models tab — counts start once it is on.",
|
|
332
|
+
"quota.countingPending": "\"Access as DSH models\" is on but the provider is not registered yet (see the Models tab); local counts start once registration takes effect.",
|
|
329
333
|
"probe.validity": "Verify token (free)",
|
|
330
334
|
"probe.validOk": "Token valid (HTTP {status})",
|
|
331
335
|
"probe.fail": "Failed: {error}",
|
|
@@ -1518,8 +1522,12 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
1518
1522
|
* 刻意**不画**「推算剩余/参考上限」进度条:官方已改为魔粒计费,「2000 次」
|
|
1519
1523
|
* 是次数口径的社区快照,与魔粒余额并排展示是误导(README「三条事实」#2)。
|
|
1520
1524
|
* 本地计数回答的是官方余额回答不了的问题:哪个模型在烧、何时撞的 429。
|
|
1525
|
+
*
|
|
1526
|
+
* `countingNote`(已翻译的提示文本,null = 不显示)由 PanelPage 给:本地计数的
|
|
1527
|
+
* 唯一生产者挂在注册 provider 后的适配器上,未接入时这张卡恒为 0/空——那件事
|
|
1528
|
+
* 必须说出口(2026-10-19 评审落地)。
|
|
1521
1529
|
*/
|
|
1522
|
-
function LocalDailyCard({ snapshot, tt }) {
|
|
1530
|
+
function LocalDailyCard({ snapshot, countingNote = null, tt }) {
|
|
1523
1531
|
const quota = snapshot.quota;
|
|
1524
1532
|
const daily = quota?.daily ?? { usedLocal: null };
|
|
1525
1533
|
const perModel = quota?.perModel;
|
|
@@ -1536,7 +1544,10 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
1536
1544
|
} }, tt("quota.unavailable")) : null, perModelUnknown ? null : h(ModelUsageTable, {
|
|
1537
1545
|
rows: perModelRows,
|
|
1538
1546
|
tt
|
|
1539
|
-
}), h("div", { style:
|
|
1547
|
+
}), countingNote !== null ? h("div", { style: {
|
|
1548
|
+
...S.formNote,
|
|
1549
|
+
color: "var(--dsw-alias-state-warn-primary)"
|
|
1550
|
+
} }, countingNote) : null, h("div", { style: S.trendLegend }, tt("quota.note")));
|
|
1540
1551
|
}
|
|
1541
1552
|
/** 今日单模型排行(相对最大者画条)。 */
|
|
1542
1553
|
function ModelUsageTable({ rows, tt }) {
|
|
@@ -2119,7 +2130,8 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
2119
2130
|
*
|
|
2120
2131
|
* **与 ProviderCard 的关键差异,也是这张卡存在的主要理由**:provider 开关翻转会
|
|
2121
2132
|
* 立即重注册,而出图工具的开关翻转**要等下次 Host 启动**——peer 的 tools registry
|
|
2122
|
-
* 没有 unregister
|
|
2133
|
+
* 没有 unregister(完整论述见 src/host/draw-register.ts 文件头注)。所以这张卡必须
|
|
2134
|
+
* 把这件事说出来(`draw.restartHint`),否则用户
|
|
2123
2135
|
* 翻开关后看不到任何变化,会以为坏了。
|
|
2124
2136
|
*
|
|
2125
2137
|
* 缺席原因是**分开渲染**的:`no-tools-service` 是「这个 Host 没提供该服务」(正常),
|
|
@@ -2611,6 +2623,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
2611
2623
|
cancelled = true;
|
|
2612
2624
|
};
|
|
2613
2625
|
}, [hasDrawBlock, pollRevision]);
|
|
2626
|
+
const countingNote = !(hasProviderBlock || fetchedProvider !== null) ? null : provider.enabled !== true ? tt("quota.countingOff") : provider.registered !== true ? tt("quota.countingPending") : null;
|
|
2614
2627
|
const [providerBusy, setProviderBusy] = useState(false);
|
|
2615
2628
|
const [providerError, setProviderError] = useState(null);
|
|
2616
2629
|
const writeErrorText = (reason, t) => reason instanceof Error && reason.name === "AbortError" ? t("common.timeout") : errorText(reason);
|
|
@@ -2761,6 +2774,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
2761
2774
|
tt
|
|
2762
2775
|
}, h(LocalDailyCard, {
|
|
2763
2776
|
snapshot: snap,
|
|
2777
|
+
countingNote,
|
|
2764
2778
|
tt
|
|
2765
2779
|
})), h(SectionCard, {
|
|
2766
2780
|
title: format(tt("section.trend"), { days: snap.trend?.days ?? 0 }),
|
package/cordis.patch.yml
CHANGED
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
# 适配器出去,每一次调用与每一条 429/错误事件都由 src/host/usage-observer.ts
|
|
63
63
|
# 在流的出口记进同一个 usage-store。口径是**本客户端**:桌面端与 Web 端是
|
|
64
64
|
# 两个 profile,各记各的账、互不可见(`npm run doctor` 并排列出各 profile),
|
|
65
|
-
#
|
|
66
|
-
#
|
|
65
|
+
# 口径的历史与裁定见 docs/PROVIDER-M4.md;别把它当跨端总量。直连魔搭的
|
|
66
|
+
# 其它客户端同样不计入(本插件看不见它们),消耗总量以官方魔粒余额为准。
|
|
67
67
|
#
|
|
68
68
|
# 偏差方向是**按流计次**:重试不在流内部发生,而是流被消费完后由
|
|
69
69
|
# agent/request-error 瀑布决定重发,因而每次重试各记一次(按「魔搭按
|
|
@@ -103,7 +103,8 @@
|
|
|
103
103
|
# 出图计数——那会变成拿 chat 的口径给生图记账。
|
|
104
104
|
#
|
|
105
105
|
# 生效时机:开关值立即保存,但**工具的挂载发生在下次 Host 启动**——
|
|
106
|
-
# peer 的 tools registry 没有 unregister
|
|
106
|
+
# peer 的 tools registry 没有 unregister,工具一旦注册就摘不下来(机制与
|
|
107
|
+
# 后果的完整论述见 src/host/draw-register.ts 文件头注)。
|
|
107
108
|
# 面板会就这一点给出提示(draw.restartHint)。
|
|
108
109
|
drawEnabled: false
|
|
109
110
|
# 出图默认模型(空 = 自动取目录里第一个出图模型)。
|
package/lib/host-config.js
CHANGED
|
@@ -1141,7 +1141,7 @@ const inject = ["webServer"];
|
|
|
1141
1141
|
* 产物反而引入第二份真源;这里用常量、由 test/config.test.mjs 钉住与
|
|
1142
1142
|
* package.json 一致。
|
|
1143
1143
|
*/
|
|
1144
|
-
const PLUGIN_VERSION = "0.6.
|
|
1144
|
+
const PLUGIN_VERSION = "0.6.5";
|
|
1145
1145
|
/**
|
|
1146
1146
|
* 默认值。刻意不含额度常数——理由见本文件头。
|
|
1147
1147
|
* 面板头条是官方魔粒余额,次数口径的推算已在面板收敛时删除,这里若留着
|
package/lib/index.js
CHANGED
|
@@ -985,6 +985,15 @@ function createFileUsageStore({ name, profile = null, retentionDays = 90, maxEve
|
|
|
985
985
|
const file = join(dir, "usage.json");
|
|
986
986
|
/** 最近一次解析异常(损坏即忽略的痕迹),快照转 shapeWarnings。 */
|
|
987
987
|
let anomaly = null;
|
|
988
|
+
/**
|
|
989
|
+
* 最近一次**写**失败的痕迹。
|
|
990
|
+
*
|
|
991
|
+
* 与 {@link anomaly} 分开记,因为两者的寿命不同:解析异常是**读**出来的,下一次
|
|
992
|
+
* 读就会覆盖它(读到好的就恢复);写失败是**写**出来的,只有一次成功的写才能
|
|
993
|
+
* 抹掉它——读得动旧值不代表上一次写落了盘(读回来的永远是上一次**成功**写下去
|
|
994
|
+
* 的那一份)。
|
|
995
|
+
*/
|
|
996
|
+
let writeAnomaly = null;
|
|
988
997
|
/** 只读闸:磁盘版本比本构建新时置位——读照常、写拒绝,绝不 clobber。 */
|
|
989
998
|
let readOnly = false;
|
|
990
999
|
/** 最近一次确认过的内存态;读路径拿它,写路径以它为基线。 */
|
|
@@ -1046,10 +1055,29 @@ function createFileUsageStore({ name, profile = null, retentionDays = 90, maxEve
|
|
|
1046
1055
|
return parsed.payload;
|
|
1047
1056
|
};
|
|
1048
1057
|
const cache = createStateReadCache(readThrough, { now: () => Date.now() });
|
|
1049
|
-
/**
|
|
1058
|
+
/**
|
|
1059
|
+
* 把内存态写盘(原子),失败不抛——计数是尽力而为的观测,不是事务。
|
|
1060
|
+
*
|
|
1061
|
+
* 但「不抛」不等于「不说」。这一句注释原先与实现相反:writeStateFile 失败会**抛**
|
|
1062
|
+
* (state-store.ts 的 writeStateFile 在 catch 里重抛),抛给谁?本仓的用法是
|
|
1063
|
+
* index.ts 那句 `.catch(() => {})`(理由见那里:观测失败不是对话失败)——于是
|
|
1064
|
+
* 一次写失败被彻底咽掉:面板继续显示上一次成功落盘的数字,计数看起来是活的,
|
|
1065
|
+
* 其实早就冻住了,而**没有任何人知道**(没有日志,没有 shapeWarning)。
|
|
1066
|
+
*
|
|
1067
|
+
* 所以失败要留两处痕迹:日志(给操作者)与 {@link writeAnomaly}(进快照的
|
|
1068
|
+
* shapeWarnings,给面板)。吞掉的是失败,不是失败的原因。
|
|
1069
|
+
*/
|
|
1050
1070
|
const persist = async (payload) => {
|
|
1051
|
-
|
|
1052
|
-
|
|
1071
|
+
try {
|
|
1072
|
+
await ensureStateDir(dir);
|
|
1073
|
+
await writeStateFile(file, JSON.stringify(payload), { temporary: temporaryOf(dir, "usage.json") });
|
|
1074
|
+
} catch (error) {
|
|
1075
|
+
const note = `usage-state: write failed (${redactSecrets(errMsg(error))})`;
|
|
1076
|
+
writeAnomaly = note;
|
|
1077
|
+
logger?.warn?.(`${name}: ${note} — the local counters are not reaching disk; the panel still shows the last values that did`);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
writeAnomaly = null;
|
|
1053
1081
|
cache.remember(payload);
|
|
1054
1082
|
};
|
|
1055
1083
|
/**
|
|
@@ -1266,9 +1294,14 @@ function createFileUsageStore({ name, profile = null, retentionDays = 90, maxEve
|
|
|
1266
1294
|
if (payload === null) return await pathExists(file) ? null : [];
|
|
1267
1295
|
return [...payload.events ?? []];
|
|
1268
1296
|
},
|
|
1269
|
-
/**
|
|
1297
|
+
/**
|
|
1298
|
+
* 最近一次异常(无异常为 null)——快照转 shapeWarnings。
|
|
1299
|
+
*
|
|
1300
|
+
* 写失败的痕迹优先于读侧的解析异常:它更近、也更严重(解析异常说「这份文件
|
|
1301
|
+
* 读不懂」,写失败说「我们刚写的东西没落盘」)。
|
|
1302
|
+
*/
|
|
1270
1303
|
anomaly() {
|
|
1271
|
-
return anomaly;
|
|
1304
|
+
return writeAnomaly ?? anomaly;
|
|
1272
1305
|
}
|
|
1273
1306
|
};
|
|
1274
1307
|
}
|
|
@@ -1738,6 +1771,24 @@ function createInferenceClient({ settings, tokenStore, deps = {}, logger }) {
|
|
|
1738
1771
|
}
|
|
1739
1772
|
return Array.from(new Set(tasks));
|
|
1740
1773
|
};
|
|
1774
|
+
/**
|
|
1775
|
+
* 令牌的稳定指纹(FNV-1a,32 位 hex)——只用作余额缓存的键。
|
|
1776
|
+
*
|
|
1777
|
+
* **不用 `token.slice(-6)` 那种末位截断**:两个不同令牌末 6 位相同的概率在
|
|
1778
|
+
* 1/16^7 量级,虽低但非零,而代价是一个 TTL(cacheSeconds,默认 60s)内把
|
|
1779
|
+
* A 的身份的余额当 B 的返回——缓存键的正确性不该交给运气。指纹不需要保密、
|
|
1780
|
+
* 不进日志(脱敏正则的形状与它无关),完整的哈希零成本。
|
|
1781
|
+
* @param {string} token - 访问令牌。
|
|
1782
|
+
* @returns {string} 8 位 hex 指纹。
|
|
1783
|
+
*/
|
|
1784
|
+
function tokenFingerprint(token) {
|
|
1785
|
+
let hash = 2166136261;
|
|
1786
|
+
for (let i = 0; i < token.length; i++) {
|
|
1787
|
+
hash ^= token.charCodeAt(i);
|
|
1788
|
+
hash = Math.imul(hash, 16777619);
|
|
1789
|
+
}
|
|
1790
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1791
|
+
}
|
|
1741
1792
|
return {
|
|
1742
1793
|
/**
|
|
1743
1794
|
* 模型目录:GET apiBase/models,免认证、零额度,coalesced 缓存
|
|
@@ -1831,13 +1882,14 @@ function createInferenceClient({ settings, tokenStore, deps = {}, logger }) {
|
|
|
1831
1882
|
/**
|
|
1832
1883
|
* 官方「魔粒」余额:GET siteBase/openapi/v1/magicubes/balance(Bearer
|
|
1833
1884
|
* 令牌;2026-10-04 实测可用,匿名 401,SPIKE.md §魔粒)。coalesced 缓存
|
|
1834
|
-
* cacheSeconds
|
|
1885
|
+
* cacheSeconds,缓存键带令牌指纹(FNV-1a 完整哈希,不是末位截断——
|
|
1886
|
+
* 换令牌 ≤1 个 TTL 内必然读到新身份,且不同令牌不会因同后缀串味)。
|
|
1835
1887
|
*/
|
|
1836
1888
|
async fetchBalance() {
|
|
1837
1889
|
const { value: token } = await tokenStore.resolve();
|
|
1838
1890
|
if (token === "") throw pluginError(CODE.AUTH_ERROR, "no ModelScope token configured — set MODELSCOPE_API_KEY or save one in the panel");
|
|
1839
1891
|
const url = settings.siteBase + "/openapi/v1/magicubes/balance";
|
|
1840
|
-
const key = "balance#" + token
|
|
1892
|
+
const key = "balance#" + tokenFingerprint(token);
|
|
1841
1893
|
return await read(key, async () => {
|
|
1842
1894
|
const response = await fetchWithTimeout(url, {
|
|
1843
1895
|
method: "GET",
|
|
@@ -2617,15 +2669,28 @@ function createFileDrawStore(options = {}) {
|
|
|
2617
2669
|
//#region src/host/draw-jobs.ts
|
|
2618
2670
|
/**
|
|
2619
2671
|
* 建一张出图作业表。
|
|
2620
|
-
* @param {DrawJobsOptions} [options] -
|
|
2672
|
+
* @param {DrawJobsOptions} [options] - 容量与保护窗参数。
|
|
2621
2673
|
* @returns {DrawJobs} 作业表。
|
|
2622
2674
|
*/
|
|
2623
2675
|
function createDrawJobs(options = {}) {
|
|
2624
2676
|
const maxJobs = Math.max(1, Math.floor(options.maxJobs ?? 64));
|
|
2677
|
+
const resumeWindowMs = Math.max(1, Math.floor(options.resumeWindowMs ?? 9e5));
|
|
2678
|
+
const hardMaxJobs = Math.max(maxJobs, Math.floor(options.hardMaxJobs ?? maxJobs * 2));
|
|
2625
2679
|
/** @type {Map<string, DrawJob>} 插入顺序即淘汰顺序(Map 保序)。 */
|
|
2626
2680
|
const jobs = /* @__PURE__ */ new Map();
|
|
2627
|
-
|
|
2628
|
-
|
|
2681
|
+
/**
|
|
2682
|
+
* 超容时的两级淘汰(判据见文件头「生命周期」):
|
|
2683
|
+
* 软上限只淘汰已过续查保护窗的(越过总放弃线,续查只换来「超过总预算」);
|
|
2684
|
+
* 硬上限保底——全是保护窗内的作业也绝不无界。
|
|
2685
|
+
* @param {number} now - 当前墙钟毫秒(`create` 的 `now` 缝传进来,测试可造旧作业)。
|
|
2686
|
+
*/
|
|
2687
|
+
const evictIfNeeded = (now) => {
|
|
2688
|
+
if (jobs.size <= maxJobs) return;
|
|
2689
|
+
for (const [id, job] of jobs) {
|
|
2690
|
+
if (jobs.size <= maxJobs) return;
|
|
2691
|
+
if (now - job.submittedAt >= resumeWindowMs) jobs.delete(id);
|
|
2692
|
+
}
|
|
2693
|
+
while (jobs.size > hardMaxJobs) {
|
|
2629
2694
|
const oldest = jobs.keys().next();
|
|
2630
2695
|
if (oldest.done === true) return;
|
|
2631
2696
|
jobs.delete(oldest.value);
|
|
@@ -2634,18 +2699,19 @@ function createDrawJobs(options = {}) {
|
|
|
2634
2699
|
return {
|
|
2635
2700
|
create({ taskId, model, prompt, now }) {
|
|
2636
2701
|
const jobId = taskId;
|
|
2702
|
+
const at = typeof now === "number" && Number.isFinite(now) ? now : Date.now();
|
|
2637
2703
|
const job = {
|
|
2638
2704
|
jobId,
|
|
2639
2705
|
taskId,
|
|
2640
2706
|
model,
|
|
2641
2707
|
prompt,
|
|
2642
|
-
submittedAt:
|
|
2708
|
+
submittedAt: at,
|
|
2643
2709
|
polls: 0
|
|
2644
2710
|
};
|
|
2645
2711
|
const previous = jobs.get(jobId);
|
|
2646
2712
|
if (previous !== void 0) job.polls = previous.polls;
|
|
2647
2713
|
jobs.set(jobId, job);
|
|
2648
|
-
evictIfNeeded();
|
|
2714
|
+
evictIfNeeded(at);
|
|
2649
2715
|
return { ...job };
|
|
2650
2716
|
},
|
|
2651
2717
|
get(jobId) {
|
|
@@ -3286,6 +3352,9 @@ function createDrawCooldown(cooldownMs) {
|
|
|
3286
3352
|
* 轮询只与更慢相关。
|
|
3287
3353
|
* 3. 失败原因实测可能**不可知**(`errors.code:0` + 空 message)。`describeDrawFailure`
|
|
3288
3354
|
* 因此明说「未给出原因」,本文件也**不从空错误里编造**。
|
|
3355
|
+
* 4. 轮询的 5xx / 429 / 408 是**瞬态**不是终局({@link isTransientPollStatus}):
|
|
3356
|
+
* 同一个物理故障不因表现为 TCP 抛错还是 5xx 响应就两种判法;只有 4xx 是
|
|
3357
|
+
* 终局证据。瞬态持续时由 deadline 收口成 pending,续查把手不丢。
|
|
3289
3358
|
*
|
|
3290
3359
|
* @module dsh-connect-modelscope-token-plan/draw-runtime
|
|
3291
3360
|
*/
|
|
@@ -3393,8 +3462,10 @@ async function submitDrawTask({ fetchImpl, apiBase, apiKey, model, prompt, image
|
|
|
3393
3462
|
* 抛错是「这件事做不成了」,`pending` 是「还没成,可以再查」。同一段循环因此
|
|
3394
3463
|
* 同时服务于两种调用形状(一次性跑完 / 分段续查),不写两份。
|
|
3395
3464
|
*
|
|
3396
|
-
* 轮询带 `X-ModelScope-Task-Type
|
|
3397
|
-
*
|
|
3465
|
+
* 轮询带 `X-ModelScope-Task-Type`(**必需**,漏了必假失败)。三类情形**不升级
|
|
3466
|
+
* 为终局**,在本次预算内继续等下一轮:① 传输层抛错(ECONNRESET / DNS);
|
|
3467
|
+
* ② 非 JSON / 200 垃圾响应体(CDN 错误页);③ **瞬态 HTTP 状态码**(5xx /
|
|
3468
|
+
* 429 / 408,见 {@link isTransientPollStatus})。只有 4xx 是终局证据。
|
|
3398
3469
|
* @param {object} options - 等待参数。
|
|
3399
3470
|
* @param {DrawFetch} options.fetchImpl - 注入的 fetch。
|
|
3400
3471
|
* @param {string} options.apiBase - apiBase。
|
|
@@ -3453,6 +3524,7 @@ async function awaitDrawTask({ fetchImpl, apiBase, apiKey, taskId, waitMs = DRAW
|
|
|
3453
3524
|
}
|
|
3454
3525
|
const pollText = await safeText(polled);
|
|
3455
3526
|
if (!polled.ok) {
|
|
3527
|
+
if (isTransientPollStatus(polled.status)) continue;
|
|
3456
3528
|
const previous = lastStatus !== void 0 ? `(上一轮读到 ${lastStatus})` : "";
|
|
3457
3529
|
return {
|
|
3458
3530
|
kind: "failed",
|
|
@@ -3741,7 +3813,10 @@ function defineDrawTool({ defineTool, fetchImpl, resolveApiKey, getEntries, sett
|
|
|
3741
3813
|
const images = [];
|
|
3742
3814
|
for (const raw of rawImages) {
|
|
3743
3815
|
const normalized = normalizeImageUrlInput(raw);
|
|
3744
|
-
if (normalized === null)
|
|
3816
|
+
if (normalized === null) {
|
|
3817
|
+
const safe = redactSecrets(raw).slice(0, 80);
|
|
3818
|
+
throw new Error(`imageUrl 不可用:${safe} —— 只接受公网 http(s) 链接或 data:image/...;base64 内联数据(本地文件上游取不到,请先上传)`);
|
|
3819
|
+
}
|
|
3745
3820
|
images.push(normalized);
|
|
3746
3821
|
}
|
|
3747
3822
|
try {
|
|
@@ -3805,6 +3880,24 @@ function toPositiveNumber(value) {
|
|
|
3805
3880
|
const n = Number(value);
|
|
3806
3881
|
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
3807
3882
|
}
|
|
3883
|
+
/**
|
|
3884
|
+
* 轮询响应里的**瞬态** HTTP 状态码:不判作业死刑,本次等待预算内继续等下一轮。
|
|
3885
|
+
*
|
|
3886
|
+
* 判据是「同一个物理故障不因表现形态而异」:传输层抛错(ECONNRESET / DNS
|
|
3887
|
+
* 解析失败)早就走的是「继续等下一轮」,而同一种 CDN 边缘抖动、上游短暂过载
|
|
3888
|
+
* 以 **5xx/429 响应**的形态出现时,旧写法直接判失败、作业出表、进 60s 冷却
|
|
3889
|
+
* ——agent 想续查只能重新提交一张图。上游排队窗口实测 1–15 分钟
|
|
3890
|
+
* (docs/SPIKE-image-gen.md),那个窗口内的边缘 5xx 是常态噪声,不是终局。
|
|
3891
|
+
*
|
|
3892
|
+
* 4xx 保持终局:401/403 = 令牌或权限真的没了,404/400 = 任务或端点真的没了
|
|
3893
|
+
* ——这些等下去也不会变。deadline 负责收口:瞬态一直持续时耗尽预算后返回
|
|
3894
|
+
* `pending`,两段式的续查把手(jobId)不丢。
|
|
3895
|
+
* @param {number} status - 轮询响应的 HTTP 状态码。
|
|
3896
|
+
* @returns {boolean} 是否按瞬态继续等。
|
|
3897
|
+
*/
|
|
3898
|
+
function isTransientPollStatus(status) {
|
|
3899
|
+
return status === 408 || status === 429 || status >= 500;
|
|
3900
|
+
}
|
|
3808
3901
|
/** 读 body 文本,读不到给空串(错误分诊不能因为读 body 失败而失败)。 */
|
|
3809
3902
|
async function safeText(response) {
|
|
3810
3903
|
try {
|
|
@@ -5791,7 +5884,8 @@ function registerProviderRoute(ctx, wiring) {
|
|
|
5791
5884
|
*
|
|
5792
5885
|
* 一处**与 provider 路由不同**的地方必须说清:provider 开关翻转会**立即**重注册
|
|
5793
5886
|
* (publish 是活的重建),而出图工具的开关翻转**要等下次 Host 启动**——peer 的
|
|
5794
|
-
* tools registry 没有 unregister
|
|
5887
|
+
* tools registry 没有 unregister,工具一旦注册就摘不下来(完整论述见
|
|
5888
|
+
* draw-register.ts 文件头注)。所以本路由保存成功后
|
|
5795
5889
|
* 不改挂载状态,只在响应里给出 `mountPending`,让面板能说清这件事,而不是让用户
|
|
5796
5890
|
* 以为开关坏了。
|
|
5797
5891
|
*
|
|
@@ -6173,7 +6267,7 @@ function apply(ctx, config = {}, deps = {}) {
|
|
|
6173
6267
|
profile,
|
|
6174
6268
|
logger: ctx.logger
|
|
6175
6269
|
});
|
|
6176
|
-
const drawJobs = createDrawJobs();
|
|
6270
|
+
const drawJobs = createDrawJobs({ resumeWindowMs: settings.drawTimeoutMs });
|
|
6177
6271
|
const usagePrefsStore = createFileUsagePrefsStore({
|
|
6178
6272
|
profile,
|
|
6179
6273
|
logger: ctx.logger
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "ModelScope Model Access",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "One-tap connect ModelScope language and image-generation models into DSH (off by default), with image recognition and context window filled in automatically. You can also see the free Magicube balance, jump to a model's info page, and record local call counts, trends and rate-limit events, with a usage pill beside the input box showing the balance and today's local count. All of it needs only an API key, with no account sign-in."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "魔搭模型接入",
|
|
4
|
-
"description": "一键接入魔搭社区的语言、生图模型到 DSH
|
|
4
|
+
"description": "一键接入魔搭社区的语言、生图模型到 DSH(默认关),自动填入图片识别与上下文窗口;还可查看免费魔粒余额、跳转到模型信息页,记录本地调用计数、趋势、限流事件,输入框旁另有用量胶囊显示余额与今日本地调用次数。上述功能仅需填入 API key,不需要登录账号。"
|
|
5
5
|
}
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"name": "dsh-connect-modelscope-token-plan",
|
|
3
3
|
"displayName": "DSH Connect ModelScope Token Plan",
|
|
4
4
|
"icon": "./icon.svg",
|
|
5
|
-
"version": "0.6.
|
|
5
|
+
"version": "0.6.5",
|
|
6
6
|
"homepage": "https://github.com/eghrhegpe/dsh-connect-modelscope-token-plan",
|
|
7
7
|
"bugs": {
|
|
8
8
|
"url": "https://github.com/eghrhegpe/dsh-connect-modelscope-token-plan/issues"
|
|
9
9
|
},
|
|
10
10
|
"author": "eghrhegpe",
|
|
11
|
-
"description": "
|
|
11
|
+
"description": "One-tap connect ModelScope language and image-generation models into DSH (off by default), with image recognition and context window filled in automatically. You can also see the free Magicube balance, jump to a model's info page, and record local call counts, trends and rate-limit events, with a usage pill beside the input box showing the balance and today's local count. All of it needs only an API key, with no account sign-in.",
|
|
12
12
|
"keywords": [
|
|
13
13
|
"dsh-plugin",
|
|
14
14
|
"deepseek-harness",
|
|
@@ -45,6 +45,10 @@
|
|
|
45
45
|
"test:mutate": "node tools/dev/mutate.mjs tools/dev/mutate-spec.json",
|
|
46
46
|
"doctor": "node tools/doctor.mjs",
|
|
47
47
|
"doctor:json": "node tools/doctor.mjs --json",
|
|
48
|
+
"host-range:update": "node tools/update-host-range.mjs",
|
|
49
|
+
"host-range:check": "node tools/update-host-range.mjs --check",
|
|
50
|
+
"market-rules:update": "node tools/check-market-rules.mjs",
|
|
51
|
+
"market-rules:check": "node tools/check-market-rules.mjs --check",
|
|
48
52
|
"build": "tsdown -c tsdown.config.mjs",
|
|
49
53
|
"prepack": "npm run build"
|
|
50
54
|
},
|
|
@@ -74,22 +78,48 @@
|
|
|
74
78
|
"@deepseek-ai/dsh-settings": ">=0.1.5 <0.3",
|
|
75
79
|
"@deepseek-ai/dsh-tools": ">=0.1.5 <0.3",
|
|
76
80
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
77
|
-
"@earendil-works/pi-ai": "
|
|
81
|
+
"@earendil-works/pi-ai": ">=0.85.0 <2.0.0"
|
|
78
82
|
},
|
|
79
83
|
"peerDependenciesMeta": {
|
|
80
|
-
"@deepseek-ai/cordis": {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"@deepseek-ai/dsh-client-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"@deepseek-ai/dsh-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"@deepseek-ai/dsh-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"@
|
|
84
|
+
"@deepseek-ai/cordis": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
87
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
88
|
+
"optional": true
|
|
89
|
+
},
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-layout": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
93
|
+
"@deepseek-ai/dsh-client-ui-renderer": {
|
|
94
|
+
"optional": true
|
|
95
|
+
},
|
|
96
|
+
"@deepseek-ai/dsh-credentials": {
|
|
97
|
+
"optional": true
|
|
98
|
+
},
|
|
99
|
+
"@deepseek-ai/dsh-home-paths": {
|
|
100
|
+
"optional": true
|
|
101
|
+
},
|
|
102
|
+
"@deepseek-ai/dsh-host-webserver": {
|
|
103
|
+
"optional": true
|
|
104
|
+
},
|
|
105
|
+
"@deepseek-ai/dsh-llm": {
|
|
106
|
+
"optional": true
|
|
107
|
+
},
|
|
108
|
+
"@deepseek-ai/dsh-llm-pi-ai": {
|
|
109
|
+
"optional": true
|
|
110
|
+
},
|
|
111
|
+
"@deepseek-ai/dsh-settings": {
|
|
112
|
+
"optional": true
|
|
113
|
+
},
|
|
114
|
+
"@deepseek-ai/dsh-tools": {
|
|
115
|
+
"optional": true
|
|
116
|
+
},
|
|
117
|
+
"@deepseek-ai/schemastery": {
|
|
118
|
+
"optional": true
|
|
119
|
+
},
|
|
120
|
+
"@earendil-works/pi-ai": {
|
|
121
|
+
"optional": true
|
|
122
|
+
}
|
|
93
123
|
},
|
|
94
124
|
"engines": {
|
|
95
125
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -101,6 +131,7 @@
|
|
|
101
131
|
"license": "MIT",
|
|
102
132
|
"devDependencies": {
|
|
103
133
|
"@types/node": "^22.19.0",
|
|
134
|
+
"js-yaml": "^5.3.0",
|
|
104
135
|
"jscpd": "^5.4.0",
|
|
105
136
|
"jsdom": "^30.1.3",
|
|
106
137
|
"react": "18.3.1",
|