@crazyhappyone/dsh-tui 0.1.0-alpha.49 → 0.1.0-alpha.50
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 +49 -0
- package/README.i18n.yaml +2 -2
- package/README.md +116 -31
- package/README.zh.md +115 -30
- package/assets/screenshots/dsh-tui-settings.png +0 -0
- package/bin/dsh-tui.js +4 -3
- package/cordis.patch.yml +65 -59
- package/dist/index.js +22846 -4975
- package/dist/schedule.js +36041 -0
- package/dist/settings.js +961 -0
- package/dist/zen-proxy.js +353 -235
- package/package.json +79 -6
- package/presets/base-disable.patch.yml +82 -0
- package/presets/cordis.patch.yml +136 -0
- package/presets/minimal.patch.yml +78 -0
- package/presets/ptc.patch.yml +134 -0
- package/presets/registry.patch.yml +12 -0
- package/presets/standard.patch.yml +128 -0
- package/src/launcher.js +146 -64
- package/src/release-patch.js +0 -23
- package/tui/README.md +0 -146
- package/tui/README.zh.md +0 -146
- package/tui/cordis.patch.yml +0 -161
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Agent preset standard: one `@deepseek-ai/dsh-agent-preset` declaration inserted
|
|
2
|
+
# after the web patch. Edits saved from the Web editor override this row's
|
|
3
|
+
# `config.plugins` by id from the profile patch.
|
|
4
|
+
- insert:
|
|
5
|
+
- id: preset-standard
|
|
6
|
+
name: '@deepseek-ai/dsh-agent-preset'
|
|
7
|
+
config:
|
|
8
|
+
id: standard
|
|
9
|
+
order: 1
|
|
10
|
+
plugins:
|
|
11
|
+
- id: persona
|
|
12
|
+
name: '@deepseek-ai/dsh-persona'
|
|
13
|
+
config:
|
|
14
|
+
prefix: You are a coding agent powered by the {{model}} model.
|
|
15
|
+
- id: agent-instructions
|
|
16
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
17
|
+
config:
|
|
18
|
+
maxBytes: 65536
|
|
19
|
+
- id: time-context
|
|
20
|
+
name: '@deepseek-ai/dsh-time-context'
|
|
21
|
+
- id: tool-bash
|
|
22
|
+
name: '@deepseek-ai/dsh-tool-bash'
|
|
23
|
+
disabled: !!js process.platform === 'win32'
|
|
24
|
+
- id: tool-pwsh
|
|
25
|
+
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
26
|
+
disabled: !!js process.platform !== 'win32'
|
|
27
|
+
- id: tool-fs
|
|
28
|
+
name: '@deepseek-ai/dsh-tool-fs'
|
|
29
|
+
- id: tool-fs-search
|
|
30
|
+
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
31
|
+
config:
|
|
32
|
+
sampleOverCapGlobResults: false
|
|
33
|
+
- id: tool-jobs
|
|
34
|
+
name: '@deepseek-ai/dsh-tool-jobs'
|
|
35
|
+
- id: tool-schedule
|
|
36
|
+
name: '@deepseek-ai/dsh-tool-schedule'
|
|
37
|
+
- id: skill-filesystem
|
|
38
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
39
|
+
- id: tool-skill
|
|
40
|
+
name: '@deepseek-ai/dsh-tool-skill'
|
|
41
|
+
- id: command-goal
|
|
42
|
+
name: '@deepseek-ai/dsh-command-goal'
|
|
43
|
+
- id: tool-goal
|
|
44
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
45
|
+
- id: planning
|
|
46
|
+
name: cordis:group
|
|
47
|
+
group: true
|
|
48
|
+
isolate:
|
|
49
|
+
planMode: true
|
|
50
|
+
config:
|
|
51
|
+
- id: plan-mode
|
|
52
|
+
name: '@deepseek-ai/dsh-plan-mode'
|
|
53
|
+
config:
|
|
54
|
+
section: |
|
|
55
|
+
You are in plan mode. Stay in plan mode until the user approves your plan through exit_plan_mode or switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
56
|
+
|
|
57
|
+
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
58
|
+
|
|
59
|
+
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
60
|
+
|
|
61
|
+
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
62
|
+
|
|
63
|
+
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
64
|
+
|
|
65
|
+
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
66
|
+
- id: compaction
|
|
67
|
+
name: cordis:group
|
|
68
|
+
group: true
|
|
69
|
+
isolate:
|
|
70
|
+
compaction: true
|
|
71
|
+
toolResultPruner: true
|
|
72
|
+
config:
|
|
73
|
+
- id: compaction-basic
|
|
74
|
+
name: '@deepseek-ai/dsh-compaction-basic'
|
|
75
|
+
- id: command-compact
|
|
76
|
+
name: '@deepseek-ai/dsh-command-compact'
|
|
77
|
+
- id: tool-result-pruner
|
|
78
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
79
|
+
config:
|
|
80
|
+
thresholdChars: 8192
|
|
81
|
+
headChars: 4096
|
|
82
|
+
tailChars: 1024
|
|
83
|
+
- id: delegation
|
|
84
|
+
name: cordis:group
|
|
85
|
+
group: true
|
|
86
|
+
isolate:
|
|
87
|
+
workflowEngine: true
|
|
88
|
+
config:
|
|
89
|
+
- id: tool-subagent-control
|
|
90
|
+
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
91
|
+
- id: tool-subagent-list-agents
|
|
92
|
+
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
93
|
+
- id: tool-subagent
|
|
94
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
95
|
+
config:
|
|
96
|
+
provider: spawn
|
|
97
|
+
toolName: subagent
|
|
98
|
+
modelSelectionSettings: true
|
|
99
|
+
- id: tool-subagent-fork
|
|
100
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
101
|
+
config:
|
|
102
|
+
provider: fork
|
|
103
|
+
toolName: subagent_fork
|
|
104
|
+
- id: workflow-ptc
|
|
105
|
+
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
106
|
+
config:
|
|
107
|
+
provider: spawn
|
|
108
|
+
- id: tool-workflow
|
|
109
|
+
name: '@deepseek-ai/dsh-tool-workflow'
|
|
110
|
+
- id: tool-ask-user
|
|
111
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
112
|
+
- id: tool-todo
|
|
113
|
+
name: '@deepseek-ai/dsh-tool-todo'
|
|
114
|
+
config:
|
|
115
|
+
allowParallelInProgress: true
|
|
116
|
+
- id: tool-web
|
|
117
|
+
name: '@deepseek-ai/dsh-tool-web'
|
|
118
|
+
config:
|
|
119
|
+
fetch: true
|
|
120
|
+
searchTimeoutMs: 60000
|
|
121
|
+
- id: present
|
|
122
|
+
name: '@deepseek-ai/dsh-tool-present'
|
|
123
|
+
- id: tool-plugin-manager
|
|
124
|
+
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
125
|
+
disabled: true
|
|
126
|
+
- id: tool-plugin-market
|
|
127
|
+
name: '@deepseek-ai/dsh-plugin-market/tools'
|
|
128
|
+
disabled: true
|
package/src/launcher.js
CHANGED
|
@@ -6,9 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
import { dirname, isAbsolute, join, resolve } from 'node:path'
|
|
8
8
|
|
|
9
|
-
const DEFAULT_SOURCE_URL = 'https://github.com/
|
|
10
|
-
const DEFAULT_SOURCE_REF = '
|
|
11
|
-
|
|
9
|
+
const DEFAULT_SOURCE_URL = 'https://github.com/deepseek-ai/deepseek-harness.git'
|
|
10
|
+
const DEFAULT_SOURCE_REF = 'dsh-v0.2.1-rc.2'
|
|
11
|
+
// Bounds a hung probe; a cold first start of `dsh --version` can exceed 3s.
|
|
12
|
+
const PROBE_TIMEOUT_MS = 10_000
|
|
13
|
+
/** dsh release the bundle patch and dist were composed against. */
|
|
14
|
+
export const COMPATIBLE_DSH_VERSION = '0.2.1-rc.2'
|
|
15
|
+
/** Engine release line (major.minor) whose plugin APIs the bundle targets. */
|
|
16
|
+
const COMPATIBLE_DSH_LINE = COMPATIBLE_DSH_VERSION.split('.').slice(0, 2).join('.')
|
|
12
17
|
|
|
13
18
|
/** @typedef {'missing' | 'directory' | 'file' | 'symlink' | 'other'} PathKind */
|
|
14
19
|
|
|
@@ -37,11 +42,10 @@ const PROBE_TIMEOUT_MS = 3000
|
|
|
37
42
|
* @property {(path: string) => PathKind} inspectPath
|
|
38
43
|
* @property {(path: string) => void} makeDirectory
|
|
39
44
|
* @property {(path: string) => string} readText
|
|
40
|
-
* @property {(path: string, content: string) => void} [writeText]
|
|
41
45
|
* @property {string} [nodeExecutable] Node executable for package bin entries without an executable bit.
|
|
42
46
|
* @property {string} [currentDirectory] Working directory for local package resolution.
|
|
43
47
|
* @property {(directory: string) => string | undefined} [resolveDshPackage] Resolve an installed @deepseek-ai/dsh package.json.
|
|
44
|
-
* @property {(command: string, args: string[], options?: {cwd?: string, stdio?: 'inherit', capture?: boolean, timeoutMs?: number}) => CommandResult} run
|
|
48
|
+
* @property {(command: string, args: string[], options?: {cwd?: string, stdio?: 'inherit' | 'output', capture?: boolean, timeoutMs?: number}) => CommandResult} run
|
|
45
49
|
* @property {(text: string) => void} writeOut
|
|
46
50
|
* @property {(text: string) => void} writeError
|
|
47
51
|
*/
|
|
@@ -49,7 +53,7 @@ const PROBE_TIMEOUT_MS = 3000
|
|
|
49
53
|
/**
|
|
50
54
|
* Parse the launcher's reserved management commands and flags.
|
|
51
55
|
* @param {readonly string[]} argv - arguments after the executable.
|
|
52
|
-
* @returns {{kind: 'version'} | {kind: 'update'} | {kind: 'probe'} | {kind: 'launch', args: string[], mode?: 'auto' | 'lightweight' | 'source'}}
|
|
56
|
+
* @returns {{kind: 'version'} | {kind: 'update'} | {kind: 'probe'} | {kind: 'launch', args: string[], mode?: 'auto' | 'lightweight' | 'source', profile?: string}}
|
|
53
57
|
*/
|
|
54
58
|
export function parseLauncherInvocation(argv) {
|
|
55
59
|
if (argv[0] === '--') return { kind: 'launch', args: [...argv.slice(1)] }
|
|
@@ -66,10 +70,18 @@ export function parseLauncherInvocation(argv) {
|
|
|
66
70
|
return { kind: 'probe' }
|
|
67
71
|
}
|
|
68
72
|
let mode = undefined
|
|
73
|
+
let profile = undefined
|
|
69
74
|
const args = []
|
|
70
75
|
for (let i = 0; i < argv.length; i++) {
|
|
71
76
|
const item = argv[i]
|
|
72
|
-
if (item === '--
|
|
77
|
+
if (item === '--') {
|
|
78
|
+
args.push(...argv.slice(i + 1))
|
|
79
|
+
break
|
|
80
|
+
} else if (item === '--profile' || item.startsWith('--profile=')) {
|
|
81
|
+
if (profile !== undefined) throw new Error('--profile may only be specified once')
|
|
82
|
+
profile = item === '--profile' ? argv[++i] : item.slice('--profile='.length)
|
|
83
|
+
if (!profile?.trim() || profile.startsWith('-')) throw new Error('--profile needs a name')
|
|
84
|
+
} else if (item === '--source') {
|
|
73
85
|
mode = 'source'
|
|
74
86
|
} else if (item === '--lightweight') {
|
|
75
87
|
mode = 'lightweight'
|
|
@@ -77,7 +89,7 @@ export function parseLauncherInvocation(argv) {
|
|
|
77
89
|
args.push(item)
|
|
78
90
|
}
|
|
79
91
|
}
|
|
80
|
-
return
|
|
92
|
+
return { kind: 'launch', args, ...(mode === undefined ? {} : { mode }), ...(profile === undefined ? {} : { profile }) }
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
/**
|
|
@@ -180,49 +192,63 @@ export function probeInstalledDsh({ env = {}, settings, adapters }) {
|
|
|
180
192
|
}
|
|
181
193
|
}
|
|
182
194
|
|
|
195
|
+
/** npm name this package installs under; the tui profile lists it as a bundle. */
|
|
196
|
+
export const BUNDLE_PACKAGE = '@crazyhappyone/dsh-tui'
|
|
197
|
+
/** Both runtime modes must read the same official profile and model settings. */
|
|
198
|
+
const TUI_PROFILE = 'deepseek-tui'
|
|
199
|
+
|
|
183
200
|
/**
|
|
184
|
-
* Locate the
|
|
201
|
+
* Locate this package's bundle root: the directory whose cordis.patch.yml the
|
|
202
|
+
* package manifest declares as `dsh.bundle.patch`.
|
|
185
203
|
* @param {string | undefined} packageRoot
|
|
186
204
|
* @param {LauncherAdapters} adapters
|
|
187
|
-
* @returns {string | undefined}
|
|
205
|
+
* @returns {string | undefined} the package root, or undefined without a bundle patch.
|
|
188
206
|
*/
|
|
189
|
-
export function
|
|
207
|
+
export function findBundleRoot(packageRoot, adapters) {
|
|
190
208
|
if (!packageRoot || typeof packageRoot !== 'string') return undefined
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
]
|
|
195
|
-
for (const candidate of candidates) {
|
|
196
|
-
if (adapters.inspectPath(candidate) === 'file') return candidate
|
|
197
|
-
}
|
|
198
|
-
return undefined
|
|
209
|
+
// pnpm records `link:` targets without a trailing separator.
|
|
210
|
+
const root = resolve(packageRoot)
|
|
211
|
+
return adapters.inspectPath(join(root, 'cordis.patch.yml')) === 'file' ? root : undefined
|
|
199
212
|
}
|
|
200
213
|
|
|
201
214
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* @
|
|
215
|
+
* Whether the tui profile already lists this package root as a linked bundle.
|
|
216
|
+
* @param {{bundleRoot: string, settings: LauncherSettings, env?: Readonly<Record<string, string | undefined>>, adapters: LauncherAdapters, profile?: string}} input
|
|
217
|
+
* @returns {boolean}
|
|
205
218
|
*/
|
|
206
|
-
export function
|
|
207
|
-
const dshHome = env['DSH_HOME'] || (homeDirectory ? join(homeDirectory, '.dsh') : undefined)
|
|
208
|
-
if (!dshHome) return
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
dsh: {
|
|
217
|
-
profile: {
|
|
218
|
-
bundles: ['@deepseek-ai/dsh-base'],
|
|
219
|
-
patchReload: 'startup',
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
}
|
|
223
|
-
if (typeof adapters.writeText === 'function') {
|
|
224
|
-
adapters.writeText(manifestPath, JSON.stringify(manifest, undefined, 2) + '\n')
|
|
219
|
+
export function tuiBundleInstalled({ bundleRoot, settings, env = {}, adapters, profile = TUI_PROFILE }) {
|
|
220
|
+
const dshHome = env['DSH_HOME'] || (settings.homeDirectory ? join(settings.homeDirectory, '.dsh') : undefined)
|
|
221
|
+
if (!dshHome) return false
|
|
222
|
+
const manifestPath = join(dshHome, 'profiles', profile, 'package.json')
|
|
223
|
+
if (adapters.inspectPath(manifestPath) !== 'file') return false
|
|
224
|
+
let manifest
|
|
225
|
+
try {
|
|
226
|
+
manifest = JSON.parse(adapters.readText(manifestPath))
|
|
227
|
+
} catch {
|
|
228
|
+
return false
|
|
225
229
|
}
|
|
230
|
+
return manifest?.dependencies?.[BUNDLE_PACKAGE] === `link:${bundleRoot}`
|
|
231
|
+
&& Array.isArray(manifest?.dsh?.profile?.bundles)
|
|
232
|
+
&& manifest.dsh.profile.bundles.includes(BUNDLE_PACKAGE)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Install this package as a bundle of the tui profile through dsh's own
|
|
237
|
+
* plugin manager, which links it, appends it after @deepseek-ai/dsh-base, and
|
|
238
|
+
* keeps the profile's user patch layer after it so settings edits apply.
|
|
239
|
+
* @param {{probe: {binPath: string, command?: string, args?: string[], cwd?: string}, bundleRoot: string, settings: LauncherSettings, env?: Readonly<Record<string, string | undefined>>, adapters: LauncherAdapters, profile?: string}} input
|
|
240
|
+
* @returns {{ok: true} | {ok: false, reason: string}}
|
|
241
|
+
*/
|
|
242
|
+
export function ensureTuiBundle({ probe, bundleRoot, settings, env = {}, adapters, profile = TUI_PROFILE }) {
|
|
243
|
+
if (tuiBundleInstalled({ bundleRoot, settings, env, adapters, profile })) return { ok: true }
|
|
244
|
+
const result = adapters.run(
|
|
245
|
+
probe.command ?? probe.binPath,
|
|
246
|
+
[...(probe.args ?? []), 'plugin', '--profile', profile, 'add', bundleRoot],
|
|
247
|
+
// The install reads nothing; input typed meanwhile stays queued for the TUI.
|
|
248
|
+
{ ...(probe.cwd === undefined ? {} : { cwd: probe.cwd }), stdio: 'output' },
|
|
249
|
+
)
|
|
250
|
+
if (result.status === 0) return { ok: true }
|
|
251
|
+
return { ok: false, reason: `\`dsh plugin --profile ${profile} add ${bundleRoot}\` failed with exit code ${result.status}` }
|
|
226
252
|
}
|
|
227
253
|
|
|
228
254
|
/**
|
|
@@ -239,7 +265,7 @@ export function runLauncher({ invocation, settings, packageVersion, adapters, en
|
|
|
239
265
|
case 'probe':
|
|
240
266
|
return showProbe({ settings, packageVersion, adapters, env })
|
|
241
267
|
case 'launch':
|
|
242
|
-
return launchTui({ args: invocation.args, mode: invocation.mode, settings, adapters, env })
|
|
268
|
+
return launchTui({ args: invocation.args, mode: invocation.mode, profile: invocation.profile, settings, adapters, env })
|
|
243
269
|
default:
|
|
244
270
|
return assertNever(invocation)
|
|
245
271
|
}
|
|
@@ -264,69 +290,116 @@ function runtimeState(settings, adapters) {
|
|
|
264
290
|
return { ok: true, initialized: true }
|
|
265
291
|
}
|
|
266
292
|
|
|
267
|
-
|
|
293
|
+
/**
|
|
294
|
+
* Check a probed engine version against the bundle's release line. A different
|
|
295
|
+
* major.minor line changes plugin APIs the bundle calls (0.2 reworked jobs and
|
|
296
|
+
* llm-deepseek config), so launching would crash after the TUI takes the screen.
|
|
297
|
+
* @param {string} version - `dsh --version` output.
|
|
298
|
+
* @param {Readonly<Record<string, string | undefined>>} env - launcher environment.
|
|
299
|
+
* @returns {{ok: true} | {ok: false, reason: string}}
|
|
300
|
+
*/
|
|
301
|
+
export function engineCompatibility(version, env = {}) {
|
|
302
|
+
const match = /^(\d+)\.(\d+)\./u.exec(version.trim())
|
|
303
|
+
if (!match || env['DSH_TUI_SKIP_ENGINE_CHECK'] === '1') return { ok: true }
|
|
304
|
+
if (`${match[1]}.${match[2]}` === COMPATIBLE_DSH_LINE) return { ok: true }
|
|
305
|
+
return {
|
|
306
|
+
ok: false,
|
|
307
|
+
reason: `installed dsh ${version.trim()} is not supported by this dsh-tui, which is built for @deepseek-ai/dsh ${COMPATIBLE_DSH_VERSION}. `
|
|
308
|
+
+ `Install the matching engine with \`npm install --global @deepseek-ai/dsh@${COMPATIBLE_DSH_VERSION}\` `
|
|
309
|
+
+ '(or set DSH_TUI_SKIP_ENGINE_CHECK=1 to launch anyway)',
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function launchTui({ args, mode, profile = TUI_PROFILE, settings, adapters, env = {} }) {
|
|
268
314
|
const effectiveMode = mode ?? settings.mode ?? 'auto'
|
|
269
315
|
|
|
270
316
|
if (effectiveMode === 'source') {
|
|
271
|
-
return launchSourceTui({ args, settings, adapters })
|
|
317
|
+
return launchSourceTui({ args, profile, settings, adapters, env })
|
|
272
318
|
}
|
|
273
319
|
|
|
274
|
-
const
|
|
320
|
+
const bundleRoot = findBundleRoot(settings.packageRoot, adapters)
|
|
275
321
|
|
|
276
|
-
if (
|
|
322
|
+
if (bundleRoot !== undefined) {
|
|
277
323
|
const probe = probeInstalledDsh({ env, settings, adapters })
|
|
278
324
|
if (probe.ok) {
|
|
279
|
-
|
|
325
|
+
const compatibility = engineCompatibility(probe.version, env)
|
|
326
|
+
if (!compatibility.ok) return reportError(adapters, compatibility.reason)
|
|
327
|
+
return launchLightweightTui({ args, profile, probe, bundleRoot, adapters, env, settings })
|
|
280
328
|
}
|
|
281
329
|
const explicit = settings.dshBin ?? env['DSH_BIN']
|
|
282
330
|
if (effectiveMode === 'lightweight' || explicit?.trim()) {
|
|
283
|
-
return reportError(adapters, `lightweight mode requires installed dsh (${probe.reason}); run \`npm i -g @deepseek-ai/dsh\` or use \`dsh-tui --source\``)
|
|
331
|
+
return reportError(adapters, `lightweight mode requires installed dsh (${probe.reason}); run \`npm i -g @deepseek-ai/dsh@${COMPATIBLE_DSH_VERSION}\` or use \`dsh-tui --source\``)
|
|
284
332
|
}
|
|
285
333
|
} else if (effectiveMode === 'lightweight') {
|
|
286
|
-
return reportError(adapters, `
|
|
334
|
+
return reportError(adapters, `bundle patch not found under ${settings.packageRoot}; run \`dsh-tui update\` to initialize source runtime`)
|
|
287
335
|
}
|
|
288
336
|
|
|
289
337
|
// Fallback to source runtime if initialized
|
|
290
338
|
const state = runtimeState(settings, adapters)
|
|
291
339
|
if (state.ok && state.initialized) {
|
|
292
|
-
return launchSourceTui({ args, settings, adapters })
|
|
340
|
+
return launchSourceTui({ args, profile, settings, adapters, env })
|
|
293
341
|
}
|
|
294
342
|
|
|
295
343
|
if (state.ok && !state.initialized) {
|
|
296
344
|
adapters.writeError('dsh-tui: 未检测到系统已安装的 dsh 核心引擎。\n')
|
|
297
345
|
adapters.writeError('dsh-tui: 推荐通过 npm 极速安装官方运行时(无需克隆完整源码仓库,仅需数十 MB):\n')
|
|
298
|
-
adapters.writeError(
|
|
346
|
+
adapters.writeError(`dsh-tui: npm install --global @deepseek-ai/dsh@${COMPATIBLE_DSH_VERSION}\n\n`)
|
|
299
347
|
adapters.writeError('dsh-tui: 若您需要进行底层源码开发与测试,可运行:\n')
|
|
300
348
|
adapters.writeError('dsh-tui: dsh-tui update\n')
|
|
301
349
|
adapters.writeError('dsh-tui: dsh-tui --source\n')
|
|
302
350
|
return reportError(adapters, `runtime is not initialized at ${settings.runtimeDirectory}; run \`dsh-tui update\` first`)
|
|
303
351
|
}
|
|
304
352
|
|
|
305
|
-
return launchSourceTui({ args, settings, adapters })
|
|
353
|
+
return launchSourceTui({ args, profile, settings, adapters, env })
|
|
306
354
|
}
|
|
307
355
|
|
|
308
|
-
function launchLightweightTui({ args, probe,
|
|
309
|
-
|
|
356
|
+
function launchLightweightTui({ args, profile, probe, bundleRoot, adapters, env = {}, settings }) {
|
|
357
|
+
const installed = ensureTuiBundle({ probe, bundleRoot, settings, env, adapters, profile })
|
|
358
|
+
if (!installed.ok) return reportError(adapters, installed.reason)
|
|
310
359
|
const result = adapters.run(
|
|
311
360
|
probe.command ?? probe.binPath,
|
|
312
|
-
[...(probe.args ?? []), '--profile',
|
|
361
|
+
[...(probe.args ?? []), '--profile', profile, ...args],
|
|
313
362
|
{ stdio: 'inherit' },
|
|
314
363
|
)
|
|
315
364
|
return result.status
|
|
316
365
|
}
|
|
317
366
|
|
|
318
|
-
function launchSourceTui({ args, settings, adapters }) {
|
|
367
|
+
function launchSourceTui({ args, profile, settings, adapters, env = {} }) {
|
|
319
368
|
const state = runtimeState(settings, adapters)
|
|
320
369
|
if (!state.ok) return reportError(adapters, state.message)
|
|
321
370
|
if (!state.initialized) {
|
|
322
371
|
return reportError(adapters, `runtime is not initialized at ${settings.runtimeDirectory}; run \`dsh-tui update\` first`)
|
|
323
372
|
}
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
373
|
+
const bundleRoot = findBundleRoot(settings.packageRoot, adapters)
|
|
374
|
+
// Preserve the library's legacy fork-only entry when no packaged bundle exists.
|
|
375
|
+
if (bundleRoot === undefined) {
|
|
376
|
+
return adapters.run(settings.packageManager, ['dsh', '--profile', profile, ...args], {
|
|
377
|
+
cwd: settings.runtimeDirectory, stdio: 'inherit',
|
|
378
|
+
}).status
|
|
379
|
+
}
|
|
380
|
+
const compatibility = engineCompatibility(readDshVersion(settings.runtimeDirectory, adapters), env)
|
|
381
|
+
if (!compatibility.ok) return reportError(adapters, compatibility.reason)
|
|
382
|
+
const command = adapters.nodeExecutable ?? 'node'
|
|
383
|
+
const cwd = settings.runtimeDirectory
|
|
384
|
+
// Source aliases do not cover generated package exports. Upstream's generator
|
|
385
|
+
// emits these contracts without requiring a complete Web/Desktop build.
|
|
386
|
+
if (adapters.inspectPath(join(cwd, 'packages/api/remote-access/lib/typert.host.js')) !== 'file') {
|
|
387
|
+
if (adapters.inspectPath(join(cwd, 'scripts/generate-typert.ts')) !== 'file') {
|
|
388
|
+
return reportError(adapters, 'source runtime is missing generated contracts and scripts/generate-typert.ts; update the dsh checkout')
|
|
389
|
+
}
|
|
390
|
+
const generated = adapters.run(command, ['--import', 'tsx/esm', 'scripts/generate-typert.ts'], { cwd, stdio: 'output' })
|
|
391
|
+
if (generated.status !== 0) return reportCommandFailure(adapters, 'generate source runtime contracts', generated)
|
|
392
|
+
}
|
|
393
|
+
// pnpm exec preserves the checkout's pinned package-manager context for the
|
|
394
|
+
// profile plugin manager, without running the `predsh` installation hook.
|
|
395
|
+
const probe = {
|
|
396
|
+
command: settings.packageManager, binPath: settings.packageManager,
|
|
397
|
+
args: ['exec', command, '--import', 'tsx/esm', 'apps/cli/src/bin.ts'], cwd,
|
|
398
|
+
}
|
|
399
|
+
const installed = ensureTuiBundle({ probe, bundleRoot, settings, env, adapters, profile })
|
|
400
|
+
if (!installed.ok) return reportError(adapters, installed.reason)
|
|
401
|
+
// Avoid `pnpm dsh`: its pre-script reinstalls the whole workspace on every boot.
|
|
402
|
+
return adapters.run(probe.command, [...probe.args, '--profile', profile, ...args], { cwd, stdio: 'inherit' }).status
|
|
330
403
|
}
|
|
331
404
|
|
|
332
405
|
function showProbe({ settings, packageVersion, adapters, env = {} }) {
|
|
@@ -338,8 +411,17 @@ function showProbe({ settings, packageVersion, adapters, env = {} }) {
|
|
|
338
411
|
adapters.writeOut(`dsh: installed\n`)
|
|
339
412
|
adapters.writeOut(`executable: ${probe.binPath}\n`)
|
|
340
413
|
adapters.writeOut(`dsh engine version: ${probe.version}\n`)
|
|
341
|
-
const
|
|
342
|
-
adapters.writeOut(
|
|
414
|
+
const compatibility = engineCompatibility(probe.version, env)
|
|
415
|
+
adapters.writeOut(compatibility.ok
|
|
416
|
+
? `engine compatibility: ok (built for ${COMPATIBLE_DSH_VERSION})\n`
|
|
417
|
+
: `engine compatibility: ${compatibility.reason}\n`)
|
|
418
|
+
const bundleRoot = findBundleRoot(settings.packageRoot, adapters)
|
|
419
|
+
let bundle = 'missing'
|
|
420
|
+
if (bundleRoot !== undefined) {
|
|
421
|
+
const state = tuiBundleInstalled({ bundleRoot, settings, env, adapters }) ? 'installed in' : 'not yet installed in'
|
|
422
|
+
bundle = `${bundleRoot} (${state} profile ${TUI_PROFILE})`
|
|
423
|
+
}
|
|
424
|
+
adapters.writeOut(`bundle: ${bundle}\n`)
|
|
343
425
|
return 0
|
|
344
426
|
}
|
|
345
427
|
adapters.writeOut(`dsh: not installed (${probe.reason})\n`)
|
package/src/release-patch.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/** Release patch projection from the matching Harness TUI composition. */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Point the three bundled plugins at release artifacts, retaining all other rows.
|
|
5
|
-
* @param {string} source - Harness packages/tui/tui/cordis.patch.yml content.
|
|
6
|
-
* @param {string} distDirectory - Relative dist directory from the output patch.
|
|
7
|
-
* @returns {string} Patch text suitable for dsh --patch.
|
|
8
|
-
*/
|
|
9
|
-
export function releasePatch(source, distDirectory) {
|
|
10
|
-
const entries = new Map([
|
|
11
|
-
['@deepseek-ai/dsh-tui/startup', 'startup.js'],
|
|
12
|
-
['./zen-proxy/index.js', 'zen-proxy.js'],
|
|
13
|
-
['@deepseek-ai/dsh-tui', 'index.js'],
|
|
14
|
-
])
|
|
15
|
-
const patch = source.replace(/^(\s+name: )(["'])([^"']+)\2$/gmu, (line, prefix, quote, name) => {
|
|
16
|
-
const file = entries.get(name)
|
|
17
|
-
if (!file) return line
|
|
18
|
-
entries.delete(name)
|
|
19
|
-
return `${prefix}${quote}${distDirectory}/${file}${quote}`
|
|
20
|
-
})
|
|
21
|
-
if (entries.size) throw new Error(`TUI patch is missing plugin entries: ${[...entries.keys()].join(', ')}`)
|
|
22
|
-
return patch
|
|
23
|
-
}
|