dsh-code 1.0.3 → 1.0.4

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/bin/deepseek.mjs CHANGED
@@ -58,6 +58,12 @@ export function globalDshRoots() {
58
58
  ].filter(Boolean))]
59
59
  }
60
60
 
61
+ /** Convert a file URL to a windows-style path even when resolved on a non-windows host. */
62
+ function fileUrlToWindowsPath(url) {
63
+ const pathname = decodeURIComponent(new URL(url).pathname)
64
+ return pathname.replace(/^\/([A-Za-z]:)/, '$1').replace(/\//g, '\\')
65
+ }
66
+
61
67
  /** Resolve the DSH entrypoint without passing user arguments through a Windows shell. */
62
68
  export function rawDshCommand(args, {
63
69
  platform = process.platform,
@@ -67,7 +73,9 @@ export function rawDshCommand(args, {
67
73
  resolvePackage = packageRequire.resolve,
68
74
  } = {}) {
69
75
  if (platform !== 'win32') return { command: 'dsh', args }
70
- const adjacentEntrypoint = fileURLToPath(new URL('../../@deepseek-ai/dsh/lib/bin.js', moduleUrl))
76
+ // fileURLToPath follows the HOST separator; the win32 target needs a
77
+ // windows-style path regardless of where this resolver itself runs.
78
+ const adjacentEntrypoint = fileUrlToWindowsPath(new URL('../../@deepseek-ai/dsh/lib/bin.js', moduleUrl))
71
79
  if (fileExists(adjacentEntrypoint)) return { command: process.execPath, args: [adjacentEntrypoint, ...args] }
72
80
  for (const root of roots) {
73
81
  try {
@@ -111,7 +119,12 @@ function printDoctor(resolveCommand = rawDshCommand, spawnCommand = spawnSync) {
111
119
  }
112
120
 
113
121
  function launchChild(command, args) {
114
- const child = spawn(command, args, { stdio: 'inherit' })
122
+ // Windows .cmd/.bat shims (npm.cmd) need a shell since Node's
123
+ // CVE-2024-27980 fix rejects spawning them directly with EINVAL. Every
124
+ // launch through here uses fixed, wrapper-owned arguments, so the shell
125
+ // surface adds no injection risk.
126
+ const needsShell = process.platform === 'win32' && /\.(cmd|bat)$/iu.test(command)
127
+ const child = spawn(command, args, { stdio: 'inherit', ...(needsShell ? { shell: true } : {}) })
115
128
  child.once('error', error => {
116
129
  console.error(`dsh-code: command failed: ${error.message}`)
117
130
  process.exitCode = 1
@@ -152,8 +165,9 @@ export function launchOperation(args = process.argv.slice(2)) {
152
165
  return launchChild(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['install', '-g', '@deepseek-ai/dsh', 'dsh-code'])
153
166
  }
154
167
  const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'
168
+ const needsShell = process.platform === 'win32' && /\.(cmd|bat)$/iu.test(npm)
155
169
  for (const name of ['@deepseek-ai/dsh', 'dsh-code']) {
156
- const result = spawnSync(npm, ['view', name, 'version'], { encoding: 'utf8', windowsHide: true })
170
+ const result = spawnSync(npm, ['view', name, 'version'], { encoding: 'utf8', windowsHide: true, ...(needsShell ? { shell: true } : {}) })
157
171
  console.log(`${name}: ${result.status === 0 ? String(result.stdout).trim() : 'version check failed'}`)
158
172
  }
159
173
  console.log('Run `deepseek update --apply` to install the latest versions.')
@@ -166,12 +180,21 @@ export function launchDsh(
166
180
  spawnProcess = spawn,
167
181
  resolveCommand = dshCommand,
168
182
  isProfileReady = profileHasDshCode,
183
+ isInteractiveStdin = () => process.stdin.isTTY === true,
169
184
  ) {
170
185
  if (!isProfileReady()) {
171
186
  console.error('dsh-code: the cli profile does not mount dsh-code yet. Run: dsh plugin --profile cli add dsh-code')
172
187
  process.exitCode = 1
173
188
  return undefined
174
189
  }
190
+ // The interactive TUI is a raw-mode terminal application: with piped or
191
+ // otherwise non-TTY stdin it would die deep inside Ink's raw-mode gate
192
+ // with a cryptic stack. Fail here with one actionable line instead.
193
+ if (!isInteractiveStdin()) {
194
+ console.error('dsh-code: this terminal UI requires an interactive TTY on stdin; run it in a real terminal instead of a pipe')
195
+ process.exitCode = 1
196
+ return undefined
197
+ }
175
198
  const command = resolveCommand(args)
176
199
  if (command === undefined) {
177
200
  console.error('dsh-code: @deepseek-ai/dsh must be installed globally beside dsh-code; run: npm install -g @deepseek-ai/dsh dsh-code')