@dsh-plugins/dsh-llm-hub 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  本项目遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.7.3] - 2026-09-17
6
+
7
+ ### 新增
8
+
9
+ - **模型页显示外部 harness 一览**:设置 → 模型 页脚多一行 chips,直接看到
10
+ codex / claude / agy 三者各自的状态 —— 可委派(绿点)、已由其它插件提供、未安装。
11
+ 鼠标悬停给出该做什么,并显示**解析到的可执行文件路径**(「装了却不生效」时,
12
+ 第一件要确认的就是它找到的是哪个副本)。
13
+
14
+ 几个刻意的取舍:
15
+ - **未安装的也显示**(置灰)。这一行的用处一半是「现在能派给谁」,另一半是
16
+ 「装上哪个就能多派一个」;全过滤掉就只剩前一半,没人会知道还能有什么。
17
+ - **`probed` 为 false 时整行不渲染**。host 半是异步探测的,页面可能在探完前就
18
+ 打开了 —— 这时渲染「未安装 ×3」是在说谎,会让人去装一个其实已经装了的 CLI。
19
+ - **前端只读 host 探好的快照**(`/api/dsh-llm-hub/harness`),不自己探。探测要
20
+ 解析可执行文件,那是 host 的活,两处各探一遍只会不一致。
21
+ - 排在页脚那行之上(order 90 vs 100):harness 讲的是「能派给谁」属能力信息,
22
+ 页脚那行是插件版本与链接属元信息。
23
+
24
+ 挂载点测试同步更新 —— 它本来就钉住了「一共挂几个 slot」,这次如实抓到了新增的
25
+ 那个,顺便补了「footer 是 list slot,两个条目都必须带 id 与 order」的断言
26
+ (漏 id 会被宿主静默丢弃:接口通、组件在、页面上什么都没有)。
27
+
5
28
  ## [0.7.0] - 2026-09-17
6
29
 
7
30
  ### 新增
