Commit Graph

406 Commits

Author SHA1 Message Date
Fusion 4b09f611d6 Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev 2026-05-24 20:13:17 +02:00
Fusion 5c998f5bf9 fix: revert all terminal resize fixes that caused layout issues
Reverted terminal.tsx, terminal_session.py, and terminal.py to clean state
from before the resize debugging saga. Removed:
- Debug console.log statements
- Explicit term.resize() calls that broke xterm.js
- position: relative CSS overrides on .xterm
- stty -echo wrapper and asyncio.sleep delay
- Extra requestAnimationFrame refresh calls

Kept:
- Mobile terminal features (special keys, modifiers, font size)
- ResizeObserver for container resize detection
- Basic fit() and WebSocket resize messaging
2026-05-24 20:13:10 +02:00
alex 08bd8bf7f9 fix: config profile form focus loss and path validation messages
- Fix React key stability in env vars, files, and mount file inputs
  to prevent focus loss on every keystroke
- Improve validation error messages to explain Files vs Mounts
- Add helper text in UI clarifying relative vs absolute paths

Fixes focus loss bug and improves UX for path validation errors.
2026-05-24 18:10:51 +00:00
alex 4d0834069b chore: merge migration heads for remove_is_builtin and add_config_profiles 2026-05-24 18:00:49 +00:00
alex 7282b91d99 Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev 2026-05-24 17:58:49 +00:00
alex 9ad11a021c feat: add config profiles
- Add ConfigProfile and ConfigProfileInclude data models with migrations
- Implement profile resolver service with ordered includes and merge rules
- Add profile CRUD API with validation, compatibility, and cycle detection
- Add instance API plumbing for profile selection on create/start/restart
- Add resolved profile preview and default resolution APIs
- Add frontend config profile API client and management UI
- Add launch/restart profile selection UI
- Add backend integration and unit tests (31 passing)

OpenSpec: add-config-profiles
Quality gates: ruff, TypeScript compile, 31 tests passing
2026-05-24 17:58:39 +00:00
Fusion 9f29ac15da fix: improve terminal resize with better stty command and logging 2026-05-24 19:52:00 +02:00
Fusion 3092038e40 debug: add console logging for terminal resize debugging 2026-05-24 19:44:21 +02:00
Fusion 4d0c70de98 fix: hide stty resize command from terminal output using ANSI escapes 2026-05-24 19:17:54 +02:00
Fusion 701bd57293 fix: send stty resize on every resize, not just first time
The _stty_sent guard prevented the container shell from updating its
terminal size after the first resize. This caused visual mismatches
where xterm.js displayed at the new size but the shell still wrapped
output at the old size.

Remove the guard so stty is sent on every resize event.
2026-05-24 19:07:37 +02:00
Fusion 4de312c170 fix: terminal resize propagation and redraw
- Frontend: Add ResizeObserver with dimension tracking for accurate resize detection
- Frontend: Fix cleanup function to properly disconnect ResizeObserver
- Frontend: Use CSS grid for terminal wrapper layout
- Backend: Add duplicate dimension check to avoid unnecessary resizes
- Backend: Ensure stty command is sent correctly to container shell
2026-05-24 17:44:00 +02:00
Fusion f37813a317 revert: remove ResizeObserver and stty-on-every-resize to fix infinite loop
The ResizeObserver detected size changes caused by the stty command
output appearing in the terminal, creating an infinite resize loop:
1. Resize detected -> fit() -> send resize to backend
2. Backend sends stty command through PTY
3. stty text appears in terminal output
4. ResizeObserver detects content height change
5. fit() calculates new rows -> send resize
6. Loop continues forever

Reverted to:
- Window resize event instead of ResizeObserver
- stty command only sent once on first resize

