dsh-connect-modelscope-token-plan 0.6.6 → 0.7.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 +19 -0
- package/README.md +1 -1
- package/client.js +18 -15
- package/cordis.patch.yml +3 -4
- package/lib/host-config.js +1 -1
- package/lib/index.js +170 -80
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -26,6 +26,25 @@
|
|
|
26
26
|
> **发版时**:顶部开 `## [X.Y.Z] — YYYY-MM-DD`,完整流程见
|
|
27
27
|
> [RELEASING.md](RELEASING.md) §3(本文开头这段是**写作者规范**,那是**发布操作流程**)。
|
|
28
28
|
|
|
29
|
+
## [0.7.0] — 2026-10-26
|
|
30
|
+
|
|
31
|
+
**出图开关当场生效,不用再重启 DSH**
|
|
32
|
+
|
|
33
|
+
- 翻开「出图工具」开关,工具立刻可用;关掉立刻从工具列表消失。此前面板只提示「下次
|
|
34
|
+
启动 DSH 才挂载」——挂上之后摘不掉,于是关掉开关工具仍会被调起。
|
|
35
|
+
- 卸载或热重载插件时工具随之下线,不在宿主的工具注册表里留下一个已失效却仍占名字的
|
|
36
|
+
僵尸条目(旧行为下同名重挂会被宿主拒绝)。
|
|
37
|
+
- 旧版 Host 若不返回注销句柄,开关仍能挂上但摘不掉:面板直说「本机 Host 不支持卸载」,
|
|
38
|
+
不再假装已经关掉。
|
|
39
|
+
|
|
40
|
+
**面板:「下次启动」的提示只剩失败场景**
|
|
41
|
+
|
|
42
|
+
- 这条提示改成瞬时读数:只在开关已开、几秒内还没挂上、且还查不出原因时出现;查得出
|
|
43
|
+
原因的(缺 tools 服务 / 模块加载失败 / 注册被拒 / 不可摘)直接把原因写在卡上。
|
|
44
|
+
|
|
45
|
+
> 「宿主没有 unregister」这个断言当年是怎么写进注释、又怎么被推翻的(含 peer 契约
|
|
46
|
+
> 取证与判据):[docs/archive/2026-10-26-0.7.0-process.md](docs/archive/2026-10-26-0.7.0-process.md)
|
|
47
|
+
|
|
29
48
|
## [0.6.6] — 2026-10-23
|
|
30
49
|
|
|
31
50
|
**对话框胶囊可以点开,还多了「· N 新模型」**
|
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|

