dsh-code 1.0.4 → 1.0.6
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/README.en.md +287 -286
- package/README.md +16 -13
- package/bin/deepseek.mjs +336 -11
- package/cordis.patch.yml +26 -18
- package/lib/index.mjs +1310 -439
- package/lib/types/app.d.ts +25 -6
- package/lib/types/attachments.d.ts +36 -4
- package/lib/types/git-workflow.d.ts +7 -2
- package/lib/types/history.d.ts +18 -11
- package/lib/types/index.d.ts +11 -2
- package/lib/types/presets.d.ts +4 -1
- package/lib/types/provider-settings.d.ts +6 -11
- package/lib/types/questions.d.ts +16 -12
- package/lib/types/render/animations.d.ts +74 -7
- package/lib/types/render/export.d.ts +0 -6
- package/lib/types/render/fuzzy.d.ts +21 -0
- package/lib/types/render/projection.d.ts +47 -5
- package/lib/types/session-directory.d.ts +48 -13
- package/lib/types/settings-file.d.ts +8 -0
- package/lib/types/store.d.ts +3 -0
- package/package.json +168 -159
- package/src/app.ts +480 -199
- package/src/attachments.ts +110 -11
- package/src/commands.ts +35 -5
- package/src/git-workflow.ts +29 -10
- package/src/history.ts +22 -13
- package/src/index.ts +1868 -1752
- package/src/internals.ts +61 -40
- package/src/permissions.ts +1 -1
- package/src/presets.ts +19 -6
- package/src/provider-settings.ts +12 -12
- package/src/questions.ts +57 -74
- package/src/render/animations.ts +606 -403
- package/src/render/export.ts +20 -10
- package/src/render/fuzzy.ts +83 -0
- package/src/render/projection.ts +1833 -1620
- package/src/session-directory.ts +94 -16
- package/src/settings-file.ts +38 -6
- package/src/skills.ts +23 -9
- package/src/store.ts +39 -1
- package/src/subagents.ts +26 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<p align="center"><img alt="Typing SVG" src="https://readme-typing-svg.herokuapp.com?font=JetBrains+Mono&weight=500&size=22&duration=4000&pause=700&color=4176E6&center=true&vCenter=true&width=680&lines=DeepSeek+Harness+Code;DSH+%E5%86%85%E6%A0%B8%E7%9A%84%E7%BB%88%E7%AB%AF%E7%BC%96%E7%A0%81%E7%95%8C%E9%9D%A2"></p>
|
|
8
8
|
<p align="center">
|
|
9
9
|
<a href="https://github.com/deepseek-ai/deepseek-harness"><img alt="DeepSeek Harness" src="https://img.shields.io/badge/DeepSeek-Harness-4176E6?style=for-the-badge&logo=deepseek&logoColor=white&labelColor=1c1917"></a>
|
|
10
|
-
<a href="https://www.npmjs.com/package/@deepseek-ai/dsh"><img alt="dsh version" src="https://img.shields.io/badge/dsh-0.1.
|
|
10
|
+
<a href="https://www.npmjs.com/package/@deepseek-ai/dsh"><img alt="dsh version" src="https://img.shields.io/badge/dsh-0.1.5--rc.1-4176E6?style=for-the-badge&logo=deepseek&logoColor=white&labelColor=1c1917"></a>
|
|
11
11
|
<a href="https://github.com/UNLINEARITY/dsh-code/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/UNLINEARITY/dsh-code?label=Stars&style=for-the-badge&logo=github&logoColor=white&color=4176E6&labelColor=1c1917"></a>
|
|
12
12
|
<a href="https://www.npmjs.com/package/dsh-code"><img alt="npm version" src="https://img.shields.io/npm/v/dsh-code?label=npm&style=for-the-badge&logo=npm&color=cb3837&labelColor=1c1917"></a>
|
|
13
13
|
<a href="https://github.com/UNLINEARITY/dsh-code/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/UNLINEARITY/dsh-code?label=License&style=for-the-badge&logo=opensourceinitiative&color=4176E6&labelColor=1c1917"></a>
|
|
@@ -25,21 +25,23 @@ DeepSeek Harness 将模型、工具、存储、策略和界面作为插件,通
|
|
|
25
25
|
|
|
26
26
|
## 二、快速开始
|
|
27
27
|
|
|
28
|
-
需要 Node `^22.19 || >=24` 和预览版 `dsh` CLI(当前版本线:`@deepseek-ai/dsh@0.1.
|
|
28
|
+
需要 Node `^22.19 || >=24` 和预览版 `dsh` CLI(当前版本线:`@deepseek-ai/dsh@0.1.5-rc.1`)。未配置模型时仍可进入 TUI、查看会话和使用非模型功能;在 `/model` 中按 Tab 进入供应商管理,配置 API key、OAuth 与设备码登录。
|
|
29
29
|
|
|
30
30
|
### 1. 安装与更新
|
|
31
31
|
|
|
32
32
|
初次安装和更新使用同一组指令:
|
|
33
33
|
|
|
34
34
|
```sh
|
|
35
|
-
npm install -g @deepseek-ai/dsh@0.1.
|
|
35
|
+
npm install -g @deepseek-ai/dsh@0.1.5-rc.1 dsh-code@1.0.6
|
|
36
36
|
npm install -g pnpm
|
|
37
|
-
dsh plugin --profile cli add dsh-code@1.0.
|
|
37
|
+
dsh plugin --profile cli add dsh-code@1.0.6
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
> 提示:pnpm 会忽略发布不足 24 小时的包,因此发布首日请使用精确版本 `dsh-code@1.0.
|
|
40
|
+
> 提示:pnpm 会忽略发布不足 24 小时的包,因此发布首日请使用精确版本 `dsh-code@1.0.6`;24 小时后可省略版本号。npm 安装不受此限制。
|
|
41
41
|
>
|
|
42
|
-
> 版本对齐:dsh-code
|
|
42
|
+
> 版本对齐:dsh-code 面向 dsh `0.1.5-rc.1` 构建,全部 Harness 依赖均精确锁定为 `0.1.5-rc.1`。已安装的用户运行 `deepseek update --apply` 即可按同一条版本线一起升级全局宿主与 cli profile 中的插件,本地开发挂载(`link:`)不受影响。
|
|
43
|
+
>
|
|
44
|
+
> 升级说明:旧会话与旧参数中记录的 `code` 预设会自动映射到上游已改名的 `ptc`,无需手动迁移。会话日志读取端随上游升级到格式 v3:旧格式日志在读取时由内核自动迁移,磁盘上的原始文件保持不变。
|
|
43
45
|
|
|
44
46
|
### 2. 启动指令
|
|
45
47
|
|
|
@@ -77,7 +79,7 @@ DSH-Code 的重点是让 DSH 的 Agent、模型、工具和持久会话可以直
|
|
|
77
79
|
### 2. Agent、模型与扩展
|
|
78
80
|
|
|
79
81
|
- 每个会话可以选择独立的 Agent Preset,用于组合工具、提示词、技能、上下文压缩、plan mode 和 subagent 能力
|
|
80
|
-
- 使用 `/mode` 选择 `standard`、`
|
|
82
|
+
- 使用 `/mode` 选择 `standard`、`ptc`、`minimal`、`cordis` 或用户自定义 Preset(旧名称 `code` 自动映射到 `ptc`)
|
|
81
83
|
- 使用 `/model` 切换模型,管理 provider、API key、OAuth/设备码登录、endpoint、可用模型和上下文窗口;`Tab` 进入 provider 管理(已配置供应商置顶分组),任意阶段 `Ctrl+C` 直接退出整个 /model 流程
|
|
82
84
|
- 在 provider 列表中,Enter 进入统一配置页:同页填写 API key 与 endpoint(留空即官方默认)、编辑已添加模型的上下文/输出窗口,`Tab` 进入发现页拉取端点真实可用模型并勾选添加
|
|
83
85
|
- 在统一配置页的模型行上按 `e` 编辑推理档位声明(格式 `low:low high:high max:max`,`false` 禁用、留空恢复继承),按 `c` 从其他已声明模型逐字复制——例如 GLM 系列按官方三档写 `low:low high:high max:max`,新模型(如 gpt-6)可一键复制 gpt-5.6 的映射
|
|
@@ -109,7 +111,7 @@ DSH-Code 的重点是让 DSH 的 Agent、模型、工具和持久会话可以直
|
|
|
109
111
|
### 4. 编码工作流
|
|
110
112
|
|
|
111
113
|
- 使用 `@` 引用工作区文件或已有会话;选择 PNG、JPEG、WebP、GIF 时会自动作为真实图片附件
|
|
112
|
-
- 支持启动 prompt、多个 `--image`
|
|
114
|
+
- 支持启动 prompt、多个 `--image` 参数,以及从终端拖入或粘贴附件:图片按图片附件发送,其他文件按原样文件附件发送(单文件 8 MiB、每条消息 8 个以内)
|
|
113
115
|
- 使用 `/diff` 按文件检查改动,使用 `/review` 发起只读代码审查
|
|
114
116
|
- 使用 `/copy` 复制最近一条完整回复,使用 Ctrl+O 查看完整历史和工具详情
|
|
115
117
|
- 支持工具审批、结构化提问、plan review、多选和自定义答案
|
|
@@ -121,7 +123,7 @@ DSH-Code 的重点是让 DSH 的 Agent、模型、工具和持久会话可以直
|
|
|
121
123
|
|
|
122
124
|
```sh
|
|
123
125
|
dsh --profile cli # 新建 standard 会话
|
|
124
|
-
dsh --profile cli --mode
|
|
126
|
+
dsh --profile cli --mode ptc # 使用指定 Agent Preset 启动(standard/minimal/cordis/ptc)
|
|
125
127
|
dsh --profile cli --continue # 恢复当前目录最新会话
|
|
126
128
|
dsh --profile cli --resume abc123 # 按 id 或唯一前缀恢复会话
|
|
127
129
|
dsh --profile cli --session my-id # 使用指定 id 新建会话
|
|
@@ -158,7 +160,7 @@ dsh --profile cli --session my-id # 使用指定 id 新建会话
|
|
|
158
160
|
| 命令 | 用途 |
|
|
159
161
|
| --- | --- |
|
|
160
162
|
| `/diff [--staged\|ref]` | 按文件查看工作区、暂存区或指定 ref 的 Git diff |
|
|
161
|
-
| `/review [--staged\|ref]` | 使用只读权限审查 Git
|
|
163
|
+
| `/review [--staged\|ref]` | 使用只读权限审查 Git 改动;新会话中可直接使用,审查提示词作为首条消息建立会话 |
|
|
162
164
|
| `/todos` | 查看当前会话的完整 todo 列表 |
|
|
163
165
|
| `/agents` | 查看当前会话创建的 subagent 会话 |
|
|
164
166
|
| `/jobs` | 查看后台任务及其运行状态 |
|
|
@@ -172,6 +174,7 @@ dsh --profile cli --session my-id # 使用指定 id 新建会话
|
|
|
172
174
|
| `/statusline` | 选择状态栏显示的项目 |
|
|
173
175
|
| `/vscode-keys` | 将 Ctrl+R 放行进 VS Code 系终端(幂等写入用户级 keybindings.json) |
|
|
174
176
|
| `/theme` | 切换终端配色主题 |
|
|
177
|
+
| `/animation` | 开关计时动画(shimmer/追逐/闪烁/切换波浪),`/animation [on\|off]` |
|
|
175
178
|
| `/help` | 查看快捷键、内置命令、Harness 命令和用户技能 |
|
|
176
179
|
| `/quit` | 退出 DSH-Code |
|
|
177
180
|
|
|
@@ -214,8 +217,8 @@ DSH-Code 读取 Harness 的实时注册表,不在本地维护另一套副本
|
|
|
214
217
|
Host 持有共享基础设施——注册表、持久化、会话查询、权限和 sandbox 策略;每个会话则获得一个隔离的 Agent scope,并由 **Agent Preset** 进行组合:
|
|
215
218
|
|
|
216
219
|
- `standard`——功能完整的通用编码 Agent
|
|
217
|
-
- `
|
|
218
|
-
- `minimal
|
|
220
|
+
- `ptc`——面向 PTC(原 Code Mode)的多操作工作流;旧名称 `code` 仍可使用
|
|
221
|
+
- `minimal`——仅保留持久 shell 的单工具精简组合
|
|
219
222
|
- `cordis`——完整 Agent,加上运行时检查与 Preset 编写指导
|
|
220
223
|
- 用户预设——自行定义工具、提示词段落、技能、上下文压缩、plan mode 与 subagent 行为
|
|
221
224
|
|
|
@@ -223,7 +226,7 @@ Host 持有共享基础设施——注册表、持久化、会话查询、权限
|
|
|
223
226
|
|
|
224
227
|
### 3. 会话记录与恢复
|
|
225
228
|
|
|
226
|
-
|
|
229
|
+
提示词、工具调用与结果、模型选择、plan 状态、权限、标题和 preset 选择都由持久 Session 事件投影得到;会话恢复、导出、历史检查、上下文统计和终端重放使用同一份记录。实时流式文本经进程内流帧呈现,落盘日志只保留装配完成的回复(内嵌计时流),两者在重放时得到同一视图。
|
|
227
230
|
|
|
228
231
|
React state 只保存输入草稿、光标、当前面板、选中项和滚动位置等临时界面状态。
|
|
229
232
|
|
package/bin/deepseek.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { spawn, spawnSync } from 'node:child_process'
|
|
|
4
4
|
import { existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
5
5
|
import { createRequire } from 'node:module'
|
|
6
6
|
import { homedir } from 'node:os'
|
|
7
|
-
import { join, resolve } from 'node:path'
|
|
7
|
+
import { dirname, join, resolve } from 'node:path'
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
9
9
|
|
|
10
10
|
const packageRequire = createRequire(import.meta.url)
|
|
@@ -49,6 +49,165 @@ export function profileHasDshCode(profileDir = cliProfileDir(), fileExists = exi
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/** The dsh-code dependency the cli profile declares (e.g. '1.0.5' or 'link:C:/repo'). */
|
|
53
|
+
export function profileDependencySpec(profileDir = cliProfileDir(), fileExists = existsSync, readFile = readFileSync) {
|
|
54
|
+
const manifest = join(profileDir, 'package.json')
|
|
55
|
+
if (!fileExists(manifest)) return undefined
|
|
56
|
+
try {
|
|
57
|
+
const raw = JSON.parse(readFile(manifest, 'utf8'))
|
|
58
|
+
const spec = raw?.dependencies?.['dsh-code']
|
|
59
|
+
return typeof spec === 'string' ? spec : undefined
|
|
60
|
+
} catch {
|
|
61
|
+
return undefined
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The dsh-code version the cli profile actually resolves and boots. */
|
|
66
|
+
export function profileMountedVersion(profileDir = cliProfileDir(), fileExists = existsSync, readFile = readFileSync) {
|
|
67
|
+
const manifest = join(profileDir, 'node_modules', 'dsh-code', 'package.json')
|
|
68
|
+
if (!fileExists(manifest)) return undefined
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(readFile(manifest, 'utf8'))?.version ?? undefined
|
|
71
|
+
} catch {
|
|
72
|
+
return undefined
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Profile-installed harness plugins that a line upgrade must carry along:
|
|
78
|
+
* every `@deepseek-ai/dsh-*` dependency except the dsh-code target itself
|
|
79
|
+
* and local checkouts (`link:`/`file:` stay untouched). Users install these
|
|
80
|
+
* through `dsh plugin add` once; without this pass `update --apply` would
|
|
81
|
+
* move the host and dsh-code forward and silently leave every other plugin
|
|
82
|
+
* pinned to the old line.
|
|
83
|
+
* @param profileDir - the cli profile root.
|
|
84
|
+
* @returns dependency entries as `{ name, spec }` in manifest order.
|
|
85
|
+
*/
|
|
86
|
+
export function profilePluginDependencies(profileDir = cliProfileDir(), fileExists = existsSync, readFile = readFileSync) {
|
|
87
|
+
const manifest = join(profileDir, 'package.json')
|
|
88
|
+
if (!fileExists(manifest)) return []
|
|
89
|
+
let dependencies
|
|
90
|
+
try {
|
|
91
|
+
dependencies = JSON.parse(readFile(manifest, 'utf8'))?.dependencies
|
|
92
|
+
} catch {
|
|
93
|
+
return []
|
|
94
|
+
}
|
|
95
|
+
if (typeof dependencies !== 'object' || dependencies === null) return []
|
|
96
|
+
const plugins = []
|
|
97
|
+
for (const [name, spec] of Object.entries(dependencies)) {
|
|
98
|
+
if (typeof spec !== 'string') continue
|
|
99
|
+
if (!name.startsWith('@deepseek-ai/dsh-')) continue
|
|
100
|
+
if (name === 'dsh-code' || name === '@deepseek-ai/dsh-base') continue
|
|
101
|
+
if (/^(link|file):/iu.test(spec)) continue
|
|
102
|
+
plugins.push({ name, spec })
|
|
103
|
+
}
|
|
104
|
+
return plugins
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The harness release line a dsh-code release expects, read from its peers. */
|
|
108
|
+
export function harnessLineFromPeers(peers) {
|
|
109
|
+
if (typeof peers !== 'object' || peers === null) return undefined
|
|
110
|
+
const anchor = peers['@deepseek-ai/dsh-session']
|
|
111
|
+
if (typeof anchor === 'string' && anchor !== '') return anchor
|
|
112
|
+
for (const [name, spec] of Object.entries(peers)) {
|
|
113
|
+
if (name.startsWith('@deepseek-ai/dsh-') && typeof spec === 'string' && spec !== '') return spec
|
|
114
|
+
}
|
|
115
|
+
return undefined
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Decide what `update --apply` installs. The global DSH launcher is pinned
|
|
120
|
+
* to the harness line the target dsh-code release declares in its peers,
|
|
121
|
+
* so the launcher can never move ahead of the plugin it must boot. A
|
|
122
|
+
* profile that mounts a local checkout (link:/file:) keeps its mount.
|
|
123
|
+
*/
|
|
124
|
+
export function updatePlan({ latestCode, peers, profileSpec, profilePlugins = [] }) {
|
|
125
|
+
const line = harnessLineFromPeers(peers)
|
|
126
|
+
const profileStep = !(typeof profileSpec === 'string' && /^(link|file):/iu.test(profileSpec))
|
|
127
|
+
return {
|
|
128
|
+
dshSpec: line === undefined ? '@deepseek-ai/dsh@latest' : `@deepseek-ai/dsh@${line}`,
|
|
129
|
+
line: line === undefined ? undefined : line,
|
|
130
|
+
lineLocked: line !== undefined,
|
|
131
|
+
codeSpec: `dsh-code@${latestCode}`,
|
|
132
|
+
profileStep,
|
|
133
|
+
// Companion plugins the profile installed through `dsh plugin add`:
|
|
134
|
+
// carried to the same line so one command moves the whole profile.
|
|
135
|
+
pluginSpecs: line === undefined || !profileStep ? [] : profilePlugins
|
|
136
|
+
.filter(plugin => plugin.spec !== line)
|
|
137
|
+
.map(plugin => `${plugin.name}@${line}`),
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Compare two @deepseek-ai/dsh release-line versions (`0.1.x`, `0.1.x-rc.N`,
|
|
143
|
+
* `0.1.x-alpha.N`). Positive when `left` is newer, negative when older, and 0
|
|
144
|
+
* on equality or an unparseable value (an unreadable version never blocks an
|
|
145
|
+
* update — release semantics follow semver prerelease ordering: final > rc >
|
|
146
|
+
* beta > alpha).
|
|
147
|
+
*/
|
|
148
|
+
export function compareHarnessLines(left, right) {
|
|
149
|
+
const parse = (value) => {
|
|
150
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-(alpha|beta|rc)\.(\d+))?$/.exec(String(value))
|
|
151
|
+
if (match === null) return undefined
|
|
152
|
+
return {
|
|
153
|
+
core: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
154
|
+
tag: match[4] === undefined ? 3 : { alpha: 0, beta: 1, rc: 2 }[match[4]],
|
|
155
|
+
num: match[5] === undefined ? 0 : Number(match[5]),
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const a = parse(left)
|
|
159
|
+
const b = parse(right)
|
|
160
|
+
if (a === undefined || b === undefined) return 0
|
|
161
|
+
for (let index = 0; index < 3; index += 1) {
|
|
162
|
+
if (a.core[index] !== b.core[index]) return a.core[index] - b.core[index]
|
|
163
|
+
}
|
|
164
|
+
if (a.tag !== b.tag) return a.tag - b.tag
|
|
165
|
+
return a.num - b.num
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The globally installed @deepseek-ai/dsh version, when one exists across the
|
|
170
|
+
* npm global roots this launcher may boot from. An update whose plan targets a
|
|
171
|
+
* line OLDER than this would silently downgrade the booted host.
|
|
172
|
+
*/
|
|
173
|
+
export function installedGlobalDshVersion(roots = globalDshRoots(), fileExists = existsSync, readFile = readFileSync) {
|
|
174
|
+
for (const root of roots) {
|
|
175
|
+
const manifest = join(root, 'node_modules', '@deepseek-ai', 'dsh', 'package.json')
|
|
176
|
+
if (!fileExists(manifest)) continue
|
|
177
|
+
try {
|
|
178
|
+
const version = JSON.parse(readFile(manifest, 'utf8'))?.version
|
|
179
|
+
if (typeof version === 'string' && version !== '') return version
|
|
180
|
+
} catch {
|
|
181
|
+
// Unreadable manifest: try the next root.
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return undefined
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Run wrapper-owned child steps in order, stopping at the first failure. */
|
|
188
|
+
export function runSequence(steps, spawnProcess = spawn) {
|
|
189
|
+
return new Promise(resolve => {
|
|
190
|
+
const run = index => {
|
|
191
|
+
const step = steps[index]
|
|
192
|
+
if (step === undefined) {
|
|
193
|
+
resolve(0)
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
const needsShell = process.platform === 'win32' && /\.(cmd|bat)$/iu.test(step.command)
|
|
197
|
+
const child = spawnProcess(step.command, step.args, { stdio: 'inherit', ...(needsShell ? { shell: true } : {}) })
|
|
198
|
+
child.once('error', error => {
|
|
199
|
+
console.error(`dsh-code: ${step.label} failed: ${error.message}`)
|
|
200
|
+
resolve(1)
|
|
201
|
+
})
|
|
202
|
+
child.once('exit', (code, signal) => {
|
|
203
|
+
if (code === 0) run(index + 1)
|
|
204
|
+
else resolve(code ?? 1)
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
run(0)
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
52
211
|
/** Npm global-prefix roots that may contain DSH when this launcher is globally linked to a checkout. */
|
|
53
212
|
export function globalDshRoots() {
|
|
54
213
|
return [...new Set([
|
|
@@ -133,6 +292,175 @@ function launchChild(command, args) {
|
|
|
133
292
|
return child
|
|
134
293
|
}
|
|
135
294
|
|
|
295
|
+
/**
|
|
296
|
+
* How to invoke npm. The JavaScript entrypoint is preferred: running it
|
|
297
|
+
* with this process's node avoids the Windows .cmd shim, whose shell
|
|
298
|
+
* workaround draws a Node deprecation warning on every call.
|
|
299
|
+
*/
|
|
300
|
+
export function npmInvocation({
|
|
301
|
+
fileExists = existsSync,
|
|
302
|
+
roots = globalDshRoots(),
|
|
303
|
+
resolvePackage = packageRequire.resolve,
|
|
304
|
+
} = {}) {
|
|
305
|
+
const besideNode = join(dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js')
|
|
306
|
+
if (fileExists(besideNode)) return { command: process.execPath, args: [besideNode] }
|
|
307
|
+
for (const root of roots) {
|
|
308
|
+
try {
|
|
309
|
+
const cli = resolvePackage('npm/bin/npm-cli.js', { paths: [root] })
|
|
310
|
+
return { command: process.execPath, args: [cli] }
|
|
311
|
+
} catch {
|
|
312
|
+
// The next configured global prefix may own npm instead.
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { command: process.platform === 'win32' ? 'npm.cmd' : 'npm', args: [] }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Read one `npm view` field as JSON, or undefined when the query fails.
|
|
320
|
+
* The subject arrives as argument parts: without a shell, a space inside
|
|
321
|
+
* one argument would reach npm as a single selector and fail.
|
|
322
|
+
*/
|
|
323
|
+
function viewJson(subjectParts, spawnCommand = spawnSync, invocation = npmInvocation()) {
|
|
324
|
+
const needsShell = process.platform === 'win32' && /\.(cmd|bat)$/iu.test(invocation.command)
|
|
325
|
+
const result = spawnCommand(invocation.command, [...invocation.args, 'view', ...subjectParts, '--json'], { encoding: 'utf8', windowsHide: true, ...(needsShell ? { shell: true } : {}) })
|
|
326
|
+
if (result.status !== 0) return undefined
|
|
327
|
+
try {
|
|
328
|
+
return JSON.parse(result.stdout)
|
|
329
|
+
} catch {
|
|
330
|
+
return undefined
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* The version of @deepseek-ai/dsh this launcher would boot, when present.
|
|
336
|
+
* Probes the same surfaces rawDshCommand resolves: the adjacent global
|
|
337
|
+
* install first, then every configured npm global prefix.
|
|
338
|
+
*/
|
|
339
|
+
function installedDshVersion({
|
|
340
|
+
platform = process.platform,
|
|
341
|
+
moduleUrl = import.meta.url,
|
|
342
|
+
fileExists = existsSync,
|
|
343
|
+
roots = globalDshRoots(),
|
|
344
|
+
resolvePackage = packageRequire.resolve,
|
|
345
|
+
} = {}) {
|
|
346
|
+
const readVersion = path => {
|
|
347
|
+
try {
|
|
348
|
+
return JSON.parse(readFileSync(path, 'utf8')).version
|
|
349
|
+
} catch {
|
|
350
|
+
return undefined
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (platform === 'win32') {
|
|
354
|
+
const adjacent = fileUrlToWindowsPath(new URL('../../@deepseek-ai/dsh/package.json', moduleUrl))
|
|
355
|
+
if (fileExists(adjacent)) return readVersion(adjacent)
|
|
356
|
+
} else {
|
|
357
|
+
try {
|
|
358
|
+
const adjacent = fileURLToPath(new URL('../../@deepseek-ai/dsh/package.json', moduleUrl))
|
|
359
|
+
if (fileExists(adjacent)) return readVersion(adjacent)
|
|
360
|
+
} catch {
|
|
361
|
+
// A non-file URL or an unreadable sibling falls through to the roots.
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
for (const root of roots) {
|
|
365
|
+
try {
|
|
366
|
+
return readVersion(resolvePackage('@deepseek-ai/dsh/package.json', { paths: [root] }))
|
|
367
|
+
} catch {
|
|
368
|
+
// The next configured global prefix may own DSH instead.
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return undefined
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** Show what is installed, what is latest, and what the cli profile boots. */
|
|
375
|
+
function printUpdateStatus() {
|
|
376
|
+
const codeLatest = viewJson(['dsh-code', 'version'])
|
|
377
|
+
const dshLatest = viewJson(['@deepseek-ai/dsh', 'version'])
|
|
378
|
+
const dshInstalled = installedDshVersion()
|
|
379
|
+
const spec = profileDependencySpec()
|
|
380
|
+
const mounted = profileMountedVersion()
|
|
381
|
+
console.log(`dsh-code: ${packageVersion} (this launcher), latest ${codeLatest ?? 'unknown'}`)
|
|
382
|
+
console.log(`@deepseek-ai/dsh: ${dshInstalled ?? 'not found'} (global), latest ${dshLatest ?? 'unknown'}`)
|
|
383
|
+
console.log(`cli profile: ${mounted !== undefined ? `dsh-code ${mounted}` : spec ?? 'not mounted'}`)
|
|
384
|
+
console.log('Run `deepseek update --apply` to upgrade the global packages and the cli profile together.')
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Upgrade the global launcher and the cli profile plugin together. The
|
|
389
|
+
* global install is pinned to the harness line the new dsh-code release
|
|
390
|
+
* declares, the profile follows through `dsh plugin add` with the same
|
|
391
|
+
* pinned spec, and the mounted version is verified at the end.
|
|
392
|
+
*/
|
|
393
|
+
async function applyUpdate({
|
|
394
|
+
view = viewJson,
|
|
395
|
+
resolveCommand = rawDshCommand,
|
|
396
|
+
spawnProcess = spawn,
|
|
397
|
+
} = {}) {
|
|
398
|
+
const npm = npmInvocation()
|
|
399
|
+
const latestCode = view(['dsh-code', 'version'], undefined, npm)
|
|
400
|
+
if (latestCode === undefined) {
|
|
401
|
+
console.error('dsh-code: could not read the latest dsh-code version from npm')
|
|
402
|
+
process.exitCode = 1
|
|
403
|
+
return
|
|
404
|
+
}
|
|
405
|
+
const peers = view([`dsh-code@${latestCode}`, 'peerDependencies'], undefined, npm)
|
|
406
|
+
const plan = updatePlan({
|
|
407
|
+
latestCode,
|
|
408
|
+
peers,
|
|
409
|
+
profileSpec: profileDependencySpec(),
|
|
410
|
+
profilePlugins: profilePluginDependencies(),
|
|
411
|
+
})
|
|
412
|
+
if (!plan.lineLocked) {
|
|
413
|
+
console.log('dsh-code: could not read the compatible harness line; installing @deepseek-ai/dsh@latest')
|
|
414
|
+
}
|
|
415
|
+
// Downgrade guard: the latest published dsh-code may still pin an older
|
|
416
|
+
// harness line than the host already installed globally (a release-ordering
|
|
417
|
+
// window). Installing would silently downgrade every existing session's
|
|
418
|
+
// booted host, and a link-mounted profile would end up on a mismatched pair
|
|
419
|
+
// with no warning — refuse instead and name the manual command.
|
|
420
|
+
const installed = installedGlobalDshVersion()
|
|
421
|
+
if (plan.line !== undefined && installed !== undefined && compareHarnessLines(plan.line, installed) < 0) {
|
|
422
|
+
console.error(`dsh-code: dsh-code@${latestCode} needs @deepseek-ai/dsh@${plan.line}, but ${installed} is already installed globally`)
|
|
423
|
+
console.error(`dsh-code: refusing to downgrade the host; to proceed anyway run: npm install -g @deepseek-ai/dsh@${plan.line} dsh-code@${latestCode}`)
|
|
424
|
+
process.exitCode = 1
|
|
425
|
+
return
|
|
426
|
+
}
|
|
427
|
+
const steps = [{ command: npm.command, args: [...npm.args, 'install', '-g', plan.dshSpec, plan.codeSpec], label: 'npm install' }]
|
|
428
|
+
if (plan.profileStep) {
|
|
429
|
+
const command = resolveCommand(['plugin', '--profile', 'cli', 'add', plan.codeSpec])
|
|
430
|
+
if (command === undefined) {
|
|
431
|
+
console.error('dsh-code: could not resolve the dsh command for the profile update')
|
|
432
|
+
process.exitCode = 1
|
|
433
|
+
return
|
|
434
|
+
}
|
|
435
|
+
steps.push({ command: command.command, args: command.args, label: 'dsh plugin add' })
|
|
436
|
+
// Companion plugins ride the same line: without this, `dsh plugin add`-ed
|
|
437
|
+
// extras stay pinned to the old harness line after an upgrade.
|
|
438
|
+
for (const pluginSpec of plan.pluginSpecs) {
|
|
439
|
+
const pluginCommand = resolveCommand(['plugin', '--profile', 'cli', 'add', pluginSpec])
|
|
440
|
+
if (pluginCommand === undefined) {
|
|
441
|
+
console.error(`dsh-code: could not resolve the dsh command for ${pluginSpec}`)
|
|
442
|
+
process.exitCode = 1
|
|
443
|
+
return
|
|
444
|
+
}
|
|
445
|
+
steps.push({ command: pluginCommand.command, args: pluginCommand.args, label: `dsh plugin add ${pluginSpec}` })
|
|
446
|
+
}
|
|
447
|
+
if (plan.pluginSpecs.length > 0) {
|
|
448
|
+
console.log(`carrying ${plan.pluginSpecs.length} profile plugin${plan.pluginSpecs.length === 1 ? '' : 's'} to the same line: ${plan.pluginSpecs.join(', ')}`)
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
console.log('cli profile mounts a local checkout; leaving the profile untouched')
|
|
452
|
+
}
|
|
453
|
+
const code = await runSequence(steps, spawnProcess)
|
|
454
|
+
const mounted = profileMountedVersion()
|
|
455
|
+
if (mounted !== undefined) console.log(`cli profile now mounts dsh-code ${mounted}`)
|
|
456
|
+
if (code === 0 && plan.profileStep && mounted !== latestCode) {
|
|
457
|
+
console.error(`dsh-code: the cli profile still mounts ${mounted ?? 'nothing'}; run: dsh plugin --profile cli add ${plan.codeSpec}`)
|
|
458
|
+
process.exitCode = 1
|
|
459
|
+
return
|
|
460
|
+
}
|
|
461
|
+
process.exitCode = code
|
|
462
|
+
}
|
|
463
|
+
|
|
136
464
|
/** Run one wrapper-owned operational command. */
|
|
137
465
|
export function launchOperation(args = process.argv.slice(2)) {
|
|
138
466
|
const operation = operationName(args)
|
|
@@ -161,17 +489,14 @@ export function launchOperation(args = process.argv.slice(2)) {
|
|
|
161
489
|
}
|
|
162
490
|
return launchChild(command.command, command.args)
|
|
163
491
|
}
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
console.log(`${name}: ${result.status === 0 ? String(result.stdout).trim() : 'version check failed'}`)
|
|
492
|
+
if (operation === 'update') {
|
|
493
|
+
if (args.includes('--apply')) {
|
|
494
|
+
void applyUpdate()
|
|
495
|
+
return true
|
|
496
|
+
}
|
|
497
|
+
printUpdateStatus()
|
|
498
|
+
return true
|
|
172
499
|
}
|
|
173
|
-
console.log('Run `deepseek update --apply` to install the latest versions.')
|
|
174
|
-
return true
|
|
175
500
|
}
|
|
176
501
|
|
|
177
502
|
/** Launch the installed DSH CLI while preserving its exit status and stdio. */
|
package/cordis.patch.yml
CHANGED
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
# dispatches slash commands through the shared registry, and folds submitted
|
|
8
8
|
# prompts back into the same durable session until the user quits.
|
|
9
9
|
|
|
10
|
+
# 0.1.5 split the deployment persona into a prefix and a suffix template
|
|
11
|
+
# around the first-party guidance sections; this terminal keeps its identity
|
|
12
|
+
# line as the prefix ({{variable}} interpolation stays strict upstream).
|
|
10
13
|
- id: system-prompt
|
|
11
14
|
config:
|
|
12
|
-
|
|
15
|
+
personaPrefix: >-
|
|
13
16
|
You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
|
|
14
17
|
|
|
15
18
|
# The shared module-reload HMR row stays off; the launcher's watch-only
|
|
@@ -34,12 +37,18 @@
|
|
|
34
37
|
disabled: true
|
|
35
38
|
- id: tool-fs-search
|
|
36
39
|
disabled: true
|
|
37
|
-
-
|
|
38
|
-
|
|
40
|
+
# skill-filesystem stays ENABLED on the host: it only feeds the skill
|
|
41
|
+
# REGISTRY (local-root discovery), never the model-facing tool surface —
|
|
42
|
+
# tool-skill below stays disabled and presets mount it per session. The
|
|
43
|
+
# terminal offers skills in the slash menu before any session exists, and a
|
|
44
|
+
# bare launch has no preset layer yet, so the registry's global layer needs
|
|
45
|
+
# this provider; presets shadow it with the same discovery on their scope.
|
|
39
46
|
- id: skill-filesystem
|
|
40
|
-
disabled:
|
|
47
|
+
disabled: false
|
|
41
48
|
- id: tool-skill
|
|
42
49
|
disabled: true
|
|
50
|
+
- id: command-goal
|
|
51
|
+
disabled: true
|
|
43
52
|
- id: tool-goal
|
|
44
53
|
disabled: true
|
|
45
54
|
- id: plan-mode
|
|
@@ -68,15 +77,11 @@
|
|
|
68
77
|
disabled: true
|
|
69
78
|
- id: tool-todo
|
|
70
79
|
disabled: true
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# registry the preset's web_search shadows the host's same-name tool while
|
|
77
|
-
# the host's web_fetch stays visible, so every model still sees both.
|
|
78
|
-
fetch: true
|
|
79
|
-
searchTimeoutMs: 60000
|
|
80
|
+
# tool-web keeps the 0.1.5-rc.1 base default (web_fetch enabled over the
|
|
81
|
+
# bundled web-fetch-http provider): the host row exposes web_fetch while the
|
|
82
|
+
# per-preset agent-plane rows keep their own fetch stance. The upstream base
|
|
83
|
+
# bundle dropped tool-str-replace-editor entirely in 0.1.5 (the minimal
|
|
84
|
+
# preset became a single-tool composition), so this patch no longer lists it.
|
|
80
85
|
|
|
81
86
|
- insert:
|
|
82
87
|
# Code Mode is a core execution capability, not a Web component.
|
|
@@ -111,11 +116,14 @@
|
|
|
111
116
|
- id: cordis-host-runner
|
|
112
117
|
name: '@deepseek-ai/dsh-cordis-host-runner'
|
|
113
118
|
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
# this
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
# Host-owned opt-in the preset delegation tools sample: standard/ptc's
|
|
120
|
+
# tool-subagent rows set `modelSelectionSettings: true`, whose start
|
|
121
|
+
# resolves this service and fails the whole preset mount when absent
|
|
122
|
+
# ("failed to apply loader entry delegation (cordis:group)"). The upstream
|
|
123
|
+
# web-app bundle carries this host row in 0.1.5-rc.1; dsh-base still does
|
|
124
|
+
# not ship it.
|
|
125
|
+
- id: subagent-model-selection-settings
|
|
126
|
+
name: '@deepseek-ai/dsh-tool-subagent/model-selection-settings'
|
|
119
127
|
|
|
120
128
|
# Interactive provider authorization. The base bundle supplies the
|
|
121
129
|
# credential store; mounting the neutral authorization seam wakes the
|