Merge pull request #72 from NolanHo/feat/dev

fix: 终端 Tab 使用账户 login shell 并以 login 模式启动
This commit is contained in:
Atlas Zeng
2026-08-15 15:53:26 +08:00
committed by GitHub
4 changed files with 87 additions and 11 deletions
+2 -2
View File
@@ -13,7 +13,7 @@
*/
import { randomUUID } from 'node:crypto'
import * as nodePty from 'node-pty'
import { ensureSpawnHelper } from './pty-manager.ts'
import { ensureSpawnHelper, shellSpawnArgs } from './pty-manager.ts'
import { SidebarError } from './wire.ts'
/** Per-agent-terminal transcript bound (bytes kept for replay and reads). */
@@ -211,7 +211,7 @@ export class AgentPtyRegistry {
): string {
const uuid = randomUUID()
const dims = clampDims(cols, rows)
const pty = nodePty.spawn(this.shell, [], {
const pty = nodePty.spawn(this.shell, shellSpawnArgs(), {
name: 'xterm-256color',
cols: dims.cols,
rows: dims.rows,
+31 -4
View File
@@ -9,6 +9,7 @@
import { chmodSync, existsSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { createRequire } from 'node:module'
import { userInfo } from 'node:os'
import * as nodePty from 'node-pty'
import { SidebarError } from './wire.ts'
@@ -118,7 +119,7 @@ export class PtyManager {
sessionId,
tabId,
cwd,
pty: nodePty.spawn(this.shell, [], {
pty: nodePty.spawn(this.shell, shellSpawnArgs(), {
name: 'xterm-256color',
cols: Math.max(2, Math.floor(cols)),
rows: Math.max(2, Math.floor(rows)),
@@ -191,9 +192,35 @@ export class PtyManager {
}
}
/** The interactive shell for this platform (empty SHELL falls back). */
/**
* The interactive shell for this platform, resolved like a terminal
* emulator: an explicit `$SHELL` on the dsh process wins (deployment
* override), then the account's login shell from passwd, then `/bin/bash`.
* The passwd step matters because service managers and container inits
* often start dsh without `SHELL`, and the tab should still open the
* user's login shell (e.g. zsh) instead of silently degrading to bash.
* Windows short-circuits to `powershell.exe` before any resolution.
*/
export function defaultShell(): string {
if (process.platform === 'win32') return 'powershell.exe'
const shell = process.env.SHELL
return shell !== undefined && shell.trim() !== '' ? shell : '/bin/bash'
const envShell = process.env.SHELL
if (envShell !== undefined && envShell.trim() !== '') return envShell
// userInfo() throws when the uid has no passwd entry (rare chroots);
// without a login shell there is nothing better than the bash default.
try {
const loginShell = userInfo().shell
if (typeof loginShell === 'string' && loginShell.trim() !== '') return loginShell
} catch {
// no passwd entry: fall through to /bin/bash
}
return '/bin/bash'
}
/**
* Spawn arguments that make the shell behave like a terminal-emulator tab:
* POSIX shells start as login shells (`-l`) so they read the profile files
* (`~/.profile`, `~/.zprofile`); Windows PowerShell takes no login flag.
*/
export function shellSpawnArgs(): string[] {
return process.platform === 'win32' ? [] : ['-l']
}