This means the container shell stays at the initial size and won't
dynamically resize when the browser window changes, but prevents
the infinite loop.
2026-05-24 16:54:05 +02:00
Fusion 74033243c9 feat: send stty resize command on every resize, not just first
Previously the stty command was only sent on the first resize. Now it
is sent every time the terminal dimensions change, so resizing the
browser window or rotating the device properly updates the container
shell size. Added a check to skip when dimensions haven't changed.
2026-05-24 16:27:37 +02:00
Fusion 9910fd4445 fix: send stty command to resize container shell on first resize
Docker exec doesn't forward PTY resize to the container process,
so the container bash stays at 80x24 regardless of frontend resize.
Work around this by sending a stty command through the terminal
on first resize to set the correct dimensions inside the container.
2026-05-24 16:25:14 +02:00
Fusion 4a66a4a384 fix: pass instance_id to _write_loop to resolve NameError
The write loop was crashing with 'name instance_id is not defined' when
processing resize messages. This caused the connection to drop with 1006
and the frontend to reconnect in a loop. Fixed by passing instance_id
as a parameter to _write_loop. Also cleaned up debug logging.
2026-05-24 16:18:53 +02:00
Fusion fa20d00d14 debug: add loop exit logging to terminal WebSocket handler 2026-05-24 16:13:44 +02:00
Fusion f59274ae64 revert: remove explicit WebSocket close that caused immediate disconnection 2026-05-24 16:12:21 +02:00
Fusion b89fb608b6 fix: explicitly close WebSocket with code 1000 when loops end
When any of the read/write/heartbeat loops ends, we were cancelling
remaining tasks but not explicitly closing the WebSocket. This caused
the connection to be dropped with 1006 abnormal closure instead of
a clean 1000 close. The frontend then reconnected, creating a loop.
2026-05-24 16:08:51 +02:00
Fusion 116cd22ff8 revert: remove stty resize workaround that caused 1006 loops 2026-05-24 16:06:00 +02:00
Fusion 0094ba01cd fix: send stty command to resize container shell
Docker exec doesn't forward PTY resize to the container process,
so the container bash stays at 80x24 regardless of frontend resize.
Work around this by sending a stty command through the terminal
on first resize to set the correct dimensions inside the container.
2026-05-24 16:04:00 +02:00
Fusion f6fb984ec6 revert: remove SIGWINCH signal that caused connection loops
Sending SIGWINCH to the docker exec process was crashing/killing it,
which closed the PTY and caused WebSocket 1006 abnormal closure loops.
Reverting to the original TIOCSWINSZ-only approach.
2026-05-24 16:00:25 +02:00
Fusion 0dba13a354 fix: send SIGWINCH to docker exec after PTY resize
When resizing the PTY, docker exec needs to be notified so it can
re-read the terminal size and propagate it to the container's PTY.
Without this, the container shell stays at 80x24 regardless of what
the frontend sends.
2026-05-24 15:57:29 +02:00
Fusion 268651bab0 Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev 2026-05-24 14:48:45 +02:00
Fusion 7389344b6d fix: defer terminal manager idle check until event loop is running
TerminalManager was trying to create an asyncio task at module import time,
but no event loop exists yet during import. This caused RuntimeError on startup.

Changes:
- _start_idle_check() now checks if event loop is running before creating task
- If no loop exists, silently skips (will be started lazily)
- Added lazy start call in get_or_create_session() when websocket connects
2026-05-24 14:48:30 +02:00
alex 073013bc61 feat: add heartbeat/ping to terminal WebSocket
- Backend: Send ping every 30s from WebSocket endpoint
- Frontend: Respond to pings with pongs, detect missed pings (60s timeout)
- Update type definitions to include 'resetting' status

Refs: persistent-terminal-sessions task 6.4
2026-05-24 12:45:50 +00:00
alex d1c187ab16 feat: implement persistent terminal sessions
- Terminal sessions now persist across WebSocket disconnections
- Added circular output buffer (10KB) for replay on reconnect
- Added idle timeout cleanup (30 minutes)
- Added reset functionality via WebSocket message and HTTP endpoint
- Concurrent connections close old WebSocket when new one connects
- Frontend: Added reset button with confirmation dialog
- Frontend: Handle resetting status and reconnection

Refs: persistent-terminal-sessions
2026-05-24 12:35:52 +00:00
alex dfbd3e60a8 Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev 2026-05-24 11:14:06 +00:00
alex 075eb6a76b fix: ensure cloned repo is mounted in compose for clone mode
- Add safety check after rendering compose template to ensure REPO_PATH is mounted
- If compose template lacks volume mount, auto-add default mount to /workspace
- Add cloned repo verification to catch empty clone directories

Refs: clone mode repo not appearing in container workspace
2026-05-24 11:13:53 +00:00
Fusion ec45283257 Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev 2026-05-24 13:02:24 +02:00
Fusion cce3fa773a feat: hide tunnel UI for terminal sessions and show session metadata
Backend:
- Add created_at to get_user_sessions response

Frontend:
- Hide tunnel error badges, probe output, and 'Recreate Tunnel' button for terminal-only sessions
- Show session start time (created_at) in active sessions list
- Show repository configuration (clone_mode, branch) for each session
- Skip health check polling for terminal-only sessions
- Update Session type to include created_at field
2026-05-24 13:02:06 +02:00
alex 9c7043bab1 fix: add merge migration for alembic heads and make remove_is_builtin idempotent
- Create merge migration f3d2dc90ba3a to merge single_interface and clone_mode heads
- Make remove_is_builtin migration idempotent with IF EXISTS clause

