merge: align dev branch with main

This commit is contained in:
Developer
2026-06-03 08:51:02 +00:00
parent 51a399c775
commit b39d6ce5f4
319 changed files with 30221 additions and 9221 deletions
+14 -27
View File
@@ -111,36 +111,23 @@ The system SHALL provide a dashboard overview.
- Recent activity
- Quick action buttons
### Requirement: Tool Interface Type Dropdown
The tool workshop SHALL provide a dropdown for selecting a single interface type.
### Requirement: Projects Listing Page Layout
The projects listing page SHALL display project cards with Settings, Delete, and Open Workspace actions, where Open Workspace is the rightmost action.
#### Scenario: Interface type dropdown
- GIVEN the tool workshop page
- WHEN a user creates or edits a tool type
- THEN the interface type field is a dropdown (not checkboxes)
- AND the options are "web" and "terminal"
- AND only one option can be selected
#### Scenario: Project card action layout
- GIVEN the projects listing page
- WHEN project cards are rendered
- THEN each card shows actions in order: Settings link, Delete button, Open Workspace button (rightmost)
### Requirement: Conditional Port Fields
The tool workshop SHALL conditionally show or hide port-related fields based on the selected interface type.
#### Scenario: Navigate to project settings
- GIVEN the projects listing page
- WHEN a user clicks the Settings link
- THEN they navigate to `/projects/:id/settings`
#### Scenario: Web tool shows port fields
- GIVEN a tool type with interface type "web"
- WHEN the user views the tool editor
- THEN the Default Port field is visible and required
- AND port-related config fields are shown
#### Scenario: Terminal tool hides port fields
- GIVEN a tool type with interface type "terminal"
- WHEN the user views the tool editor
- THEN the Default Port field is hidden
- AND port-related config fields are hidden or disabled
#### Scenario: Changing interface type updates visibility
- GIVEN a user changes interface type from "web" to "terminal"
- WHEN the change is applied
- THEN port fields are immediately hidden
- AND any port value is preserved but not validated
#### Scenario: No inline edit modal
- GIVEN the projects listing page
- WHEN a user views a project card
- THEN no inline Edit button or modal dialog is available
## Dependencies
+1 -20
View File
@@ -6,7 +6,7 @@ Manage git repositories as bare repos on disk with metadata in database.
## Requirements
### Requirement: Repository Creation
The system SHALL allow creating new bare git repositories with an optional SSH key association.
The system SHALL allow creating new bare git repositories.
#### Scenario: Create repository
- GIVEN an authenticated user with a project
@@ -14,25 +14,6 @@ The system SHALL allow creating new bare git repositories with an optional SSH k
- THEN a bare repo is initialized on disk at `/data/repos/{user_id}/{project_id}/{repo_name}.git`
- AND metadata is stored in the database
#### Scenario: Create repository with SSH key
- **GIVEN** an authenticated user with a project
- **WHEN** they create a new repository with `ssh_key_id`
- **THEN** a bare repo is initialized on disk
- **AND** the SSH key association is stored in the database
### Requirement: Repository SSH key assignment
The system SHALL allow associating an SSH key with a GitRepository for clone operations and container git access.
#### Scenario: Assign SSH key at repository creation
- **GIVEN** an authenticated user creating a repository
- **WHEN** they provide an `ssh_key_id`
- **THEN** the repository is associated with that SSH key
#### Scenario: Update repository SSH key
- **GIVEN** an authenticated user with an existing repository
- **WHEN** they call `PATCH /repositories/{id}/ssh-key` with a new `ssh_key_id`
- **THEN** the repository's SSH key association is updated
### Requirement: Repository Cloning
The system SHALL support cloning external repositories.
+27 -6
View File
@@ -31,17 +31,38 @@ The system SHALL list projects owned by the authenticated user, including relate
- WHEN one user requests their project list
- THEN only that user's projects are returned
### Requirement: Project Updates
The system SHALL support updating project details for project owners only.
### Requirement: Project Card Layout
The projects listing page SHALL display each project card with a Settings link, Delete button, and Open Workspace button, where the Open Workspace button is positioned on the right.
#### Scenario: Update project
- GIVEN a project owner
- WHEN they update the name or description
#### Scenario: View project card actions
- GIVEN the projects listing page
- WHEN a project card is rendered
- THEN it displays:
- A Settings link navigating to `/projects/:id/settings`
- A Delete button with confirmation
- An Open Workspace button positioned on the right side
#### Scenario: Navigate to project settings
- GIVEN the projects listing page
- WHEN a user clicks the Settings link on a project card
- THEN they are navigated to the project settings page
#### Scenario: No inline edit on project cards
- GIVEN the projects listing page
- WHEN a project card is rendered
- THEN no inline Edit button or modal dialog is present
### Requirement: Project Updates
The system SHALL support updating project details for project owners via the project settings page.
#### Scenario: Update project via settings
- GIVEN a project owner viewing the project settings page
- WHEN they update the name or description and save
- THEN the changes are persisted
#### Scenario: Non-owner update denied
- GIVEN a user who is not the project owner
- WHEN they attempt to update project details
- WHEN they attempt to update project details via the settings page
- THEN the system responds with forbidden status
### Requirement: Project Deletion
+79 -84
View File
@@ -1,95 +1,90 @@
## MODIFIED Requirements
# Tool Instance Management Specification
## Purpose
Launch, monitor, and manage development tool instances in Docker containers.
## Requirements
### Requirement: Tool Instance Creation
The system SHALL create and launch tool instances from repositories.
#### Scenario: Launch tool
- GIVEN an authenticated user with a project and repository
- WHEN they create a tool instance
- THEN:
1. A unique subdomain is generated: `{tool-name}-{tool-id}.hq.local`
2. The Docker Compose template is rendered with project values
3. `docker compose up -d` is executed
4. Container ID and status are stored
### Requirement: Tool Lifecycle
The system SHALL manage tool lifecycle operations.
#### Scenario: Stop tool
- GIVEN a running tool instance
- WHEN the user stops it
- THEN `docker compose stop` is executed
- AND status is updated to "stopped"
#### Scenario: Start tool
- GIVEN a stopped tool instance
- WHEN the user starts it
- THEN `docker compose start` is executed
- AND status is updated to "running"
#### Scenario: Delete tool
- GIVEN a tool instance
- WHEN the user deletes it
- THEN the container and volumes are removed
- AND the database record is deleted
### Requirement: Traefik Integration
The system SHALL auto-generate Traefik labels for routing.
#### Scenario: Route generation
- GIVEN a running tool instance
- THEN these labels are set:
- `traefik.enable=true`
- `traefik.http.routers.{tool_id}.rule=Host(\`{subdomain}.hq.local\`)`
- `traefik.http.routers.{tool_id}.entrypoints=web`
- `traefik.http.services.{tool_id}.loadbalancer.server.port={port}`
### Requirement: Status Monitoring
The system SHALL track tool status with startup and health states.
#### Scenario: Status check with health details
- **GIVEN** a tool instance
- **WHEN** status is queried
- **THEN** the real-time container status is returned:
- `pending`: Instance created, container not yet started
- `starting`: Container is running, readiness probe in progress
- `running`: Container is running and probe passed (or terminal tool)
- `unhealthy`: Container is running but probe failed/timed out
- `stopped`: Container was stopped by user
- `error`: Container failed to start or crashed
The system SHALL track tool status.
## ADDED Requirements
#### Scenario: Status check
- GIVEN a tool instance
- WHEN status is queried
- THEN the real-time container status is returned:
- pending, building, running, stopped, error
### Requirement: Health check endpoint enhancement
The system SHALL provide detailed health information through the health check endpoint.
### Requirement: Log Access
#### Scenario: Health check with container and tunnel status
- **GIVEN** a running instance
- **WHEN** `GET /instances/{id}/health` is called
- **THEN** the response includes:
- `healthy`: boolean - overall health
- `container_status`: "running", "exited", "restarting", or "not_found"
- `tunnel_status`: "healthy", "unreachable", "error_response", or "not_applicable"
- `tunnel_status_code`: HTTP status code or null
- `probe_status`: "passed", "failed", "pending", or "not_configured"
- `last_probe_output`: string or null
The system SHALL provide access to container logs.
### Requirement: Smart tunnel recreation
The system SHALL only allow tunnel recreation when the tunnel itself is broken.
#### Scenario: View logs
- GIVEN a tool instance
- WHEN logs are requested
- THEN the last 100 lines are returned
- AND live streaming is available via WebSocket
#### Scenario: Recreate tunnel for unreachable tunnel
- **GIVEN** an instance with `tunnel_status: "unreachable"`
- **WHEN** the recreate tunnel endpoint is called
- **THEN** the tunnel is recreated
- **AND** the new URL is returned
## Dependencies
#### Scenario: Block recreation for application errors
- **GIVEN** an instance with `tunnel_status: "error_response"` (e.g., HTTP 502)
- **WHEN** the recreate tunnel endpoint is called
- **THEN** the request is rejected with 400 Bad Request
- **AND** the error message explains the tunnel is working but the application is returning errors
- tool-types (tool definitions)
- git-repo (repository access)
- project-management (project context)
- Docker runtime
- Traefik reverse proxy
### Requirement: Clone mode instance creation
The system SHALL support creating tool instances with a clone mode that clones the repository into the instance directory.
## Quality Gates
#### Scenario: Create instance in clone mode
- **GIVEN** an authenticated user with a repository that has an SSH key and remote URL
- **WHEN** they create an instance with `clone_mode: "clone"` and `branch: "main"`
- **THEN** the system clones the repository into the instance directory
- **AND** the compose file uses the clone path as `REPO_PATH`
- **AND** the instance record stores `clone_mode="clone"` and `branch="main"`
#### Scenario: Create instance in mount mode
- **GIVEN** an authenticated user with a repository
- **WHEN** they create an instance with `clone_mode: "mount"` (or omit the field)
- **THEN** the compose file uses the host repository path as `REPO_PATH`
- **AND** the instance record stores `clone_mode="mount"`
### Requirement: SSH key mounting for git operations
The system SHALL mount the repository's SSH key into clone-mode containers for git operations.
#### Scenario: Start clone-mode instance
- **GIVEN** a clone-mode instance with an associated SSH key
- **WHEN** the instance is started
- **THEN** the SSH key is decrypted and written to `instance_dir/.ssh/`
- **AND** the `.ssh` directory is mounted into the container
- **AND** the container can perform git push/pull operations
### Requirement: Dirty check on clone deletion
The system SHALL check for uncommitted changes before deleting a clone-mode instance.
#### Scenario: Delete clean clone
- **GIVEN** a clone-mode instance with no uncommitted changes
- **WHEN** the user requests deletion
- **THEN** the instance is deleted successfully
#### Scenario: Delete dirty clone with confirmation
- **GIVEN** a clone-mode instance with uncommitted changes
- **WHEN** the user requests deletion
- **THEN** the system returns a warning with change details
- **AND** the user must confirm deletion
#### Scenario: Force delete dirty clone
- **GIVEN** a clone-mode instance with uncommitted changes
- **WHEN** the user requests deletion with `force=true`
- **THEN** the instance is deleted regardless of uncommitted changes
## REMOVED Requirements
None.
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
- `npm run typecheck` must pass
- `npm run lint` must pass
-19
View File
@@ -16,19 +16,6 @@ The system SHALL provide terminal sessions via WebSocket.
- THEN a WebSocket connection is established
- AND a shell is spawned in the container via `docker exec`
#### Scenario: Open terminal with startup command
- GIVEN a running tool instance with a tool type that has `startup_command` set
- WHEN the user opens the terminal
- THEN a WebSocket connection is established
- AND the startup command is executed before the interactive shell
- AND the shell is spawned in the container via `docker exec`
#### Scenario: Open terminal without startup command
- GIVEN a running tool instance with a tool type that has no `startup_command`
- WHEN the user opens the terminal
- THEN a WebSocket connection is established
- AND the shell spawns directly without any startup execution
### Requirement: Terminal I/O
The system SHALL stream terminal I/O via WebSocket.
@@ -64,12 +51,6 @@ The system SHALL manage terminal sessions.
- THEN the session is cleaned up
- AND the shell process is terminated
#### Scenario: Reset terminal session runs startup command
- GIVEN an active terminal session
- WHEN the user resets the session
- THEN a new shell is spawned
- AND the startup command executes before the new interactive shell
### Requirement: Access Control
The system SHALL restrict terminal access.
+2 -46
View File
@@ -11,14 +11,8 @@ The system SHALL provide a `ToolType` model to store tool definitions.
- `name`: unique string (e.g., "code-server")
- `display_name`: human-readable string (e.g., "VS Code Server")
- `description`: optional text
- `category`: string (e.g., "editor", "notebook")
- `interface_type`: single string — "web" or "terminal"
- `requires_port`: boolean indicating if port/tunnel configuration is needed
- `compose_template`: Docker Compose YAML string
- `dockerfile_template`: Dockerfile string
- `definition_type`: string — "compose" or "dockerfile"
- `required_variables`: list of required template variables
- `startup_command`: optional text — command to run before interactive shell for terminal sessions
- `is_builtin`: boolean flag for system-defined types
- `created_at`/`updated_at`: timestamps
@@ -36,11 +30,8 @@ The system SHALL provide REST API endpoints for tool type management.
- GIVEN an admin user
- WHEN they POST /api/tool-types with valid data
- THEN the system creates a new tool type
- AND validates `interface_type` is "web" or "terminal"
- AND validates `requires_port` is boolean
- AND validates the compose template YAML (if definition_type is "compose")
- AND validates the compose template YAML
- AND validates all required variables are present in template
- AND accepts optional `startup_command` field
- AND returns 201 Created with the new tool type
#### Scenario: Get tool type
@@ -53,14 +44,9 @@ The system SHALL provide REST API endpoints for tool type management.
- GIVEN an admin user
- WHEN they PUT /api/tool-types/{id} with valid data
- THEN the system updates the tool type
- AND accepts optional `startup_command` field
- AND re-validates the compose template
- AND returns 200 OK with updated tool type
#### Scenario: Get tool type includes startup command
- GIVEN an authenticated user
- WHEN they GET /api/tool-types/{id}
- THEN the response includes `startup_command` if set
#### Scenario: Delete tool type
- GIVEN an admin user
- WHEN they DELETE /api/tool-types/{id}
@@ -112,33 +98,3 @@ The system SHALL validate Docker Compose templates.
- THEN the system SHALL require:
- `services` key present
- At least one service defined
### Requirement: Port requirement indication
The system SHALL allow tool types to indicate whether they require port configuration.
#### Scenario: Web tool requires port
- GIVEN a tool type with `interface_type` = "web"
- WHEN the tool type is created or updated
- THEN `requires_port` SHALL default to true
- AND port-related configuration is shown in the UI
#### Scenario: Terminal tool does not require port
- GIVEN a tool type with `interface_type` = "terminal"
- WHEN the tool type is created or updated
- THEN `requires_port` SHALL default to false
- AND port-related configuration is hidden in the UI
### Requirement: Single interface validation
The system SHALL enforce that each tool type has exactly one interface type.
#### Scenario: Invalid interface type
- GIVEN a tool type creation request with `interface_type` = "invalid"
- WHEN the request is processed
- THEN the system returns 400 Bad Request
- AND the error message indicates valid values are "web" or "terminal"
#### Scenario: Missing interface type
- GIVEN a tool type creation request without `interface_type`
- WHEN the request is processed
- THEN the system returns 400 Bad Request
- AND the error message indicates interface_type is required
+39 -14
View File
@@ -1,8 +1,14 @@
## MODIFIED Requirements
# Tool Type Definition Specification
## Purpose
Define and register tool types using Docker Compose templates for launching development tools.
## Requirements
### Requirement: Tool Type Model
The system SHALL store tool type definitions in the database without built-in vs custom distinction.
The system SHALL store tool type definitions in the database.
#### Scenario: Create tool type
- GIVEN an admin user
@@ -14,18 +20,32 @@ The system SHALL store tool type definitions in the database without built-in vs
- icon: Visual identifier
- category: Tool category
- default_env_vars: Default environment variables
- default_port: **Required** primary port the tool listens on
- interfaces: List of supported interfaces ("web", "terminal")
- default_ports: Exposed ports
#### Scenario: Tool type without port rejected
- GIVEN a user creating a tool type without `default_port`
- WHEN the request is submitted
- THEN the system rejects with a 422 validation error
### Requirement: Template Variables
The system SHALL support template variable substitution.
#### Scenario: Variable substitution
- GIVEN a Docker Compose template
- WHEN it's rendered for a tool instance
- THEN these variables are substituted:
- `{{REPO_PATH}}`: Path to the git repository
- `{{WORKSPACE_DIR}}`: Working directory inside container
- `{{USER_ID}}`: User identifier
- `{{PROJECT_ID}}`: Project identifier
- `{{TOOL_ID}}`: Tool instance identifier
### Requirement: Built-in Tools
**Reason**: Built-in tools are now regular preconfigured tool types in the database, not special privileged types.
**Migration**: Built-in tool types (code-server, jupyter-notebook, opencode) are seeded as regular database records during migration. They can be edited or deleted like any other tool type.
The system SHALL include default tool types.
#### Scenario: Built-in tools
- GIVEN a fresh installation
- THEN these tool types are pre-configured:
- code-server: VS Code in browser
- jupyter-notebook: Jupyter notebooks
- opencode: OpenCode agent environment
### Requirement: Template Validation
@@ -36,7 +56,12 @@ The system SHALL validate Docker Compose templates.
- WHEN a user tries to create/update a tool type
- THEN the system rejects with validation errors
#### Scenario: Port not exposed in template
- GIVEN a tool type with `default_port: 8443`
- WHEN the compose template does not expose port 8443
- THEN the system rejects with a validation error indicating the port mismatch
## Dependencies
- Database models: ToolType
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass