dsh-dock 0.1.0 → 0.4.1

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.
@@ -0,0 +1,301 @@
1
+ // dsh-dock · 功能模块【图片理解代理】· 宿主半部(v0.3.1 迁自 index.js,行为不变)
2
+ //
3
+ // 纯文本模型收到图片时,自动调用配置的视觉模型识别图片,
4
+ // 把识别文本替换进请求(多模态模型不受影响,原样自识别)。
5
+ // 拦截点:官方 llm/stream 瀑布流不允许改写 frozen 请求、registerAdapter 拒绝重复注册,
6
+ // 故对 llm.stream / llm.prepareCall 做方法级包装(保存原函数,dispose 恢复)。
7
+ // 配置存自有 settings 命名空间 dsh-dock(visionProxy: enabled/provider/model),
8
+ // 其编辑 UI 在模型设置页内(见 features/modelconfig/view.js)。
9
+ import { BlockAssembler } from '@deepseek-ai/dsh-llm'
10
+ import { DOCK_NS } from '../../src/host-core.js'
11
+ import { readModelDirectory } from '../modelconfig/host.js'
12
+
13
+ /** 递归判断内容块里是否有图片(含 tool-result 嵌套,与官方 contentHasImage 同构)。 */
14
+ function contentHasImageDeep(content) {
15
+ if (!Array.isArray(content)) return false
16
+ for (const block of content) {
17
+ if (block && block.type === 'image') return true
18
+ if (block && block.type === 'tool-result' && contentHasImageDeep(block.content)) return true
19
+ }
20
+ return false
21
+ }
22
+
23
+ /** 让视觉模型描述一张图片,返回识别文本(失败抛错)。 */
24
+ async function describeImage(deps, cfg, block, signal) {
25
+ const prompt = '请用中文详细描述这张图片的内容:主体、文字(若有则逐字转写)、数据/图表要点、与编码任务相关的细节。控制在 300 字以内,直接输出描述,不要客套。'
26
+ // 单次识别调用:返回文本(可为空串,由调用方决定重试),失败抛错。
27
+ // ⚠️ 不带 sessionId:llm/stream 的会话检查点监听对带 sessionId 的调用先做检查点,
28
+ // 回合进行中会 fail-closed 短路适配器。
29
+ // ⚠️ 走 prepareCall 通道而非 llm.stream:llm/stream 公共路径对带图请求实测会被
30
+ // 截断(单 block-start 后静默结束,两种适配器均然,端点直连正常);agent-loop 的
31
+ // 带图请求全部经 prepareCall().stream() 且工作正常,此处同通道。
32
+ // prepareCall(config).stream(options) 要求 options 与 config 在 provider/model/
33
+ // reasoningEffort/maxTokens 等请求控制字段上完全一致,故两处同源构造。
34
+ const run = async (effort) => {
35
+ const config = { provider: cfg.provider, model: cfg.model, maxTokens: 4096 }
36
+ if (effort) config.reasoningEffort = effort
37
+ const prepared = await deps.prepareCall(config, signal)
38
+ // options 必须与 resolvedConfig 完全一致(callConfigEquals),以 prepared.config 为底
39
+ const options = Object.assign({}, prepared.config, {
40
+ messages: [{
41
+ role: 'user',
42
+ source: { kind: 'plugin', plugin: 'dsh-dock' },
43
+ content: [block, { type: 'text', text: prompt }],
44
+ }],
45
+ // 递归保护:本请求由代理发起,包装层直接放行
46
+ dockVisionProxy: true,
47
+ })
48
+ if (signal) options.signal = signal
49
+ const assembler = new BlockAssembler()
50
+ for await (const chunk of prepared.stream(options)) {
51
+ assembler.push(chunk)
52
+ // ⚠️ BlockAssembler.finish 是 getter,未收到 finish chunk 时也返回 {kind:'stop'}
53
+ // (恒真值)——只能用 chunk.type 判断,用 assembler.finish 当条件会在第一个
54
+ // chunk 后就 break,识别永远为空(2026-08-22 排查一下午的根因)。
55
+ if (chunk && chunk.type === 'finish') break
56
+ }
57
+ if (assembler.finish && assembler.finish.kind === 'error') {
58
+ throw new Error(String(assembler.finish.failure?.message || '视觉模型调用失败'))
59
+ }
60
+ return assembler.blocks()
61
+ .filter((b) => b && b.type === 'text')
62
+ .map((b) => b.text)
63
+ .join('')
64
+ .trim()
65
+ }
66
+ // 识别是辅助调用,求快:先试关思考(off);报不支持或返回空内容都回退默认档重试
67
+ let text
68
+ try {
69
+ text = await run('off')
70
+ } catch (e) {
71
+ if (!(e instanceof Error && /does not support reasoning effort|UNSUPPORTED_REASONING_EFFORT/i.test(e.message))) {
72
+ throw e
73
+ }
74
+ }
75
+ if (!text) text = await run(undefined)
76
+ if (!text) throw new Error('视觉模型没有返回文本')
77
+ return text
78
+ }
79
+
80
+ /**
81
+ * 识别结果缓存(attachmentId + 视觉模型 → 文本)。
82
+ * 端点对连续识图有节流(实测第 ~6 次起掐流成单个 block-start);agent 每轮
83
+ * 重试/多步都会带着历史图片重发请求,逐图重识别既慢又会撞限流——同一张图
84
+ * 10 分钟内只识别一次,失败不缓存。
85
+ */
86
+ const describeCache = new Map()
87
+ const DESCRIBE_CACHE_TTL = 10 * 60 * 1000
88
+ const DESCRIBE_CACHE_MAX = 64
89
+
90
+ function cacheKey(cfg, block) {
91
+ return `${cfg.provider}/${cfg.model}:${block && block.attachment ? block.attachment.attachmentId : ''}`
92
+ }
93
+
94
+ function cacheGet(key) {
95
+ const hit = describeCache.get(key)
96
+ if (!hit) return undefined
97
+ if (Date.now() - hit.at > DESCRIBE_CACHE_TTL) {
98
+ describeCache.delete(key)
99
+ return undefined
100
+ }
101
+ return hit.text
102
+ }
103
+
104
+ function cacheSet(key, text) {
105
+ if (describeCache.size >= DESCRIBE_CACHE_MAX) {
106
+ const oldest = describeCache.keys().next().value
107
+ describeCache.delete(oldest)
108
+ }
109
+ describeCache.set(key, { text, at: Date.now() })
110
+ }
111
+
112
+ /**
113
+ * 请求改写:目标是纯文本模型且带图片时,逐图调用视觉模型,图片块替换为识别文本。
114
+ * 任何失败都只降级(保留原图或占位说明),绝不阻断主请求。
115
+ * @returns 改写后的 options(无需改写时原样返回)
116
+ */
117
+ async function transformForVisionProxy(deps, options) {
118
+ const cfg = deps.config()
119
+ const log = (msg) => { try { console.log('[dsh-dock] visionproxy ' + msg) } catch { /* 日志失败不影响主流程 */ } }
120
+ try {
121
+ if (!cfg || !cfg.enabled || !cfg.provider || !cfg.model) return options
122
+ if (!options || typeof options !== 'object') return options
123
+ if (options.dockVisionProxy) return options
124
+ if (options.provider === cfg.provider && options.model === cfg.model) return options
125
+ if (options.purpose) return options // 内部辅助调用(如会话标题)不代理
126
+ if (!Array.isArray(options.messages)) return options
127
+ if (!options.messages.some((m) => m && contentHasImageDeep(m.content))) return options
128
+ log(`带图请求 ${options.provider}/${options.model}`)
129
+
130
+ // 目标模型是否支持图片:优先问运行时(llm.resolveModelInfo,与官方投影同源:
131
+ // entry.input → 内置目录 → 路由默认),避免插件目录误判"靠内置目录继承多模态"的
132
+ // 模型(如 kimi-k2.7,条目未写 input 但目录里是 [text,image]);
133
+ // 旧宿主无此方法或单次解析失败时,退回插件目录视图兜底。
134
+ let imageCapable
135
+ try {
136
+ const info = deps.resolveModelInfo ? await deps.resolveModelInfo(options.provider, options.model) : undefined
137
+ if (info && Array.isArray(info.inputModalities)) imageCapable = info.inputModalities.includes('image')
138
+ } catch {
139
+ // 落到目录回退
140
+ }
141
+ if (imageCapable === undefined) {
142
+ const dir = deps.directory()
143
+ const p = (dir.providers || []).find((x) => x.id === options.provider)
144
+ const m = p && Array.isArray(p.models) ? p.models.find((x) => x.id === options.model) : undefined
145
+ imageCapable = !!(m && Array.isArray(m.input) && m.input.includes('image'))
146
+ }
147
+ if (imageCapable) {
148
+ log(`跳过 ${options.provider}/${options.model}:运行时判定多模态(原图直发)`)
149
+ return options
150
+ }
151
+
152
+ let changed = false
153
+ const replaceBlocks = async (blocks) => {
154
+ const next = []
155
+ for (const block of blocks) {
156
+ if (block && block.type === 'image') {
157
+ changed = true
158
+ const key = cacheKey(cfg, block)
159
+ const cached = cacheGet(key)
160
+ if (cached !== undefined) {
161
+ log('命中识别缓存')
162
+ next.push({ type: 'text', text: `[图片内容(由视觉模型 ${cfg.provider}/${cfg.model} 识别):${cached}]` })
163
+ continue
164
+ }
165
+ try {
166
+ const desc = await describeImage(deps, cfg, block, options.signal)
167
+ cacheSet(key, desc)
168
+ log(`识别成功 ${desc.length} 字`)
169
+ next.push({ type: 'text', text: `[图片内容(由视觉模型 ${cfg.provider}/${cfg.model} 识别):${desc}]` })
170
+ } catch (e) {
171
+ // 识别失败:降级为说明文本,官方投影不会再把请求打挂
172
+ log(`识别失败:${e instanceof Error ? e.message : String(e)}`)
173
+ next.push({ type: 'text', text: `[图片未能识别(${e instanceof Error ? e.message : String(e)}),已省略]` })
174
+ }
175
+ continue
176
+ }
177
+ if (block && block.type === 'tool-result' && contentHasImageDeep(block.content)) {
178
+ next.push(Object.assign({}, block, { content: await replaceBlocks(block.content) }))
179
+ continue
180
+ }
181
+ next.push(block)
182
+ }
183
+ return next
184
+ }
185
+ const messages = []
186
+ for (const msg of options.messages) {
187
+ if (msg && contentHasImageDeep(msg.content)) {
188
+ messages.push(Object.assign({}, msg, { content: await replaceBlocks(msg.content) }))
189
+ } else {
190
+ messages.push(msg)
191
+ }
192
+ }
193
+ return changed ? Object.assign({}, options, { messages }) : options
194
+ } catch {
195
+ return options // 总兜底:代理自身异常不影响主请求
196
+ }
197
+ }
198
+
199
+ /** 安装 llm 方法包装:stream 与 prepareCall 两条入口都过一遍图片代理改写。 */
200
+ function installVisionProxy(ctx, directory) {
201
+ const llm = ctx.get('llm')
202
+ if (!llm || typeof llm.stream !== 'function' || typeof llm.prepareCall !== 'function') {
203
+ throw new Error('llm 服务不可用,无法安装图片理解代理')
204
+ }
205
+ // 原函数先绑定保存:改写判定与识别调用都走原函数,不受包装影响
206
+ const origResolve = typeof llm.resolveModelInfo === 'function' ? llm.resolveModelInfo.bind(llm) : null
207
+ if (origResolve) llm.__dockOrigResolveModelInfo = origResolve
208
+ const origPrepareCall = llm.prepareCall.bind(llm)
209
+ const deps = {
210
+ config: () => {
211
+ try {
212
+ const settings = ctx.get('settings')
213
+ const v = settings && typeof settings.get === 'function' ? settings.get(DOCK_NS) : undefined
214
+ return v && v.visionProxy ? v.visionProxy : { enabled: false, provider: '', model: '' }
215
+ } catch {
216
+ return { enabled: false, provider: '', model: '' }
217
+ }
218
+ },
219
+ // 识别调用走保存的原函数:天然绕开包装层,无递归
220
+ stream: (options) => llm.__dockOrigStream(options),
221
+ // 识别调用走原 prepareCall 通道(带图请求的被验证路径)
222
+ prepareCall: origPrepareCall,
223
+ // 运行时模型信息查询(图片能力判定与官方投影同源);旧宿主无此方法时为 null
224
+ resolveModelInfo: origResolve,
225
+ directory,
226
+ }
227
+ llm.__dockOrigStream = llm.stream.bind(llm)
228
+ const origPrepare = llm.prepareCall
229
+ llm.stream = (options) => (async function* () {
230
+ yield* llm.__dockOrigStream(await transformForVisionProxy(deps, options))
231
+ })()
232
+ llm.prepareCall = async (config, signal) => {
233
+ const prepared = await origPrepare.call(llm, config, signal)
234
+ // prepared 是冻结对象:浅拷贝后包一层 stream,dispatched 一次性约束经原闭包保持。
235
+ // ⚠️ stream 必须同步返回 AsyncIterable:消费方 `for await (of stream)` 不接受
236
+ // Promise(Node 语义:of 表达式直接取 Symbol.asyncIterator),async 包装会令
237
+ // 全部请求报 "stream is not async iterable"(2026-08-22 事故)。
238
+ return Object.assign({}, prepared, {
239
+ stream: (options) => (async function* () {
240
+ yield* prepared.stream(await transformForVisionProxy(deps, options))
241
+ })(),
242
+ })
243
+ }
244
+ // 虚拟多模态:代理启用时,纯文本模型对外宣称支持图片,让官方带图准入放行——
245
+ // apiproxy 的 prompt 收图校验、mcp-client/tool-fs 的图片声明检查都查
246
+ // llm.resolveModelInfo 的 inputModalities;图片真正到端点前会被 stream/prepareCall
247
+ // 包装层替换为识别文本。视觉模型自身与多模态模型不受影响;官方内部投影不走
248
+ // 此公共方法,truth 不变。
249
+ if (origResolve) {
250
+ llm.resolveModelInfo = async (provider, model, signal) => {
251
+ const info = await origResolve(provider, model, signal)
252
+ try {
253
+ const cfg = deps.config()
254
+ if (!cfg || !cfg.enabled || !cfg.provider || !cfg.model) return info
255
+ if (provider === cfg.provider && model === cfg.model) return info
256
+ if (info && Array.isArray(info.inputModalities) && info.inputModalities.length > 0
257
+ && !info.inputModalities.includes('image')) {
258
+ return Object.assign({}, info, { inputModalities: info.inputModalities.concat('image') })
259
+ }
260
+ } catch {
261
+ // 保真回退
262
+ }
263
+ return info
264
+ }
265
+ }
266
+ return () => {
267
+ delete llm.__dockOrigStream
268
+ delete llm.stream
269
+ delete llm.prepareCall
270
+ delete llm.__dockOrigResolveModelInfo
271
+ delete llm.resolveModelInfo
272
+ }
273
+ }
274
+
275
+ /** 测试钩子:清空识别缓存(冒烟在用例间隔离)。 */
276
+ export function __clearDescribeCache() {
277
+ describeCache.clear()
278
+ }
279
+
280
+ /** 功能注册表条目:index.js 组装 hostSetups 时使用。 */
281
+ export const feature = {
282
+ id: 'visionproxy',
283
+ name: '图片理解代理',
284
+ description: '纯文本模型收图时自动调用视觉模型识别(多模态模型不受影响)',
285
+ defaultEnabled: true,
286
+ setup(ctx) {
287
+ // 模型目录的 TTL 缓存:图片代理判定目标模型是否纯文本时用(避免每个请求都全量 describe)
288
+ let dockDirCache = { at: 0, data: { providers: [] } }
289
+ const directoryCached = () => {
290
+ if (Date.now() - dockDirCache.at > 10000) {
291
+ try {
292
+ dockDirCache = { at: Date.now(), data: readModelDirectory(ctx) }
293
+ } catch {
294
+ // 保持旧缓存
295
+ }
296
+ }
297
+ return dockDirCache.data
298
+ }
299
+ return installVisionProxy(ctx, directoryCached)
300
+ },
301
+ }
package/index.js CHANGED
@@ -1,37 +1,45 @@
1
- // dsh-dock · Host 半部(Node 侧入口)
1
+ // dsh-dock · Host 半部(Node 侧入口)· v0.4.0 模块化架构
2
2
  //
