feat: implement tool instances backend and session navigation

Backend:
- Create ToolInstance model with status tracking
- Add Alembic migration for tool_instances table
- Create Docker service for compose template rendering and container execution
- Add CRUD API endpoints for tool instances
- Add lifecycle endpoints (start/stop/restart)
- Add user sessions endpoint for navigation
- Register routers in main.py

Frontend:
- Create SessionsProvider with React context
- Create sessions API client
- Update AppShell with sessions section in navigation
- Add session status indicators and polling
- Add CSS for session navigation

Quality gates: typecheck ✓, lint ✓, build ✓
This commit is contained in:
Fusion
2026-05-19 20:42:59 +02:00
parent c52367401b
commit c795f8f873
21 changed files with 1455 additions and 2 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
name: tool-instances
+146
View File
@@ -0,0 +1,146 @@
# Tool Instances - Design
## Architecture
```
Tool Instance System
├── Backend
│ ├── ToolInstance Model
│ ├── Session API (CRUD + lifecycle)
│ ├── Docker Service (compose execution)
│ └── Status Polling
├── Frontend
│ ├── SessionStore (active sessions)
│ ├── AppShell Integration (nav entries)
│ ├── Instance Manager (repo page)
│ └── Session Launcher (create dialog)
└── Docker
├── Compose Template Rendering
├── Container Execution
└── Volume Management
```
## Data Model
### ToolInstance
```python
class ToolInstance(Base):
id: UUID
name: str # Generated: "vscode-myrepo-abc123"
display_name: str # User-friendly name
tool_type_id: UUID -> ToolType
repository_id: UUID -> GitRepository
project_id: UUID -> Project
owner_id: UUID -> User
status: str # pending, building, running, stopped, error
container_id: str | None
compose_path: str | None # Path to rendered compose file
url: str | None # Access URL
port: int | None
last_started_at: datetime | None
last_stopped_at: datetime | None
created_at: datetime
updated_at: datetime
```
## API Design
### Endpoints
```
POST /projects/{id}/repositories/{id}/instances
GET /projects/{id}/repositories/{id}/instances
GET /projects/{id}/repositories/{id}/instances/{id}
PUT /projects/{id}/repositories/{id}/instances/{id}
DELETE /projects/{id}/repositories/{id}/instances/{id}
POST /projects/{id}/repositories/{id}/instances/{id}/start
POST /projects/{id}/repositories/{id}/instances/{id}/stop
POST /projects/{id}/repositories/{id}/instances/{id}/restart
GET /projects/{id}/repositories/{id}/instances/{id}/status
GET /projects/{id}/repositories/{id}/instances/{id}/logs
GET /users/me/sessions # Active sessions for nav
```
## Docker Integration
### Compose Template Rendering
```yaml
# Template variables:
# {{REPO_PATH}} - Absolute path to repo
# {{INSTANCE_NAME}} - Unique instance name
# {{TOOL_PORT}} - Exposed port
services:
{{INSTANCE_NAME}}:
image: codercom/code-server:latest
volumes:
- {{REPO_PATH}}:/workspace
ports:
- "{{TOOL_PORT}}:8080"
environment:
- PASSWORD={{INSTANCE_NAME}}
```
### Execution Flow
1. Create instance directory: `data/instances/{instance_id}/`
2. Render compose file to `docker-compose.yml`
3. Run `docker compose -f {path} up -d`
4. Capture container ID from output
5. Poll status until running or error
## Frontend Integration
### Session Store
```typescript
interface Session {
id: string;
name: string;
displayName: string;
toolType: string;
toolIcon: string;
repositoryId: string;
projectId: string;
status: "pending" | "running" | "stopped" | "error";
url: string | null;
}
const useSessions = () => {
const sessions = useAtom(sessionsAtom);
const addSession = (session: Session) => { ... };
const removeSession = (id: string) => { ... };
const updateStatus = (id: string, status: string) => { ... };
return { sessions, addSession, removeSession, updateStatus };
};
```
### AppShell Navigation
- Add "Sessions" section in nav
- Show active sessions with tool icons
- Session status indicator (green dot for running)
- Click opens tool in new tab
- Dropdown for managing sessions
### Repository Page
- "Launch Tool" button
- Dialog to select tool type
- Instance list with status/actions
- Quick actions: start/stop/delete
## State Machine
```
[create] -> pending -> [docker up] -> building -> [container running] -> running
|
v
[docker error] -> error
[running] -> [stop] -> stopped
[stopped] -> [start] -> pending -> building -> running
[any] -> [delete] -> [docker down] -> deleted
```
## Security
- Only repository owner can create instances
- Instances run in isolated Docker networks
- No privileged containers
- Resource limits (CPU, memory) on containers
@@ -0,0 +1,50 @@
# Tool Instances with Sessions
## Problem
Users currently have no way to launch development tools (code-server, Jupyter, etc.) directly from their repositories. The tool-types system exists but cannot create running container instances. Additionally, there's no concept of a "session" - a running tool linked to a specific repo that appears in navigation for quick access.
## Solution
Implement a complete tool instance management system with sessions:
1. **ToolInstance Model** - Links a ToolType to a GitRepository with status tracking
2. **Session Concept** - A running ToolInstance that gets a top-level navigation entry
3. **Docker Integration** - Render compose templates and execute docker compose commands
4. **Lifecycle Management** - Start, stop, restart, and delete instances
5. **Navigation Integration** - Active sessions appear in the app shell for quick access
## Key Features
### Tool Instance Creation
- Select a tool type and repository
- Generate unique instance name
- Render Docker Compose template with variables
- Execute `docker compose up -d`
- Store container metadata
### Session Management
- Sessions are active/running instances
- Each session gets a top-level nav entry with the tool icon
- Session dropdown in app shell shows active sessions
- Clicking a session opens the tool in a new tab/window
### Lifecycle Operations
- Start: `docker compose start`
- Stop: `docker compose stop`
- Restart: `docker compose restart`
- Delete: `docker compose down -v` + remove DB record
### Status Monitoring
- pending, building, running, stopped, error
- Real-time status via Docker API
- Last accessed timestamp
## Success Criteria
- [ ] Create tool instances from repository page
- [ ] Sessions appear in top-level navigation
- [ ] Start/stop/restart/delete instances
- [ ] Status monitoring works
- [ ] Docker Compose templates render correctly
- [ ] Navigation updates when sessions change
@@ -0,0 +1,147 @@
# Tool Instances Specification
## Requirements
### Functional Requirements
1. **ToolInstance Model**: Store instance metadata with status tracking
2. **Session API**: CRUD operations + lifecycle (start/stop/restart/delete)
3. **Docker Integration**: Render compose templates and execute commands
4. **Status Monitoring**: Real-time container status polling
5. **Log Access**: View container logs (last 100 lines)
6. **Session Navigation**: Active sessions appear in app shell
7. **URL Generation**: Unique access URLs for each running instance
### Non-Functional Requirements
1. **Security**: Isolated containers, no privileged mode
2. **Resource Limits**: CPU and memory constraints
3. **Error Handling**: Graceful failure with cleanup
4. **Performance**: Start time < 30 seconds
## API Specification
### Create Instance
```
POST /projects/{project_id}/repositories/{repo_id}/instances
Body: {
tool_type_id: string,
display_name: string (optional)
}
Response: {
id: string,
name: string,
display_name: string,
tool_type_id: string,
status: "pending",
created_at: string
}
```
### List Instances
```
GET /projects/{project_id}/repositories/{repo_id}/instances
Response: {
instances: [...]
}
```
### Get Instance
```
GET /projects/{project_id}/repositories/{repo_id}/instances/{instance_id}
Response: {
id: string,
name: string,
display_name: string,
status: string,
container_id: string | null,
url: string | null,
port: number | null,
last_started_at: string | null,
last_stopped_at: string | null,
created_at: string
}
```
### Lifecycle Operations
```
POST /projects/{project_id}/repositories/{repo_id}/instances/{instance_id}/start
POST /projects/{project_id}/repositories/{repo_id}/instances/{instance_id}/stop
POST /projects/{project_id}/repositories/{repo_id}/instances/{instance_id}/restart
Response: { status: string }
```
### Delete Instance
```
DELETE /projects/{project_id}/repositories/{repo_id}/instances/{instance_id}
Response: 204 No Content
```
### Get User Sessions
```
GET /users/me/sessions
Response: {
sessions: [
{
id: string,
display_name: string,
tool_type_name: string,
tool_icon: string,
repository_name: string,
project_name: string,
status: string,
url: string | null
}
]
}
```
## Docker Compose Template Variables
- `{{REPO_PATH}}`: Absolute path to git repository on host
- `{{INSTANCE_NAME}}`: Unique instance identifier
- `{{INSTANCE_ID}}`: UUID of the instance
- `{{TOOL_PORT}}`: Assigned port for the tool
- `{{USER_ID}}`: Owner user ID
- `{{PROJECT_ID}}`: Project ID
## Status Values
- `pending`: Instance created, waiting to start
- `building`: Docker compose up in progress
- `running`: Container is running
- `stopped`: Container stopped
- `error`: Container failed to start or crashed
## Database Schema
```sql
CREATE TABLE tool_instances (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
display_name VARCHAR(255) NOT NULL,
tool_type_id UUID NOT NULL REFERENCES tool_types(id),
repository_id UUID NOT NULL REFERENCES git_repositories(id),
project_id UUID NOT NULL REFERENCES projects(id),
owner_id UUID NOT NULL REFERENCES users(id),
status VARCHAR(50) NOT NULL DEFAULT 'pending',
container_id VARCHAR(255),
compose_path VARCHAR(1024),
url VARCHAR(1024),
port INTEGER,
last_started_at TIMESTAMP WITH TIME ZONE,
last_stopped_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_tool_instances_owner ON tool_instances(owner_id);
CREATE INDEX idx_tool_instances_repo ON tool_instances(repository_id);
CREATE INDEX idx_tool_instances_status ON tool_instances(status);
```
## Testing Requirements
1. **Unit Tests**: Docker command generation, template rendering
2. **Integration Tests**: API endpoints, database operations
3. **Manual Testing**: Container lifecycle, navigation updates
+157
View File
@@ -0,0 +1,157 @@
# Tool Instances - Tasks
## Phase 1: Backend Model & Database
- [ ] **Task 1.1**: Create ToolInstance model
- Create `src/models/tool_instance.py`
- Fields: id, name, display_name, tool_type_id, repository_id, project_id, owner_id, status, container_id, compose_path, url, port, timestamps
- Add relationship to ToolType and GitRepository
- Add to `models/__init__.py`
- [ ] **Task 1.2**: Create Alembic migration
- Generate migration for tool_instances table
- Add indexes for owner_id, repository_id, status
## Phase 2: Backend Docker Service
- [ ] **Task 2.1**: Create Docker service
- Create `src/services/docker.py`
- Function: `render_compose_template(template, variables)`
- Function: `execute_compose_command(compose_path, action)`
- Function: `get_container_status(container_id)`
- Function: `get_container_logs(container_id, tail=100)`
- Error handling and cleanup
- [ ] **Task 2.2**: Create instance directory structure
- Base path: `data/instances/{instance_id}/`
- Render compose file to `docker-compose.yml`
- Ensure directory exists and is writable
## Phase 3: Backend API
- [ ] **Task 3.1**: Create instances API module
- Create `src/api/tool_instances.py`
- Import required dependencies
- [ ] **Task 3.2**: Implement create instance endpoint
- POST /projects/{id}/repositories/{id}/instances
- Validate tool_type_id exists
- Generate unique instance name
- Render compose template
- Save to database (status: pending)
- Return instance metadata
- [ ] **Task 3.3**: Implement list instances endpoint
- GET /projects/{id}/repositories/{id}/instances
- Filter by repository
- Include tool type info
- [ ] **Task 3.4**: Implement get instance endpoint
- GET /projects/{id}/repositories/{id}/instances/{id}
- Include real-time status from Docker
- [ ] **Task 3.5**: Implement lifecycle endpoints
- POST .../start - execute docker compose up
- POST .../stop - execute docker compose stop
- POST .../restart - execute docker compose restart
- Update status in database
- [ ] **Task 3.6**: Implement delete instance endpoint
- DELETE /projects/{id}/repositories/{id}/instances/{id}
- Execute docker compose down -v
- Remove instance directory
- Delete database record
- [ ] **Task 3.7**: Implement user sessions endpoint
- GET /users/me/sessions
- Return all running instances for current user
- Include tool type icon and names
- [ ] **Task 3.8**: Register router
- Add tool_instances router to main.py
## Phase 4: Frontend State Management
- [ ] **Task 4.1**: Create session store
- Create `src/state/sessions.ts`
- Define Session interface
- Create atom for sessions list
- Add helper functions
- [ ] **Task 4.2**: Create sessions API client
- Create `src/api/sessions.ts`
- Functions: list, create, start, stop, restart, delete, getStatus
- TypeScript interfaces
## Phase 5: Frontend Components
- [ ] **Task 5.1**: Update AppShell with sessions
- Add "Sessions" section in navigation
- Show active sessions with icons
- Status indicators (green dot)
- Click opens tool URL
- [ ] **Task 5.2**: Create LaunchToolDialog
- Select tool type from dropdown
- Enter display name (optional)
- Create instance on submit
- Show creation progress
- [ ] **Task 5.3**: Create InstanceList component
- List instances for a repository
- Show status, name, tool type
- Action buttons: start/stop/restart/delete
- Open URL button
- [ ] **Task 5.4**: Create InstanceCard component
- Compact card showing instance info
- Status badge
- Quick actions
## Phase 6: Frontend Pages
- [ ] **Task 6.1**: Add instances to repository page
- Add "Instances" tab or section
- Show InstanceList
- Add "Launch Tool" button
- [ ] **Task 6.2**: Create sessions dropdown
- Add to header or nav
- Quick access to active sessions
- Show status indicators
## Phase 7: Integration & Polish
- [ ] **Task 7.1**: Add instance status polling
- Poll status every 5 seconds
- Update session store
- Reflect in UI
- [ ] **Task 7.2**: Add error handling
- Docker failures
- Template rendering errors
- Network errors
- [ ] **Task 7.3**: Add loading states
- Creating instance
- Starting/stopping
- Deleting
## Phase 8: Quality Gates
- [ ] **Task 8.1**: Backend tests
- ruff check
- mypy check
- pytest
- [ ] **Task 8.2**: Frontend tests
- typecheck
- lint
- build
- [ ] **Task 8.3**: Manual testing
- Create instance
- Start/stop/restart
- Delete
- Navigation updates
- Status polling