|
|
45
45
|
|
|
46
|
-
- 同一 tab 下方有**「出图工具」**卡:开启后支持文生图、图生图与多图编辑。它与「接入为 DSH
|
|
46
|
+
- 同一 tab 下方有**「出图工具」**卡:开启后支持文生图、图生图与多图编辑。它与「接入为 DSH 模型」是两条独立开关,开关保存后**当场**挂/卸工具(不用重启)。出图是异步的:一次没出好会返回 `status: pending` 与 `jobId`,带上它再调一次即可续查(不必重出)。协议细节见 [docs/SPIKE-image-gen.md](docs/SPIKE-image-gen.md)(2026-10-10 实测,v0.2 起已实现)。
|
|
47
47
|
- 卡片底部有**「上下文窗口覆盖」**选择器(不覆盖 / 128K / 256K / 512K / 1M):魔搭目录不返回窗口字段,插件按「目录声明 → 你选的档位 → 内置策展表(models.dev 交叉真值)→ 128K 兜底」四级兜底;覆盖压过策展表。档位来源与「为什么不给输出上限内置策展表」见 [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md)。
|
|
48
48
|
|
|
49
49
|
**「接入」tab**——粘贴一枚 `ms-…` 访问令牌即完成凭据配置(令牌永不入日志、永不进插件目录)。没有令牌时面板会提示你到这里来填。
|
package/client.js
CHANGED
|
@@ -223,7 +223,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
223
223
|
"draw.busy": "切换中…",
|
|
224
224
|
"draw.mounted": "已注册:{tool}",
|
|
225
225
|
"draw.notMounted": "未注册——打开上方开关即可开启。",
|
|
226
|
-
"draw.restartHint": "
|
|
226
|
+
"draw.restartHint": "开关立即生效;工具正在挂载,若几秒后仍未挂上,上方会给出原因。",
|
|
227
227
|
"draw.currentNone": "当前目录里没有可出图的模型——出图会失败(先确认访问令牌已配置、目录已拉到)。",
|
|
228
228
|
"draw.currentSrc.declaredTextToImage": "平台声明为文生图",
|
|
229
229
|
"draw.currentSrc.knownTextToImage": "已知可纯文生图",
|
|
@@ -245,6 +245,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
245
245
|
"draw.absent.noTools": "开关已开,但本机没有 tools 服务,工具没挂上(Host 侧限制,不是配置问题)。",
|
|
246
246
|
"draw.absent.peerLoad": "开关已开,但 tools 模块加载失败,出图工具缺席(Host 问题,非配置)。",
|
|
247
247
|
"draw.absent.registry": "开关已开,但工具注册被 Host 拒绝,出图工具缺席(Host 问题,非配置)。",
|
|
248
|
+
"draw.absent.notRemovable": "工具已注册,但本机 Host 不返回注销句柄——关掉开关也摘不掉它(旧 Host 限制)。",
|
|
248
249
|
"draw.absent.unknown": "开关已开,但出图工具未挂载,原因未知。",
|
|
249
250
|
"draw.preferredGone": "已保存的默认模型 {model} 已不在当前目录(下架或改名)——出图会用它失败,请在下面重选。",
|
|
250
251
|
"trend.none": "还没有本地调用记录。",
|
|
@@ -386,7 +387,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
386
387
|
"draw.busy": "Switching…",
|
|
387
388
|
"draw.mounted": "Registered: {tool}",
|
|
388
389
|
"draw.notMounted": "Not registered — turn the switch above on.",
|
|
389
|
-
"draw.restartHint": "The switch
|
|
390
|
+
"draw.restartHint": "The switch takes effect immediately; the tool is still mounting. If it stays unmounted, the reason appears above.",
|
|
390
391
|
"draw.currentNone": "No image model in the catalogue right now — drawing will fail (check the access token and that the catalogue has loaded).",
|
|
391
392
|
"draw.currentSrc.declaredTextToImage": "platform-declared text-to-image",
|
|
392
393
|
"draw.currentSrc.knownTextToImage": "known to accept text-to-image",
|
|
@@ -408,6 +409,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
408
409
|
"draw.absent.noTools": "The switch is on, but this Host has no tools service, so the tool is not mounted (a Host limitation, not a config problem).",
|
|
409
410
|
"draw.absent.peerLoad": "The switch is on, but the tools module failed to load; the draw tool is absent (a Host fault, not config).",
|
|
410
411
|
"draw.absent.registry": "The switch is on, but the Host refused the tool registration; the draw tool is absent (a Host fault, not config).",
|
|
412
|
+
"draw.absent.notRemovable": "The tool is registered, but this Host returns no disposer — turning the switch off cannot unmount it (an older Host limitation).",
|
|
411
413
|
"draw.absent.unknown": "The switch is on, but the draw tool is not mounted; the reason is unknown.",
|
|
412
414
|
"draw.preferredGone": "The saved default model {model} is no longer in the catalogue (delisted or renamed) — drawing with it will fail. Pick another one below.",
|
|
413
415
|
"trend.none": "No local call records yet.",
|
|
@@ -1288,8 +1290,8 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
1288
1290
|
/**
|
|
1289
1291
|
* 把 wire/路由返回的出图块归一成 `DrawStatus`;不合法一律降级。
|
|
1290
1292
|
*
|
|
1291
|
-
* 与 `providerOf` 同一套防御性归一(旧 Host 可能缺字段)。`absentReason`
|
|
1292
|
-
*
|
|
1293
|
+
* 与 `providerOf` 同一套防御性归一(旧 Host 可能缺字段)。`absentReason` 只认四个
|
|
1294
|
+
* 已知码——上游若新增第五个,归一到 `null` 而不是把它透传成渲染不出的值;面板的
|
|
1293
1295
|
* 「缺席」措辞有兜底键,但**状态本身**宁可显示「未知」也不假装认识它。
|
|
1294
1296
|
* @param {unknown} raw - 原始块。
|
|
1295
1297
|
* @returns {DrawStatus} 归一后的状态。
|
|
@@ -1299,7 +1301,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
1299
1301
|
const d = raw;
|
|
1300
1302
|
const candidates = Array.isArray(d.candidateIds) ? d.candidateIds.map((id) => String(id ?? "")).filter((id) => id !== "") : [];
|
|
1301
1303
|
const reason = d.absentReason;
|
|
1302
|
-
const absentReason = reason === "no-tools-service" || reason === "peer-load-failed" || reason === "registry-refused" ? reason : null;
|
|
1304
|
+
const absentReason = reason === "no-tools-service" || reason === "peer-load-failed" || reason === "registry-refused" || reason === "not-removable" ? reason : null;
|
|
1303
1305
|
const rawCandidates = Array.isArray(d.candidates) ? d.candidates.map((entry) => {
|
|
1304
1306
|
const row = entry ?? {};
|
|
1305
1307
|
return {
|
|
@@ -2210,15 +2212,15 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
2210
2212
|
/**
|
|
2211
2213
|
* 出图工具卡:开关、候选模型、挂载状态。
|
|
2212
2214
|
*
|
|
2213
|
-
* **与 ProviderCard
|
|
2214
|
-
*
|
|
2215
|
-
*
|
|
2216
|
-
*
|
|
2217
|
-
*
|
|
2215
|
+
* **与 ProviderCard 的关键差异**:provider 开关翻转是「重建 provider 对」,出图工具
|
|
2216
|
+
* 的开关翻转是「注册/注销一个 agent 工具」——两者都在**本次 Host 生命周期内**生效
|
|
2217
|
+
* (peer 的 `tools.register` 返回 disposer,完整论述见 src/host/draw-register.ts 文件
|
|
2218
|
+
* 头注)。保存后路由会等挂载收敛完再回快照,所以这张卡通常直接显示「已注册」;
|
|
2219
|
+
* `draw.restartHint` 只在瞬时未挂上(或挂载失败)时出现,不再宣称「要等下次启动」。
|
|
2218
2220
|
*
|
|
2219
|
-
*
|
|
2220
|
-
*
|
|
2221
|
-
*
|
|
2221
|
+
* 缺席/不可摘原因是**分开渲染**的:`no-tools-service` 是「这个 Host 没提供该服务」
|
|
2222
|
+
* (正常),`not-removable` 是「能用但关不掉」,另两种是 Host 缺陷。每种都给出各自
|
|
2223
|
+
* 的一句话,因为「开关开着但工具不在」如果没有解释,用户没有任何办法自己诊断。
|
|
2222
2224
|
* @param {object} props - 渲染参数。
|
|
2223
2225
|
* @returns {unknown} 卡片节点。
|
|
2224
2226
|
*/
|
|
@@ -2273,7 +2275,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
2273
2275
|
},
|
|
2274
2276
|
role: "status"
|
|
2275
2277
|
}, tt("draw.notMounted"));
|
|
2276
|
-
const restartHint = enabled && status.mountPending === true ? h("div", { style: {
|
|
2278
|
+
const restartHint = enabled && status.mountPending === true && status.absentReason === null ? h("div", { style: {
|
|
2277
2279
|
...S.formNote,
|
|
2278
2280
|
color: "var(--dsw-alias-state-warn-primary)"
|
|
2279
2281
|
} }, tt("draw.restartHint")) : null;
|
|
@@ -2431,7 +2433,8 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
2431
2433
|
DRAW_ABSENT_KEY = {
|
|
2432
2434
|
"no-tools-service": "draw.absent.noTools",
|
|
2433
2435
|
"peer-load-failed": "draw.absent.peerLoad",
|
|
2434
|
-
"registry-refused": "draw.absent.registry"
|
|
2436
|
+
"registry-refused": "draw.absent.registry",
|
|
2437
|
+
"not-removable": "draw.absent.notRemovable"
|
|
2435
2438
|
};
|
|
2436
2439
|
}));
|
|
2437
2440
|
|
package/cordis.patch.yml
CHANGED
|
@@ -102,10 +102,9 @@
|
|
|
102
102
|
# · 出图**不扣**魔粒(chat 才扣,1 次 = 1 魔粒),所以面板刻意**不显示**
|
|
103
103
|
# 出图计数——那会变成拿 chat 的口径给生图记账。
|
|
104
104
|
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
# 面板会就这一点给出提示(draw.restartHint)。
|
|
105
|
+
# 生效时机:开关值保存后**当场**挂/卸工具——peer 的 tools.register 返回 disposer
|
|
106
|
+
# (注销句柄),关掉就把工具摘掉,不用重启(机制与后果的完整论述见
|
|
107
|
+
# src/host/draw-register.ts 文件头注)。旧 Host 不返回句柄时才降级为 not-removable。
|
|
109
108
|
drawEnabled: false
|
|
110
109
|
# 出图默认模型(空 = 自动取目录里第一个出图模型)。
|
|
111
110
|
# 注意只有目录内**平台声明**出图能力的模型才进候选:实测目录 35 条里
|
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.
|
|
1144
|
+
const PLUGIN_VERSION = "0.7.0";
|
|
1145
1145
|
/**
|
|
1146
1146
|
* 默认值。刻意不含额度常数——理由见本文件头。
|
|
1147
1147
|
* 面板头条是官方魔粒余额,次数口径的推算已在面板收敛时删除,这里若留着
|
package/lib/index.js
CHANGED
|
@@ -4202,11 +4202,10 @@ function sleep(ms, signal) {
|
|
|
4202
4202
|
/** 当前缺席原因(`null` = 未登记 / 已清掉)。 */
|
|
4203
4203
|
let note = null;
|
|
4204
4204
|
/**
|
|
4205
|
-
*
|
|
4205
|
+
* 此刻工具是否真的在 registry 里。
|
|
4206
4206
|
*
|
|
4207
|
-
* 与 `note` 的区别见文件头:`note`
|
|
4208
|
-
*
|
|
4209
|
-
* 只有这一位能把它认出来。
|
|
4207
|
+
* 与 `note` 的区别见文件头:`note` 是「为什么没成」,这一位是「此刻在不在」。
|
|
4208
|
+
* 开关翻转会当场重算这一位,所以它是动态读数,不是「本次挂载」的存档。
|
|
4210
4209
|
*/
|
|
4211
4210
|
let mounted = false;
|
|
4212
4211
|
/**
|
|
@@ -4252,22 +4251,33 @@ function resetDrawToolNote() {
|
|
|
4252
4251
|
//#endregion
|
|
4253
4252
|
//#region src/host/draw-register.ts
|
|
4254
4253
|
/**
|
|
4255
|
-
* 出图工具的挂载副作用:读开关 → 解析目录 → 注册 agent
|
|
4254
|
+
* 出图工具的挂载副作用:读开关 → 解析目录 → 注册 agent 工具;开关翻转时**现挂现卸**。
|
|
4256
4255
|
*
|
|
4257
4256
|
* 与 `provider-publish.ts` 的分工:那个管「魔搭作为 chat provider 的注册」,本文件
|
|
4258
4257
|
* 管「给 agent 一个出图工具」。两者是**独立的开关**——回答的是不同问题,合并没有
|
|
4259
4258
|
* 好处(用户可能想用工具出图但不想把魔搭塞进模型选择器,反之亦然)。
|
|
4260
4259
|
*
|
|
4261
|
-
*
|
|
4262
|
-
*
|
|
4263
|
-
* -
|
|
4264
|
-
*
|
|
4260
|
+
* 承重契约:peer 的 tools.register 返回 disposer(`() => void` 注销句柄,见
|
|
4261
|
+
* `@deepseek-ai/dsh-tools` 的 `ToolsService.register`)。所以
|
|
4262
|
+
* - 开关翻转可以在**本次 Host 生命周期内**生效:`sync()` 读一次开关,要开就注册、
|
|
4263
|
+
* 要关就调 disposer 摘掉,不需要等下次启动;
|
|
4264
|
+
* - 句柄必须**存住**:丢掉它 = 插件卸载后注册残留(僵尸工具),而 registry 对重名
|
|
4265
|
+
* insert 会抛错,下一次挂载直接失败(错误文本形如 `tool "<name>" is already
|
|
4266
|
+
* registered`)。旧版把返回的 `unknown` 直接丢弃,正是这条缺口的来源;
|
|
4265
4267
|
* - 目录变化**不需要**重注册:工具在**调用时**才读目录(`getEntries` 是个函数),
|
|
4266
4268
|
* 所以一次目录刷新会自然被下一次出图看到。
|
|
4267
4269
|
*
|
|
4268
|
-
*
|
|
4269
|
-
*
|
|
4270
|
-
*
|
|
4270
|
+
* 并发:`sync()` 串行排队(`chain`),每次执行时**现读**开关,所以快速连点开关
|
|
4271
|
+
* 最终收敛到最后一次保存的值;重复 sync 幂等(已注册就跳过——真去重复注册会撞
|
|
4272
|
+
* registry 的重名保护,把它当正常分支会把「开关开着」误报成缺席)。
|
|
4273
|
+
*
|
|
4274
|
+
* 失败一律降级为「工具缺席」,绝不炸掉插件:tools 服务缺失、peer 加载失败、registry
|
|
4275
|
+
* 拒收——三种都由 `draw-tool-state.ts` 记下原因供面板显示。另有一种**非缺席**的
|
|
4276
|
+
* 降级:registry 收下了却**没返回 disposer**(旧 Host / 旧 peer)。工具能用但关不
|
|
4277
|
+
* 掉,记 `not-removable` 让面板说实话——否则用户关掉开关、面板显示「已关闭」而
|
|
4278
|
+
* 工具还在。
|
|
4279
|
+
*
|
|
4280
|
+
* 本文件不吞错误(那会让「工具为什么不在」无从诊断),只把它们翻译成可展示的原因码。
|
|
4271
4281
|
*
|
|
4272
4282
|
* peer-dependent:`@deepseek-ai/dsh-tools` 由 `deps.loadToolsModule` 惰性注入。
|
|
4273
4283
|
*
|
|
@@ -4285,9 +4295,8 @@ function resetDrawToolNote() {
|
|
|
4285
4295
|
/**
|
|
4286
4296
|
* 解析一个可选服务,允许它稍后才注册。
|
|
4287
4297
|
*
|
|
4288
|
-
* 承 sensenova
|
|
4289
|
-
*
|
|
4290
|
-
* 就没有)。
|
|
4298
|
+
* 承 sensenova 同名函数的纪律:**重试的是读,不是写**。这里只做有限次立即读取,
|
|
4299
|
+
* 不做长退避(服务要么已注册,要么这次就没有)。
|
|
4291
4300
|
* @param {object} ctx - Host 上下文。
|
|
4292
4301
|
* @param {string} service - 服务名。
|
|
4293
4302
|
* @param {() => boolean} isDisposed - 卸载闸。
|
|
@@ -4303,67 +4312,142 @@ async function resolveService(ctx, service, isDisposed) {
|
|
|
4303
4312
|
return null;
|
|
4304
4313
|
}
|
|
4305
4314
|
/**
|
|
4306
|
-
*
|
|
4315
|
+
* 建一个出图工具挂载控制器(不产生副作用,`sync()` 才读开关/注册)。
|
|
4307
4316
|
* @param {object} ctx - Host 上下文。
|
|
4308
|
-
* @param {object} wiring - `{ settings, configError, tokenStore, inference,
|
|
4317
|
+
* @param {object} wiring - `{ settings, configError, tokenStore, inference, drawStore, publisher, logger }`。
|
|
4309
4318
|
* @param {object} deps - 测试缝(`loadToolsModule`、`drawFetch`、`pollIntervalMs`)。
|
|
4310
|
-
* @returns {
|
|
4319
|
+
* @returns {DrawToolMount} 控制器。
|
|
4311
4320
|
*/
|
|
4312
|
-
|
|
4321
|
+
function createDrawToolMount(ctx, wiring, deps = {}) {
|
|
4313
4322
|
const { settings, configError, tokenStore, inference, drawStore, publisher, logger } = wiring;
|
|
4314
4323
|
const loggerFor = logger ?? ctx.logger;
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4324
|
+
/** 当前已注册工具的注销句柄;`null` = 没挂上(或已摘掉)。 */
|
|
4325
|
+
let disposeTool = null;
|
|
4326
|
+
/** peer 的 `defineTool` 只解析一次:成功后就缓存,失败留到下次 sync 重试。 */
|
|
4327
|
+
let defineTool = null;
|
|
4328
|
+
/** 卸载闸:`dispose()` 之后任何在途/后续 sync 都不再注册。 */
|
|
4329
|
+
let closed = false;
|
|
4330
|
+
/** 串行链:保证「读开关 → 挂/卸」不会被两次 sync 交错执行。 */
|
|
4331
|
+
let chain = Promise.resolve();
|
|
4332
|
+
const isDisposed = () => closed || publisher?.isDisposed?.() === true;
|
|
4333
|
+
/**
|
|
4334
|
+
* 摘掉已注册的工具(幂等)。
|
|
4335
|
+
*
|
|
4336
|
+
* 先清注册位再调 disposer:即使 disposer 抛了,面板也不该继续显示「已注册」。
|
|
4337
|
+
* @returns {void}
|
|
4338
|
+
*/
|
|
4339
|
+
function unmount() {
|
|
4340
|
+
const dispose = disposeTool;
|
|
4341
|
+
disposeTool = null;
|
|
4342
|
+
setDrawToolMounted(false);
|
|
4343
|
+
if (dispose !== null) try {
|
|
4344
|
+
dispose();
|
|
4345
|
+
} catch (error) {
|
|
4346
|
+
loggerFor?.warn?.(`${name}: draw tool dispose failed (${redactSecrets(errMsg(error))})`);
|
|
4326
4347
|
}
|
|
4327
|
-
|
|
4348
|
+
setDrawToolNote(null);
|
|
4349
|
+
}
|
|
4350
|
+
/**
|
|
4351
|
+
* 解析 peer 的 `defineTool`(成功后缓存)。
|
|
4352
|
+
* @returns {Promise<Function|null>} `defineTool` 或 `null`(失败已记原因)。
|
|
4353
|
+
*/
|
|
4354
|
+
async function loadDefineTool() {
|
|
4355
|
+
if (defineTool !== null) return defineTool;
|
|
4328
4356
|
try {
|
|
4329
4357
|
const mod = await Promise.resolve(deps.loadToolsModule ? deps.loadToolsModule() : import("@deepseek-ai/dsh-tools"));
|
|
4330
|
-
|
|
4358
|
+
const found = mod?.defineTool ?? mod?.default?.defineTool ?? null;
|
|
4359
|
+
if (typeof found === "function") {
|
|
4360
|
+
defineTool = found;
|
|
4361
|
+
return found;
|
|
4362
|
+
}
|
|
4331
4363
|
} catch (error) {
|
|
4332
|
-
setDrawToolNote("peer-load-failed");
|
|
4333
4364
|
loggerFor?.warn?.(`${name}: draw tool peer module failed to load (${redactSecrets(errMsg(error))}) — the tool is absent`);
|
|
4334
|
-
return;
|
|
4335
4365
|
}
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4366
|
+
return null;
|
|
4367
|
+
}
|
|
4368
|
+
/**
|
|
4369
|
+
* 一次收敛:读开关,要开就注册、要关就注销。
|
|
4370
|
+
* @returns {Promise<void>}
|
|
4371
|
+
*/
|
|
4372
|
+
async function apply() {
|
|
4373
|
+
if (closed) return;
|
|
4374
|
+
try {
|
|
4375
|
+
if (configError !== null) return;
|
|
4376
|
+
const panelValue = drawStore ? await drawStore.enabled().catch(() => null) : null;
|
|
4377
|
+
if (resolveSwitchEnabled(panelValue, settings.drawEnabled === true) !== true) {
|
|
4378
|
+
unmount();
|
|
4379
|
+
return;
|
|
4380
|
+
}
|
|
4381
|
+
if (disposeTool !== null) {
|
|
4382
|
+
setDrawToolNote(null);
|
|
4383
|
+
setDrawToolMounted(true);
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
4386
|
+
const tools = await resolveService(ctx, "tools", isDisposed);
|
|
4387
|
+
if (tools === null || typeof tools.register !== "function") {
|
|
4388
|
+
setDrawToolNote("no-tools-service");
|
|
4389
|
+
setDrawToolMounted(false);
|
|
4390
|
+
return;
|
|
4391
|
+
}
|
|
4392
|
+
const found = await loadDefineTool();
|
|
4393
|
+
if (found === null) {
|
|
4394
|
+
setDrawToolNote("peer-load-failed");
|
|
4395
|
+
setDrawToolMounted(false);
|
|
4396
|
+
return;
|
|
4397
|
+
}
|
|
4398
|
+
const drawFetch = deps.drawFetch ?? ((url, options) => fetch(url, options));
|
|
4399
|
+
const dispose = tools.register(defineDrawTool({
|
|
4400
|
+
defineTool: found,
|
|
4401
|
+
fetchImpl: drawFetch,
|
|
4402
|
+
resolveApiKey: async () => (await tokenStore.resolve()).value,
|
|
4403
|
+
getEntries: async () => {
|
|
4404
|
+
return (await inference.fetchModels().catch(() => null))?.entries ?? [];
|
|
4405
|
+
},
|
|
4406
|
+
getPreferredModelId: async () => {
|
|
4407
|
+
const panelValue = drawStore ? await drawStore.modelId().catch(() => null) : null;
|
|
4408
|
+
return resolveSwitchValue(panelValue, settings.drawModelId === "" ? void 0 : settings.drawModelId) ?? null;
|
|
4409
|
+
},
|
|
4410
|
+
settings: {
|
|
4411
|
+
apiBase: settings.apiBase,
|
|
4412
|
+
drawModelId: settings.drawModelId,
|
|
4413
|
+
drawTimeoutMs: settings.drawTimeoutMs,
|
|
4414
|
+
drawWaitMs: settings.drawWaitMs
|
|
4415
|
+
},
|
|
4416
|
+
...wiring.drawJobs !== void 0 ? { jobs: wiring.drawJobs } : {},
|
|
4417
|
+
...deps.pollIntervalMs !== void 0 ? { pollIntervalMs: deps.pollIntervalMs } : {},
|
|
4418
|
+
isDisposed
|
|
4419
|
+
}));
|
|
4420
|
+
if (typeof dispose === "function") {
|
|
4421
|
+
disposeTool = dispose;
|
|
4422
|
+
setDrawToolNote(null);
|
|
4423
|
+
setDrawToolMounted(true);
|
|
4424
|
+
} else {
|
|
4425
|
+
disposeTool = null;
|
|
4426
|
+
setDrawToolNote("not-removable");
|
|
4427
|
+
setDrawToolMounted(true);
|
|
4428
|
+
loggerFor?.warn?.(`${name}: draw tool registered without a disposer — turning the switch off cannot unmount it on this host`);
|
|
4429
|
+
}
|
|
4430
|
+
} catch (error) {
|
|
4431
|
+
setDrawToolNote("registry-refused");
|
|
4432
|
+
setDrawToolMounted(false);
|
|
4433
|
+
loggerFor?.warn?.(`${name}: draw tool registration refused (${redactSecrets(errMsg(error))}) — the tool is absent`);
|
|
4339
4434
|
}
|
|
4340
|
-
const drawFetch = deps.drawFetch ?? ((url, options) => fetch(url, options));
|
|
4341
|
-
tools.register(defineDrawTool({
|
|
4342
|
-
defineTool,
|
|
4343
|
-
fetchImpl: drawFetch,
|
|
4344
|
-
resolveApiKey: async () => (await tokenStore.resolve()).value,
|
|
4345
|
-
getEntries: async () => {
|
|
4346
|
-
return (await inference.fetchModels().catch(() => null))?.entries ?? [];
|
|
4347
|
-
},
|
|
4348
|
-
getPreferredModelId: async () => {
|
|
4349
|
-
const panelValue = drawStore ? await drawStore.modelId().catch(() => null) : null;
|
|
4350
|
-
return resolveSwitchValue(panelValue, settings.drawModelId === "" ? void 0 : settings.drawModelId) ?? null;
|
|
4351
|
-
},
|
|
4352
|
-
settings: {
|
|
4353
|
-
apiBase: settings.apiBase,
|
|
4354
|
-
drawModelId: settings.drawModelId,
|
|
4355
|
-
drawTimeoutMs: settings.drawTimeoutMs,
|
|
4356
|
-
drawWaitMs: settings.drawWaitMs
|
|
4357
|
-
},
|
|
4358
|
-
...wiring.drawJobs !== void 0 ? { jobs: wiring.drawJobs } : {},
|
|
4359
|
-
...deps.pollIntervalMs !== void 0 ? { pollIntervalMs: deps.pollIntervalMs } : {},
|
|
4360
|
-
isDisposed
|
|
4361
|
-
}));
|
|
4362
|
-
setDrawToolMounted(true);
|
|
4363
|
-
} catch (error) {
|
|
4364
|
-
setDrawToolNote("registry-refused");
|
|
4365
|
-
loggerFor?.warn?.(`${name}: draw tool registration refused (${redactSecrets(errMsg(error))}) — the tool is absent`);
|
|
4366
4435
|
}
|
|
4436
|
+
return {
|
|
4437
|
+
sync() {
|
|
4438
|
+
chain = chain.then(apply).catch((error) => {
|
|
4439
|
+
loggerFor?.warn?.(`${name}: draw tool sync failed (${redactSecrets(errMsg(error))})`);
|
|
4440
|
+
});
|
|
4441
|
+
return chain;
|
|
4442
|
+
},
|
|
4443
|
+
dispose() {
|
|
4444
|
+
closed = true;
|
|
4445
|
+
unmount();
|
|
4446
|
+
},
|
|
4447
|
+
mounted() {
|
|
4448
|
+
return drawToolMounted();
|
|
4449
|
+
}
|
|
4450
|
+
};
|
|
4367
4451
|
}
|
|
4368
4452
|
|
|
4369
4453
|
//#endregion
|
|
@@ -6148,12 +6232,11 @@ function registerProviderRoute(ctx, wiring) {
|
|
|
6148
6232
|
* 「给 agent 一个出图工具」。两者的开关、状态文件、候选集合都不同,合并会让
|
|
6149
6233
|
* 「关掉出图」波及 provider 的注册时序(详见 draw-store.ts 的头注)。
|
|
6150
6234
|
*
|
|
6151
|
-
*
|
|
6152
|
-
*
|
|
6153
|
-
*
|
|
6154
|
-
*
|
|
6155
|
-
*
|
|
6156
|
-
* 以为开关坏了。
|
|
6235
|
+
* 开关翻转**当场生效**:保存成功后本路由 await 挂载控制器的 `syncDraw()`(注册
|
|
6236
|
+
* 或注销),再把**收敛之后**的真实状态写进响应。所以面板拿到的不是「已保存、等
|
|
6237
|
+
* 重启」,而是「已挂上」或「挂载失败的原因」。`syncDraw` 由装配层注入(`index.ts`
|
|
6238
|
+
* 把同一个控制器交给路由与挂载),本路由不自己建——否则「注册」与「注销」会各持
|
|
6239
|
+
* 一份状态。
|
|
6157
6240
|
*
|
|
6158
6241
|
* peer-free:不 import 任何 Host peer。
|
|
6159
6242
|
* @module dsh-connect-modelscope-token-plan/routes/draw
|
|
@@ -6192,7 +6275,7 @@ async function readEntries(inference) {
|
|
|
6192
6275
|
* @returns {Function} 注销回调。
|
|
6193
6276
|
*/
|
|
6194
6277
|
function registerDrawRoute(ctx, wiring) {
|
|
6195
|
-
const { settings, drawStore, inference, logger } = wiring;
|
|
6278
|
+
const { settings, drawStore, inference, syncDraw, logger } = wiring;
|
|
6196
6279
|
/**
|
|
6197
6280
|
* 组装响应。GET 与写成功后的响应共用,两个方向不可能对「当前状态」说两套话。
|
|
6198
6281
|
* @param {object} [extra] - 追加/覆盖字段。
|
|
@@ -6218,15 +6301,15 @@ function registerDrawRoute(ctx, wiring) {
|
|
|
6218
6301
|
effectiveSource: choice.source,
|
|
6219
6302
|
absentReason: drawToolNote(),
|
|
6220
6303
|
/**
|
|
6221
|
-
*
|
|
6304
|
+
* 开关为开、但工具此刻还没挂上(正在收敛,或收敛失败)。
|
|
6222
6305
|
*
|
|
6223
|
-
*
|
|
6224
|
-
*
|
|
6225
|
-
*
|
|
6306
|
+
* 现在开关翻转是**当场**收敛的(见上面的 `syncDraw`),所以正常保存后这一位
|
|
6307
|
+
* 就是 false。它仍然有用:GET 与进程刚启动、还没跑到第一次 sync 时仍可能为
|
|
6308
|
+
* true,面板靠它区分「暂时没挂」与「挂上了」。
|
|
6226
6309
|
*
|
|
6227
|
-
* 注意判据不是「有缺席原因」:缺席原因非 null
|
|
6228
|
-
*
|
|
6229
|
-
*
|
|
6310
|
+
* 注意判据不是「有缺席原因」:缺席原因非 null 表示收敛已尝试且失败(面板会
|
|
6311
|
+
* 优先显示那个原因),而「还没跑完」时 absentReason 为 null。见
|
|
6312
|
+
* `draw-tool-state.ts` 文件头。
|
|
6230
6313
|
*/
|
|
6231
6314
|
mountPending: resolveSwitchEnabled(panelSwitch, settings.drawEnabled) === true && drawToolMounted() !== true,
|
|
6232
6315
|
preferredInCatalog: preferredModelId === null ? null : catalog.ok ? candidates.some((row) => row.id === preferredModelId) : null,
|
|
@@ -6276,6 +6359,7 @@ function registerDrawRoute(ctx, wiring) {
|
|
|
6276
6359
|
try {
|
|
6277
6360
|
if (hasEnabled) await drawStore.saveEnabled(body.enabled);
|
|
6278
6361
|
if (body.modelId !== void 0) await drawStore.saveModelId(body.modelId);
|
|
6362
|
+
if (hasEnabled) await syncDraw?.();
|
|
6279
6363
|
} catch (error) {
|
|
6280
6364
|
respondError(response, error);
|
|
6281
6365
|
return;
|
|
@@ -6297,6 +6381,7 @@ function registerDrawRoute(ctx, wiring) {
|
|
|
6297
6381
|
}));
|
|
6298
6382
|
try {
|
|
6299
6383
|
await drawStore.reset();
|
|
6384
|
+
await syncDraw?.();
|
|
6300
6385
|
} catch (error) {
|
|
6301
6386
|
respondError(response, error);
|
|
6302
6387
|
return;
|
|
@@ -6479,7 +6564,7 @@ function registerRoutes(ctx, wiring) {
|
|
|
6479
6564
|
*
|
|
6480
6565
|
* **出图工具**
|
|
6481
6566
|
* - `draw.ts` 协议纯逻辑半边(peer-free)
|
|
6482
|
-
* - `draw-register.ts`
|
|
6567
|
+
* - `draw-register.ts` 工具挂载控制器(开关翻转现挂现卸)
|
|
6483
6568
|
* - `draw-runtime.ts` 运行时(两段式:提交 → 轮询)
|
|
6484
6569
|
* - `draw-jobs.ts` 作业表(本次挂载内有效)
|
|
6485
6570
|
* - `draw-tool-state.ts` 缺席原因 + 本次挂载注册位(内存)
|
|
@@ -6582,12 +6667,16 @@ function apply(ctx, config = {}, deps = {}) {
|
|
|
6582
6667
|
reconcile,
|
|
6583
6668
|
logger: ctx.logger
|
|
6584
6669
|
};
|
|
6585
|
-
const
|
|
6670
|
+
const drawMount = createDrawToolMount(ctx, wiring, deps);
|
|
6671
|
+
const offs = registerRoutes(ctx, {
|
|
6672
|
+
...wiring,
|
|
6673
|
+
syncDraw: () => drawMount.sync()
|
|
6674
|
+
});
|
|
6586
6675
|
ctx.effect(() => {
|
|
6587
6676
|
reconcile().catch((error) => {
|
|
6588
6677
|
ctx.logger?.warn?.(`${name}: mount reconcile failed (${redactSecrets(errMsg(error))})`);
|
|
6589
6678
|
});
|
|
6590
|
-
|
|
6679
|
+
drawMount.sync();
|
|
6591
6680
|
const timer = setInterval(() => {
|
|
6592
6681
|
reconcile().catch((error) => {
|
|
6593
6682
|
ctx.logger?.warn?.(`${name}: scheduled reconcile failed (${redactSecrets(errMsg(error))})`);
|
|
@@ -6595,6 +6684,7 @@ function apply(ctx, config = {}, deps = {}) {
|
|
|
6595
6684
|
}, settings.pollSeconds * 1e3);
|
|
6596
6685
|
return () => {
|
|
6597
6686
|
clearInterval(timer);
|
|
6687
|
+
drawMount.dispose();
|
|
6598
6688
|
resetDrawToolNote();
|
|
6599
6689
|
drawJobs.dispose();
|
|
6600
6690
|
publisher.dispose();
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-modelscope-token-plan",
|
|
3
3
|
"displayName": "DSH Connect ModelScope Token Plan",
|
|
4
4
|
"icon": "./icon.svg",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.7.0",
|
|
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"
|