3
- // v0.1.0 基础框架:
4
- // - 功能注册表(FEATURES):与 Client 半部注册表同构,规划中的功能已登记;
5
- // - 每功能独立生命周期(hostSetups 返回 disposer,开关只影响自己);
6
- // - 函数级错误隔离:单个功能 setup 抛错只标记 error,不影响其他功能。
3
+ // 功能坞 = hub + 一个个独立功能模块(像 dsh 本身由小包组成):
4
+ // - 每个功能是一个 features/<id>/ 目录:host.js(宿主半部)+ view.js(x)(客户端视图),
5
+ // 可整体拎出单独打包发布(scripts/extract-feature.mjs 生成独立包骨架);
6
+ // - 独立发布的功能包装回本插件后,经 client 半部的 dockBridge 注册进面板菜单;
7
+ // - 本文件只做组装:import 各模块的 feature 描述符,统一生命周期(开关/错误隔离/卸载清理),
8
+ // 不含任何具体功能逻辑。
7
9
  //
8
- // 路线图:
9
- // - 0.2.0 接入模型余额:在 hostSetups.balance 里实现余额拉取,
10
- // Client↔Host 联通(typert)随本版本一起打通;
11
- // - 0.3.0 接入 Token 用量记录:hostSetups.tokenlog 监听事件记账;
12
- // - 0.4.0 接入任务动画。
10
+ // 功能清单(宿主半部;客户端菜单次序由 view 模块的 order 字段决定):
11
+ // - models 模型设置(v0.3.0):模型目录读 + 官方配置写
12
+ // - visionproxy 图片理解代理(v0.3.1):纯文本模型收图自动走视觉模型识别
13
+ // - balance 模型余额(v0.2.0):各 Provider 账户余额/配额
14
+ // - tokenlog 用量记录(v0.4.0):LLM 调用记账与统计(移植自 @wycto/dsh-token-usage)
15
+ // - animation 任务动画(路线图 0.5.0 占位)
16
+ import { DOCK_NS, DockConfig } from './src/host-core.js'
17
+ import { feature as fModels } from './features/modelconfig/host.js'
18
+ import { feature as fVisionProxy } from './features/visionproxy/host.js'
19
+ import { feature as fBalance } from './features/balance/host.js'
20
+ import { feature as fTokenlog } from './features/tokenlog/host.js'
21
+
13
22
  export const name = 'dsh-dock'