package/lib/client.js CHANGED
@@ -60,6 +60,8 @@ window.__ModuleLoader__.load({
60
60
  const META_URL = '/api/dsh-llm-hub/meta'
61
61
 
62
62
  /** 模型可用性:读缓存 / 强制全量重探(host 半同名路由)。 */
63
+ /** harness 探测快照;host 半在 apply 时探一次,这里只读。 */
64
+ const HARNESS_URL = '/api/dsh-llm-hub/harness'
63
65
  const AVAILABILITY_URL = '/api/dsh-llm-hub/availability'
64
66
  const AVAILABILITY_RECHECK_URL = '/api/dsh-llm-hub/availability/recheck'
65
67
 
@@ -75,6 +77,13 @@ window.__ModuleLoader__.load({
75
77
  toppedUp: '充值',
76
78
  unavailable: '账户不可用',
77
79
  failed: '查询失败',
80
+ harnessTitle: '外部 Agent',
81
+ harnessReady: '可委派',
82
+ harnessOccupied: '已由其它插件提供',
83
+ harnessMissing: '未安装',
84
+ harnessHintReady: '会话里可用 subagent_%s 把独立任务交给它',
85
+ harnessHintOccupied: '本机装了,但这个提供方已被另一个插件注册',
86
+ harnessHintMissing: '装上 %s 并重启 DSH,它就会出现',
78
87
  probe: '探测网关',
79
88
  probing: '探测中…',
80
89
  reachable: '可达',
@@ -124,6 +133,13 @@ window.__ModuleLoader__.load({
124
133
  toppedUp: 'Topped up',
125
134
  unavailable: 'Account unavailable',
126
135
  failed: 'Balance check failed',
136
+ harnessTitle: 'External agents',
137
+ harnessReady: 'ready',
138
+ harnessOccupied: 'provided by another plugin',
139
+ harnessMissing: 'not installed',
140
+ harnessHintReady: 'Delegate a standalone task with subagent_%s',
141
+ harnessHintOccupied: 'Installed here, but another plugin already registered this provider',
142
+ harnessHintMissing: 'Install %s and restart DSH to make it appear',
127
143
  probe: 'Probe gateway',
128
144
  probing: 'Probing…',
129
145
  reachable: 'Reachable',
@@ -178,6 +194,17 @@ window.__ModuleLoader__.load({
178
194
  'margin-top:8px;padding:8px 10px;border-radius:8px;',
179
195
  'border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);',
180
196
  'font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}',
197
+ '.dsh-llm-hub-harness{display:flex;align-items:center;gap:8px;flex-wrap:wrap;',
198
+ 'font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}',
199
+ '.dsh-llm-hub-harness__chip{display:inline-flex;align-items:center;gap:5px;',
200
+ 'padding:2px 8px;border-radius:999px;border:.5px solid var(--dsw-alias-border-l1);',
201
+ 'background:var(--dsw-alias-bg-layer-2)}',
202
+ '.dsh-llm-hub-harness__chip--missing{opacity:.55}',
203
+ '.dsh-llm-hub-harness__dot{width:6px;height:6px;border-radius:50%;',
204
+ 'background:var(--dsw-alias-label-secondary);flex:none}',
205
+ '.dsh-llm-hub-harness__dot--ready{background:var(--dsw-alias-state-success-primary,#3fb950)}',
206
+ '.dsh-llm-hub-harness__name{color:var(--dsw-alias-label-primary)}',
207
+ '.dsh-llm-hub-harness__state{opacity:.75}',
181
208
  '.dsh-llm-hub-balance__label{color:var(--dsw-alias-label-secondary)}',
182
209
  '.dsh-llm-hub-balance__amount{color:var(--dsw-alias-label-primary);font-weight:600;',
183
210
  'font-variant-numeric:tabular-nums}',
@@ -625,6 +652,56 @@ window.__ModuleLoader__.load({
625
652
  * @param props - 注入的 `t`。
626
653
  * @returns 页脚节点;元信息读不到时返回 null(不占位、不报错)。
627
654
  */
655
+ /**
656
+ * 外部 harness 一览。
657
+ *
658
+ * 只读 host 半 apply 时探好的快照(`/api/dsh-llm-hub/harness`),不自己探 ——
659
+ * 探测要 spawn 解析可执行文件,那是 host 的活,前端重复做一遍只会两处不一致。
660
+ *
661
+ * 未安装的**也显示**(置灰):这一行的用处一半是「现在能派给谁」,另一半是
662
+ * 「装上哪个就能多派一个」。全过滤掉就只剩前一半,用户永远不知道还能有什么。
663
+ * @param props - `t` 文案函数。
664
+ * @returns 一行 chips;探测未完成或接口不可用时返回 null。
665
+ */
666
+ function HarnessRow(props) {
667
+ const t = typeof props.t === 'function' ? props.t : fallbackT
668
+ const [report, setReport] = React.useState(null)
669
+ React.useEffect(() => {
670
+ let alive = true
671
+ fetch(HARNESS_URL, { headers: { accept: 'application/json' } })
672
+ .then((res) => res.json())
673
+ .then((body) => { if (alive) setReport(body) })
674
+ .catch(() => { if (alive) setReport({ ok: false }) })
675
+ return () => { alive = false }
676
+ }, [])
677
+ // probed 为 false 是启动竞态(host 还没探完),不是「一个都没装」——
678
+ // 这时候渲染「未安装 ×3」会说谎,所以什么都不显示。
679
+ if (report === null || report.ok !== true || report.probed !== true) return null
680
+ const list = Array.isArray(report.harnesses) ? report.harnesses : []
681
+ if (list.length === 0) return null
682
+ ensureStyle()
683
+ return h('div', { className: 'dsh-llm-hub-harness' },
684
+ h('span', { className: 'dsh-llm-hub-harness__state' }, t('harnessTitle')),
685
+ list.map((item) => {
686
+ const state = item.registered === true
687
+ ? { text: t('harnessReady'), hint: t('harnessHintReady').replace('%s', String(item.id).replace(/-/g, '_')), ready: true }
688
+ : (item.installed === true
689
+ ? { text: t('harnessOccupied'), hint: t('harnessHintOccupied'), ready: false }
690
+ : { text: t('harnessMissing'), hint: t('harnessHintMissing').replace('%s', item.bin), ready: false })
691
+ return h('span', {
692
+ key: item.id,
693
+ className: 'dsh-llm-hub-harness__chip' + (item.installed === true ? '' : ' dsh-llm-hub-harness__chip--missing'),
694
+ // 路径放进 tooltip:装了却不生效时,第一件要确认的就是「它找到的是哪个副本」。
695
+ title: state.hint + (typeof item.executable === 'string' ? '\n' + item.executable : '')
696
+ },
697
+ h('span', { className: 'dsh-llm-hub-harness__dot' + (state.ready ? ' dsh-llm-hub-harness__dot--ready' : '') }),
698
+ h('span', { className: 'dsh-llm-hub-harness__name' }, item.displayName),
699
+ h('span', { className: 'dsh-llm-hub-harness__state' }, state.text)
700
+ )
701
+ })
702
+ )
703
+ }
704
+
628
705
  function HubFooter(props) {
629
706
  const t = typeof props.t === 'function' ? props.t : fallbackT
630
707
  const availability = props.availability
@@ -846,6 +923,15 @@ window.__ModuleLoader__.load({
846
923
  // 的 children 声明),list 类 slot 注册**必须带 id** —— 参照宿主自己的
847
924
  // settings.onboarding 注册(id:'welcome-notice', order:-100)。
848
925
  // 第一版漏了 id,注册被静默丢弃:接口通、组件在、页面上什么都没有。
926
+ // harness 一览排在 footer 那行之上(order 更小):它讲的是「能派给谁」,
927
+ // 属于能力信息;footer 那行是插件自身的版本与链接,属于元信息。
928
+ ctx.slots.inject('settings.models.footer', () => ctx.slots.register({
929
+ name: 'settings.models.footer',
930
+ id: 'dsh-llm-hub-harness',
931
+ order: 90,
932
+ locale: NS,
933
+ inject: () => ({ t })
934
+ }, HarnessRow))
849
935
  ctx.slots.inject('settings.models.footer', () => ctx.slots.register({
850
936
  name: 'settings.models.footer',
851
937
  id: 'dsh-llm-hub-footer',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plugins/dsh-llm-hub",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "给 DSH 模型页补上官方适配器缺的那半:网关可达性探测、模型目录拉取、DeepSeek 余额常驻,以及 MiniMax / 智谱 / Moonshot / StepFun 的余额与配额卡;再把本机装好的 codex / claude / agy 注册成子代理(装了才出现)。零运行时依赖,不改动 DSH 安装。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -26,7 +26,7 @@
26
26
  }
27
27
  },
28
28
  "scripts": {
29
- "deploy": "rsync -av --delete lib/ ~/.dsh/profiles/web/node_modules/@webkubor/dsh-llm-hub/lib/ && rsync -av package.json cordis.patch.yml README.md README.en.md LICENSE ~/.dsh/profiles/web/node_modules/@webkubor/dsh-llm-hub/",
29
+ "deploy": "rsync -av --delete lib/ ~/.dsh/profiles/web/node_modules/@dsh-plugins/dsh-llm-hub/lib/ && rsync -av package.json cordis.patch.yml README.md README.en.md LICENSE ~/.dsh/profiles/web/node_modules/@dsh-plugins/dsh-llm-hub/",
30
30
  "preview": "npm run deploy && open http://127.0.0.1:3080",
31
31
  "check": "for f in lib/*.js; do node --check \"$f\" || exit 1; done",
32
32
  "test": "node --test"