feat: terminal startup command and container tools
- Add startup_command field to ToolType model and API - Execute startup command before interactive shell in terminal sessions - Add tmux and ranger to OpenCode container spec - Update Tool Workshop UI with startup_command input for terminal types - Add backend tests for startup_command CRUD operations - Sync specs: tool-terminal, tool-types-definition, opencode-web-server - New spec: tool-terminal-startup-command Quality gates: Frontend typecheck/lint passed. Backend tests blocked by environment (Python/Docker not available). OpenSpec: terminal-startup-and-container-tools
This commit is contained in:
@@ -58,6 +58,7 @@ class TerminalManager:
|
||||
self,
|
||||
instance_id: uuid.UUID,
|
||||
container_id: str,
|
||||
startup_command: str | None = None,
|
||||
) -> TerminalSession:
|
||||
"""Get existing session or create a new one."""
|
||||
# Ensure idle check is running (lazy start)
|
||||
@@ -82,8 +83,8 @@ class TerminalManager:
|
||||
# Create new session
|
||||
logger.info("Creating new terminal session for instance %s", instance_id)
|
||||
session_id = str(uuid.uuid4())
|
||||
session = TerminalSession(session_id, instance_id, container_id)
|
||||
await session.start()
|
||||
session = TerminalSession(session_id, instance_id, container_id, startup_command=startup_command)
|
||||
await session.start(startup_command=startup_command)
|
||||
self._sessions[instance_id_str] = session
|
||||
|
||||
return session
|
||||
@@ -127,6 +128,7 @@ class TerminalManager:
|
||||
self,
|
||||
instance_id: uuid.UUID,
|
||||
container_id: str,
|
||||
startup_command: str | None = None,
|
||||
) -> TerminalSession:
|
||||
"""Reset a session by killing it and creating a new one."""
|
||||
instance_id_str = str(instance_id)
|
||||
@@ -139,8 +141,8 @@ class TerminalManager:
|
||||
|
||||
# Create new session
|
||||
session_id = str(uuid.uuid4())
|
||||
session = TerminalSession(session_id, instance_id, container_id)
|
||||
await session.start()
|
||||
session = TerminalSession(session_id, instance_id, container_id, startup_command=startup_command)
|
||||
await session.start(startup_command=startup_command)
|
||||
self._sessions[instance_id_str] = session
|
||||
|
||||
return session
|
||||
|
||||
@@ -29,10 +29,11 @@ class TerminalSession:
|
||||
# Idle timeout in seconds (30 minutes)
|
||||
IDLE_TIMEOUT = 30 * 60
|
||||
|
||||
def __init__(self, session_id: str, instance_id: uuid.UUID, container_id: str) -> None:
|
||||
def __init__(self, session_id: str, instance_id: uuid.UUID, container_id: str, startup_command: str | None = None) -> None:
|
||||
self.session_id = session_id
|
||||
self.instance_id = instance_id
|
||||
self.container_id = container_id
|
||||
self.startup_command = startup_command
|
||||
self.process: asyncio.subprocess.Process | None = None
|
||||
self._closed = False
|
||||
self._master_fd: int | None = None
|
||||
@@ -52,7 +53,7 @@ class TerminalSession:
|
||||
self._cols = 80
|
||||
self._rows = 24
|
||||
|
||||
async def start(self) -> None:
|
||||
async def start(self, startup_command: str | None = None) -> None:
|
||||
"""Start the docker exec process with a shell using a PTY."""
|
||||
# Create a pseudo-terminal on the host
|
||||
self._master_fd, self._slave_fd = pty.openpty()
|
||||
@@ -61,6 +62,13 @@ class TerminalSession:
|
||||
self._set_terminal_size(self._cols, self._rows)
|
||||
logger.info(f"Starting terminal session {self.session_id} for container {self.container_id} with initial size {self._cols}x{self._rows}")
|
||||
|
||||
# Build the shell command
|
||||
if startup_command:
|
||||
shell_cmd = f'bash -c "{startup_command}" || true; exec bash -il'
|
||||
logger.info(f"Using startup command for session {self.session_id}: {startup_command}")
|
||||
else:
|
||||
shell_cmd = "bash -il"
|
||||
|
||||
# Start docker exec with the slave fd as stdin/stdout/stderr
|
||||
# Using -it because the slave fd IS a TTY
|
||||
self.process = await asyncio.create_subprocess_exec(
|
||||
@@ -71,7 +79,8 @@ class TerminalSession:
|
||||
"TERM=xterm",
|
||||
self.container_id,
|
||||
"bash",
|
||||
"-il",
|
||||
"-c",
|
||||
shell_cmd,
|
||||
stdin=self._slave_fd,
|
||||
stdout=self._slave_fd,
|
||||
stderr=self._slave_fd,
|
||||
|
||||
Reference in New Issue
Block a user