diff --git a/src/workspaces/plugins/Terminal/server/pty-sidecar.mjs b/src/workspaces/plugins/Terminal/server/pty-sidecar.mjs index ac4fbf7d..8bd21ffc 100644 --- a/src/workspaces/plugins/Terminal/server/pty-sidecar.mjs +++ b/src/workspaces/plugins/Terminal/server/pty-sidecar.mjs @@ -1,10 +1,17 @@ import http from 'node:http'; import { existsSync } from 'node:fs'; import { cp, mkdir } from 'node:fs/promises'; -import { join } from 'node:path'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { WebSocketServer } from 'ws'; import * as pty from 'node-pty'; +const __dirname = dirname(fileURLToPath(import.meta.url)); +const isDocker = existsSync('/opt/terminal-templates/.zshrc'); + +const templateDir = isDocker ? '/opt/terminal-templates' : join(__dirname, 'templates'); +const ohMyZshSource = isDocker ? '/opt/oh-my-zsh' : null; + const port = Number(process.env.TERMINAL_PTY_PORT ?? '5337'); const host = process.env.TERMINAL_PTY_HOST ?? '127.0.0.1'; @@ -26,20 +33,45 @@ const sendJson = (ws, msg) => { const ensureUserFiles = async (homeDir) => { await mkdir(homeDir, { recursive: true }); await mkdir(join(homeDir, '.config'), { recursive: true }); + await mkdir(join(homeDir, '.local', 'bin'), { recursive: true }); const zshrcPath = join(homeDir, '.zshrc'); if (!existsSync(zshrcPath)) { - await cp('/opt/terminal-templates/.zshrc', zshrcPath); + await cp(join(templateDir, '.zshrc'), zshrcPath); } const starshipPath = join(homeDir, '.config', 'starship-officer.toml'); if (!existsSync(starshipPath)) { - await cp('/opt/terminal-templates/starship-officer.toml', starshipPath); + await cp(join(templateDir, 'starship-officer.toml'), starshipPath); } const ohMyZshPath = join(homeDir, '.oh-my-zsh'); if (!existsSync(ohMyZshPath)) { - await cp('/opt/oh-my-zsh', ohMyZshPath, { recursive: true }); + if (ohMyZshSource && existsSync(ohMyZshSource)) { + await cp(ohMyZshSource, ohMyZshPath, { recursive: true }); + } else { + // Clone oh-my-zsh on first run (host mode) + const proc = Bun.spawn({ + cmd: ['git', 'clone', '--depth=1', 'https://github.com/ohmyzsh/ohmyzsh.git', ohMyZshPath], + stdout: 'ignore', + stderr: 'ignore', + }); + await proc.exited; + } + } + + // Install starship on host if missing (not needed in Docker) + if (!isDocker) { + const starshipBin = join(homeDir, '.local', 'bin', 'starship'); + if (!existsSync(starshipBin)) { + const installProc = Bun.spawn({ + cmd: ['sh', '-c', 'curl -sS https://starship.rs/install.sh | sh -s -- -y -b "$HOME/.local/bin"'], + env: { ...process.env, HOME: homeDir }, + stdout: 'ignore', + stderr: 'ignore', + }); + await installProc.exited; + } } }; diff --git a/src/workspaces/plugins/Terminal/server/templates/.zshrc b/src/workspaces/plugins/Terminal/server/templates/.zshrc index 52d5b2fd..fbd0c2c1 100644 --- a/src/workspaces/plugins/Terminal/server/templates/.zshrc +++ b/src/workspaces/plugins/Terminal/server/templates/.zshrc @@ -25,6 +25,17 @@ source $ZSH/oh-my-zsh.sh if [[ -n "$OFFICER_TERMINAL_USER" ]]; then export STARSHIP_CONFIG="$HOME/.config/starship-officer.toml" fi + +# Auto-install starship if not available (host mode) +if ! command -v starship &> /dev/null; then + if [[ ! -x "$HOME/.local/bin/starship" ]]; then + mkdir -p "$HOME/.local/bin" + echo "Installing starship..." + curl -sS https://starship.rs/install.sh | sh -s -- -y -b "$HOME/.local/bin" 2>/dev/null + fi + export PATH="$HOME/.local/bin:$PATH" +fi + eval "$(starship init zsh)" # ============================================================================