14
23
 
24
+ // 硬依赖 webServer(路由注册)+ llm(图片理解代理包装)+ settings(自有命名空间读写)。
25
+ export const inject = ['webServer', 'llm', 'settings']
26
+
27
+ /** 测试钩子:清空识别缓存(冒烟在用例间隔离;实现在 visionproxy 模块)。 */
28
+ export { __clearDescribeCache } from './features/visionproxy/host.js'
29
+
15
30
  export function apply(ctx) {
16
- // ---- 功能注册表(Host 侧)----
17
- // defaultEnabled 缺省为 false:v0.1.0 只有示例功能,真实功能按路线图接入后打开。
31
+ // ---- 功能注册表(Host 侧):每个条目来自对应功能模块的 feature 描述符 ----
32
+ // defaultEnabled:与 Client 半部保持一致。已接入的功能默认打开,
33
+ // 规划中的功能缺省 false,等实现后移除 roadmap 并打开。
18
34
  const FEATURES = [
19
- {
20
- id: 'balance',
21
- name: '模型余额',
22
- roadmap: '0.2.0',
23
- description: '拉取所有模型 Provider 账户余额',
24
- },
25
- {
26
- id: 'tokenlog',
27
- name: 'Token 用量记录',
28
- roadmap: '0.3.0',
29
- description: '记录全部 LLM API 调用并统计',
30
- },
35
+ fModels,
36
+ fVisionProxy,
37
+ fBalance,
38
+ fTokenlog,
31
39
  {
32
40
  id: 'animation',
33
41
  name: '任务动画',
34
- roadmap: '0.4.0',
42
+ roadmap: '0.5.0',
35
43
  description: '任务进度动画与通知',
36
44
  },
37
45
  ]
@@ -39,24 +47,23 @@ export function apply(ctx) {
39
47
  const state = new Map()
40
48
  for (const f of FEATURES) state.set(f.id, { enabled: false, dispose: null, error: null })
41
49
 
42
- // 每个功能的 Host 半部安装函数:返回 disposer,关闭功能时调用。
43
- // 接入时在此实现,例如:
44
- // balance: () => {
45
- // const stop = ctx.interval(pullBalance, 60_000)
46
- // return () => stop()
47
- // },
48
- const hostSetups = {}
50
+ // 自有 settings 命名空间(dsh-dock):图片理解代理等插件配置的持久化;
51
+ // 读取走 settings.get(内存 resolved 值,写入经 settings.mutate,均热生效)
52
+ ctx.inject(['settings'], (sctx) => {
53
+ sctx.settings.register(DOCK_NS, DockConfig, {})
54
+ })
49
55
 
56
+ // 每个功能的 Host 半部安装函数:setup 返回 disposer,关闭功能时调用。
50
57
  function setEnabled(id, enabled) {
51
58
  const st = state.get(id)
52
59
  if (!st || st.enabled === enabled) return
53
60
  st.enabled = enabled
54
61
  st.error = null
55
62
  if (enabled) {
56
- const setup = hostSetups[id]
57
- if (setup) {
63
+ const f = FEATURES.find((x) => x.id === id)
64
+ if (f && typeof f.setup === 'function') {
58
65
  try {
59
- st.dispose = setup() || null
66
+ st.dispose = f.setup(ctx) || null
60
67
  } catch (err) {
61
68
  st.error = String((err && err.message) || err)
62
69
  console.error('[dsh-dock] feature setup failed:', id, err)
@@ -72,6 +79,13 @@ export function apply(ctx) {
72
79
  }
73
80
  }
74
81
 
82
+ // 默认启用已实现的功能(与 Client 半部 defaultEnabled 对齐)。
83
+ // 目前 Host↔Client 的开关同步在 0.5.0(持久化 + 双侧注册表打通)落地,
84
+ // 在此之前 Host 侧开关以本处 defaultEnabled 为准。
85
+ for (const f of FEATURES) {
86
+ if (f.defaultEnabled) setEnabled(f.id, true)
87
+ }
88
+
75
89
  console.log('[dsh-dock] host half loaded; features registered:', FEATURES.map((f) => f.id).join(', '))
76
90
 
77
91
  // 插件卸载/更新时兜底清理仍在运行的功能
@@ -86,4 +100,4 @@ export function apply(ctx) {
86
100
  }
87
101
  }
88
102
  })
89
- }
103
+ }
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "dsh-dock",
3
- "version": "0.1.0",
4
- "description": "DeepSeek Harness 功能中枢插件:用一张管理面板统一注册、开关所有小功能(模型余额、Token 用量记录、任务动画等)。0.1.0 为基础框架版本,功能接入路线见 README。",
3
+ "version": "0.4.1",
4
+ "description": "DeepSeek Harness 功能坞插件(dsh-dock):模块化功能中枢——每个功能一个 features/ 模块(可单独提取成包发布,又可经 dockBridge 装回面板)。含用量记录(LLM 调用记账/费用统计)、模型设置、图片理解代理、模型余额;侧栏入口 + 可拖拽弹窗(最大化/最小化/缩放)。",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "files": [
8
8
  "index.js",
9
9
  "client.js",
10
- "cordis.patch.yml"
10
+ "src/",
11
+ "features/",
12
+ "cordis.patch.yml",
13
+ "screenshots"
11
14
  ],
12
15
  "exports": {
13
16
  ".": "./index.js",
@@ -15,17 +18,23 @@
15
18
  "./package.json": "./package.json"
16
19
  },
17
20
  "dsh": {
18
- "bundle": { "patch": "./cordis.patch.yml" },
19
- "client": { "platform": "web" }
21
+ "bundle": {
22
+ "patch": "./cordis.patch.yml"
23
+ },
24
+ "client": {
25
+ "platform": "web"
26
+ }
20
27
  },
21
28
  "keywords": [
22
29
  "dsh",
23
30
  "deepseek-harness",
24
31
  "cordis",
25
32
  "plugin",
33
+ "dock",
34
+ "function-dock",
35
+ "功能坞",
26
36
  "panel",
27
37
  "hub",
28
- "feature-hub",
29
38
  "web",
30
39
  "dashboard"
31
40
  ],
@@ -38,5 +47,11 @@
38
47
  "homepage": "https://github.com/wycto/dsh-dock",
39
48
  "publishConfig": {
40
49
  "access": "public"
50
+ },
51
+ "dependencies": {
52
+ "@deepseek-ai/dsh-credentials": "^0.1.0-rc.6",
53
+ "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
54
+ "@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
55
+ "@deepseek-ai/schemastery": "^3.18.1"
41
56
  }
42
- }
57
+ }
Binary file
Binary file
Binary file