feat(terminal): POSIX 也预检 shell 可执行文件,统一 shell-not-found 错误码

This commit is contained in:
HuanLinOTO
2026-09-08 21:21:30 +08:00
parent 829ad49ddf
commit 26706bc3e9
3 changed files with 93 additions and 35 deletions
+36 -2
View File
@@ -25,6 +25,7 @@ import {
splitShellArgs,
unquotePath,
} from '../src/pty-manager.ts'
import { SidebarError } from '../src/wire.ts'
describe('pty helpers', () => {
it('prefers an explicit shell, then SHELL, then the account login shell on POSIX', () => {
@@ -114,8 +115,41 @@ describe('pty helpers', () => {
.toThrow('shell executable not found: "missing-shell"')
})
it('keeps POSIX bare shell resolution delegated to execvp', () => {
expect(resolveShellExecutable(' zsh ', { platform: 'linux', env: {}, exists: () => false })).toBe('zsh')
it('POSIX: resolves bare names along PATH and names a missing configured shell', () => {
const options = {
platform: 'linux' as const,
env: { PATH: '/usr/local/bin:/usr/bin' },
exists: (path: string) => path === join('/usr/bin', 'zsh'),
}
expect(resolveShellExecutable('zsh', options)).toBe(join('/usr/bin', 'zsh'))
const thrown = (() => {
try {
resolveShellExecutable('nope', options)
return undefined
} catch (error) {
return error
}
})()
expect(thrown).toBeInstanceOf(SidebarError)
expect((thrown as SidebarError).code).toBe('shell-not-found')
expect((thrown as SidebarError).message).toBe('shell executable not found: "nope"')
expect((thrown as SidebarError).meta?.shell).toBe('nope')
})
it('POSIX: checks an absolute path exists and passes it through verbatim', () => {
expect(resolveShellExecutable('/explicit/zsh', { platform: 'linux', env: {}, exists: () => true }))
.toBe('/explicit/zsh')
expect(() => resolveShellExecutable('/missing/zsh', { platform: 'linux', env: {}, exists: () => false }))
.toThrow('shell executable not found: "/missing/zsh"')
})
it('unquotes the configured shell before resolving (Windows probe included)', () => {
const options = {
platform: 'win32' as const,
env: { PATH: 'C:\\Tools' },
exists: (path: string) => path.replaceAll('\\', '/') === 'C:/Tools/pwsh.exe',
}
expect(resolveShellExecutable('"pwsh.exe"', options).replaceAll('\\', '/')).toBe('C:/Tools/pwsh.exe')
})
it('trims the configured shell and defaults it to auto for old documents', () => {