Refs: alembic migration fix for dev branch
2026-05-24 11:00:03 +00:00
alex 802d8f1e8c fix: strip remote prefix from branch names in list_branches
Git branch -a --format=%(refname:short) returns remote branches as
'origin/branch-name', not 'remotes/origin/branch-name'. The code was
only filtering 'remotes/' prefix, causing clone to fail with branch
names like 'origin/feat/foo'.

Now properly detects remote names using 'git remote' and strips the
remote prefix (e.g., 'origin/') from branch names.
2026-05-24 10:25:01 +00:00
alex 5cec4a7a6f Merge branch 'feat/session-branch-selection' into dev
Resolved conflicts:
- Moved branch selection UI from inline sessions.tsx to CreateSessionForm component
- Integrated branch dropdown and new branch creation into CreateSessionForm
- Removed duplicate branch state management from sessions.tsx

All branch selection tests pass (7/7).
2026-05-24 10:06:14 +00:00
alex 014b88ee56 test: add unit tests for session branch selection
- Test CreateInstanceRequest model with new_branch field
- Test local branch creation via git checkout -b
- Test instance branch storage logic
2026-05-24 09:31:09 +00:00
alex 3672312028 feat: support creating local branch during session creation
- Add new_branch field to CreateInstanceRequest
- Run git checkout -b after cloning when new_branch is provided
- Store new branch name in ToolInstance record
2026-05-24 09:21:29 +00:00
Fusion 312a646b89 feat: remove built-in tool types distinction
- Drop is_builtin column from tool_types table
- Remove built-in tool seeding from startup
- Remove is_builtin from API schemas and frontend types
- Update tool-types spec to reflect removal of built-in concept
- Add Alembic migration for column removal
- Update tests to work without built-in distinction
2026-05-23 20:02:19 +02:00
Fusion 953ea05756 fix: show all probe attempts including successful ones
Remove 500-character truncation on probe output so users can see
all attempts including the final successful one. Add probe status
indicator (passed/failed/pending) that's always visible when probe
data exists.
2026-05-23 07:43:51 +02:00
Fusion 507b71c586 fix: React crash when opening terminal sessions
The backend was returning 'tool_type_interface_type' (string) but the
frontend expected 'tool_type_interfaces' (array). This caused
undefined.includes() crash when clicking Open on terminal sessions.

Changed both list_instances and get_user_sessions to return
tool_type_interfaces as an array. Also added clone_mode and branch
to get_user_sessions response.
2026-05-23 07:36:46 +02:00
Fusion aebcf25bf4 fix: OpenCode instances fail with 'no port configured' error
For terminal-only tools like OpenCode, default_port is 0 which is falsy
in Python. The code incorrectly treated port 0 as 'not configured' and
marked the instance as error. Now we only check if tool_type exists,
and default to port 0. Terminal tools skip tunnel creation anyway.
2026-05-23 07:29:56 +02:00
Fusion ae42cac61e fix: terminal tools always showing as unhealthy
For terminal-only tools (no URL), only check container status for
overall health instead of requiring tunnel health. Terminal tools
do not have tunnels, so tunnel_status stays as 'not_applicable'
which was failing the healthy check.
2026-05-22 23:55:51 +02:00
alex e2ad7d7fb6 fix: prevent null default_port for terminal tools 2026-05-22 21:49:31 +00:00
alex 9e1334eb6d fix: remove port exposure from terminal tool (opencode) 2026-05-22 21:47:02 +00:00
alex a559470369 fix: add openssh-client to API Dockerfile for SSH git clone support 2026-05-22 21:29:28 +00:00
alex 8cab17472e Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev 2026-05-22 21:27:25 +00:00
alex e9d404b1ff feat: add SSH key payload signing and verification endpoints
- POST /ssh-keys/{id}/sign - sign payload with Ed25519 private key
- POST /ssh-keys/{id}/verify - verify signature with public key
- Returns base64-encoded signatures
2026-05-22 21:27:10 +00:00
Fusion dab6c74046 Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev 2026-05-22 23:23:36 +02:00
alex ca8b255148 fix: log detailed error messages in repository preflight and clone 2026-05-22 21:21:45 +00:00
Fusion 02a2ad6df5 feat: allow modification of built-in tool types
Remove restrictions on updating and deleting built-in tool types.
Show delete button for all tool types in Tool Workshop.
2026-05-22 23:20:59 +02:00
alex 4ef0f108ea fix: use SSH key during repository preflight and clone 2026-05-22 21:19:15 +00:00
alex dc8ef0e463 fix: chain clone_mode migration after single_interface migration 2026-05-22 21:12:46 +00:00