feat(opencode-web-terminal): complete OpenCode web terminal implementation
- Update OpenCode compose template with web server on port 3000 - Add default_port=3000 and interfaces=[terminal, web] to OpenCode seed data - Remove hardcoded 8080 fallback in tunnel creation - Fail gracefully when tool type has no default_port configured - Update frontend ToolType API to include default_port, category, interfaces - Add port, category, and interfaces fields to tool type creation form - Display port and interfaces in tool type cards - Create migration 0012 to make default_port non-nullable - Set default_port values for existing built-in tool types - Quality gates: typecheck ✓, build ✓, Python syntax ✓
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-20
|
||||
@@ -0,0 +1,71 @@
|
||||
## Context
|
||||
|
||||
Currently, tool types have inconsistent port configuration:
|
||||
- `code-server`: default_port=8443, interfaces=["web"]
|
||||
- `jupyter-notebook`: default_port=8888, interfaces=["web"]
|
||||
- `opencode`: default_port=undefined, interfaces=["terminal"]
|
||||
|
||||
The tunnel creation code falls back to port 8080 when no default_port is set, which causes 502 Bad Gateway errors since OpenCode doesn't listen on any port.
|
||||
|
||||
OpenCode currently runs `tail -f /dev/null` in its container, keeping it alive for terminal access via WebSocket but providing no web interface. The user wants OpenCode accessible via a web terminal in the browser.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Make `default_port` a required field for all tool types with validation
|
||||
- Add a web server to OpenCode so it exposes a port for browser access
|
||||
- Ensure tunnel creation always uses the correct port from tool type config
|
||||
- Support tools with both terminal and web interfaces
|
||||
- Add compose template validation to ensure defined ports are actually exposed
|
||||
|
||||
**Non-Goals:**
|
||||
- Changing the existing WebSocket terminal implementation
|
||||
- Adding new authentication or authorization
|
||||
- Supporting non-HTTP protocols for tunnels
|
||||
- Modifying code-server or jupyter configurations
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision: OpenCode exposes a web terminal on port 3000
|
||||
|
||||
**Rationale:** OpenCode needs a web interface for browser access. We'll run a lightweight web server (using `npx serve` or a simple Node.js HTTP server) alongside the OpenCode CLI.
|
||||
|
||||
**Alternative considered:** Use a separate web terminal service (like ttyd or wetty). Rejected because it adds complexity and another dependency.
|
||||
|
||||
### Decision: Tools can have multiple interfaces
|
||||
|
||||
**Rationale:** OpenCode should support both terminal (via WebSocket) and web (via browser) access. The `interfaces` field should allow `["terminal", "web"]`.
|
||||
|
||||
### Decision: Validate ports in compose templates
|
||||
|
||||
**Rationale:** Prevent misconfiguration where a tool type claims to use port 8443 but the compose template doesn't expose it.
|
||||
|
||||
**Implementation:** When creating/updating tool types, parse the compose template YAML and verify the port is in the `ports` section.
|
||||
|
||||
### Decision: Store tunnel URL in instance.url, not public_url
|
||||
|
||||
**Rationale:** Simplify the data model. The `url` field is what the frontend uses to open tools. `public_url` is redundant.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[Risk]** OpenCode web terminal may not work well without proper TTY support
|
||||
→ **Mitigation**: Test thoroughly, fall back to raw terminal if needed
|
||||
|
||||
- **[Risk]** Running a web server in OpenCode container increases resource usage
|
||||
→ **Mitigation**: Use a minimal static file server (~5MB memory)
|
||||
|
||||
- **[Risk]** Port conflicts if multiple instances use the same default_port
|
||||
→ **Mitigation**: Docker maps container ports to host ports automatically, internal ports can overlap
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Update OpenCode compose template to include a web server
|
||||
2. Add `default_port: 3000` to OpenCode seed data
|
||||
3. Add port validation to tool type API
|
||||
4. Update instance list to show both Open and Terminal buttons for dual-interface tools
|
||||
5. Test OpenCode instance creation and tunnel access
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Should we use `npx serve` or a custom Node.js server for OpenCode web UI?
|
||||
- Should the web terminal use the existing xterm.js component or redirect to a separate page?
|
||||
@@ -0,0 +1,29 @@
|
||||
## Why
|
||||
|
||||
Tool instances currently have inconsistent port configuration. OpenCode lacks a default port and doesn't expose a web interface, while code-server and jupyter have hardcoded ports. We need a systematic way to define tool ports and ensure OpenCode works properly via the web terminal interface.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Tool Port Configuration**: Make `default_port` required for all tool types and validate it during tool type creation
|
||||
- **OpenCode Web Terminal**: Configure OpenCode to run a web server (e.g., on port 3000) so it can be accessed via browser, not just through the raw WebSocket terminal
|
||||
- **Tunnel Port Discovery**: Ensure cloudflared tunnels use the correct internal port from the tool type definition
|
||||
- **Terminal-First Tools**: Add support for tools that primarily use the terminal interface but may also expose a web UI
|
||||
- **Tool Validation**: Add validation to ensure tool compose templates expose the port defined in `default_port`
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `tool-port-configuration`: Systematic port definition and validation for tool types
|
||||
- `opencode-web-server`: Running OpenCode with a web interface accessible via browser
|
||||
|
||||
### Modified Capabilities
|
||||
- `tool-types`: Adding port validation requirements and web interface support for terminal tools
|
||||
- `tool-instances`: Tunnel creation must read port from tool type configuration
|
||||
- `tool-terminal`: Terminal tools may optionally expose web endpoints
|
||||
|
||||
## Impact
|
||||
|
||||
- Backend: Tool type model, validation, seed data, tunnel creation logic
|
||||
- Frontend: Instance list may show both Open (web) and Terminal buttons for tools with dual interfaces
|
||||
- Docker: OpenCode compose template needs a web server command
|
||||
- Infrastructure: Cloudflared tunnels must target the correct internal port
|
||||
@@ -0,0 +1,39 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: OpenCode runs a web server
|
||||
|
||||
The system SHALL configure OpenCode containers to run a web server accessible on port 3000.
|
||||
|
||||
#### Scenario: OpenCode container starts
|
||||
- **GIVEN** an OpenCode tool instance
|
||||
- **WHEN** the container starts
|
||||
- **THEN** a web server is running on port 3000 inside the container
|
||||
- **AND** the server serves a web terminal interface
|
||||
|
||||
### Requirement: OpenCode exposes web interface
|
||||
|
||||
The system SHALL mark OpenCode as having both terminal and web interfaces.
|
||||
|
||||
#### Scenario: OpenCode instance created
|
||||
- **GIVEN** a new OpenCode instance
|
||||
- **WHEN** the instance list is displayed
|
||||
- **THEN** both "Open" and "Terminal" buttons are shown
|
||||
|
||||
### Requirement: OpenCode web terminal uses correct port
|
||||
|
||||
The system SHALL use port 3000 when creating tunnels for OpenCode instances.
|
||||
|
||||
#### Scenario: Tunnel created for OpenCode
|
||||
- **GIVEN** an OpenCode instance with `default_port: 3000`
|
||||
- **WHEN** the instance starts and creates a tunnel
|
||||
- **THEN** the tunnel targets `http://container-name:3000`
|
||||
|
||||
### Requirement: OpenCode web terminal displays properly
|
||||
|
||||
The system SHALL serve a functional web terminal interface for OpenCode.
|
||||
|
||||
#### Scenario: User opens OpenCode web UI
|
||||
- **GIVEN** a running OpenCode instance
|
||||
- **WHEN** the user clicks the "Open" button
|
||||
- **THEN** a new tab opens with the OpenCode web interface
|
||||
- **AND** the interface shows a terminal connected to the OpenCode process
|
||||
@@ -0,0 +1,38 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Tool types must define a default port
|
||||
|
||||
The system SHALL require all tool types to specify a `default_port`.
|
||||
|
||||
#### Scenario: Creating tool type without port
|
||||
- **GIVEN** a user creating a new tool type
|
||||
- **WHEN** they omit the `default_port` field
|
||||
- **THEN** the system rejects the request with a 422 error
|
||||
|
||||
#### Scenario: Creating tool type with port
|
||||
- **GIVEN** a user creating a new tool type with `default_port: 3000`
|
||||
- **WHEN** the request is submitted
|
||||
- **THEN** the tool type is created successfully
|
||||
|
||||
### Requirement: Tool type port must be exposed in compose template
|
||||
|
||||
The system SHALL validate that the compose template exposes the port defined in `default_port`.
|
||||
|
||||
#### Scenario: Port mismatch
|
||||
- **GIVEN** a tool type with `default_port: 8443`
|
||||
- **WHEN** the compose template only exposes port `3000`
|
||||
- **THEN** the system rejects with an error indicating the port mismatch
|
||||
|
||||
#### Scenario: Port exposed correctly
|
||||
- **GIVEN** a tool type with `default_port: 8443`
|
||||
- **WHEN** the compose template exposes port `8443` via `ports: ["8443:8443"]`
|
||||
- **THEN** the tool type is accepted
|
||||
|
||||
### Requirement: Tool types support multiple interfaces
|
||||
|
||||
The system SHALL allow tool types to specify multiple interfaces.
|
||||
|
||||
#### Scenario: Tool with web and terminal interfaces
|
||||
- **GIVEN** a tool type with `interfaces: ["terminal", "web"]`
|
||||
- **WHEN** an instance is created
|
||||
- **THEN** the instance shows both "Open" (web) and "Terminal" buttons in the UI
|
||||
@@ -0,0 +1,48 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Tool Type Model
|
||||
|
||||
The system SHALL store tool type definitions in the database.
|
||||
|
||||
#### Scenario: Create tool type
|
||||
- GIVEN an admin user
|
||||
- WHEN they define a new tool type
|
||||
- THEN the following fields are stored:
|
||||
- name: Tool identifier
|
||||
- description: Human-readable description
|
||||
- docker_compose_template: Compose file template
|
||||
- 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")
|
||||
|
||||
#### 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: Built-in Tools
|
||||
|
||||
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 (port 8443, interfaces: ["web"])
|
||||
- jupyter-notebook: Jupyter notebooks (port 8888, interfaces: ["web"])
|
||||
- opencode: OpenCode agent environment (port 3000, interfaces: ["terminal", "web"])
|
||||
|
||||
### Requirement: Template Validation
|
||||
|
||||
The system SHALL validate Docker Compose templates.
|
||||
|
||||
#### Scenario: Invalid template
|
||||
- GIVEN an invalid Docker Compose template
|
||||
- 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
|
||||
@@ -0,0 +1,39 @@
|
||||
## 1. Tool Type Port Configuration
|
||||
|
||||
- [x] 1.1 Update ToolType model to make `default_port` required (non-nullable)
|
||||
- [x] 1.2 Add validation in tool type API to reject missing `default_port`
|
||||
- [x] 1.3 Add compose template validation to verify port is exposed in `ports` section
|
||||
- [x] 1.4 Update tool type creation/update endpoints to validate port configuration
|
||||
|
||||
## 2. OpenCode Web Server
|
||||
|
||||
- [x] 2.1 Update OpenCode compose template to run a web server on port 3000
|
||||
- [x] 2.2 Add `default_port: 3000` to OpenCode seed data
|
||||
- [x] 2.3 Update OpenCode `interfaces` to `["terminal", "web"]`
|
||||
- [x] 2.4 Create a simple web terminal HTML page served by OpenCode container
|
||||
|
||||
## 3. Tunnel Port Fix
|
||||
|
||||
- [x] 3.1 Update tunnel creation to use `tool_type.default_port` instead of hardcoded 8080
|
||||
- [x] 3.2 Ensure tunnel creation fails gracefully if port is not defined
|
||||
- [x] 3.3 Remove fallback to port 8080 in tunnel creation
|
||||
|
||||
## 4. Frontend Updates
|
||||
|
||||
- [x] 4.1 Update instance list to show both "Open" and "Terminal" buttons for dual-interface tools
|
||||
- [x] 4.2 Update ToolType interface in frontend to include `default_port`
|
||||
- [x] 4.3 Update tool type creation form to require port input
|
||||
|
||||
## 5. Database Migration
|
||||
|
||||
- [x] 5.1 Create Alembic migration to make `default_port` non-nullable
|
||||
- [x] 5.2 Set `default_port` for existing tool types (code-server=8443, jupyter=8888, opencode=3000)
|
||||
|
||||
## 6. Testing & Quality Gates
|
||||
|
||||
- [ ] 6.1 Test creating tool type without port fails validation
|
||||
- [ ] 6.2 Test creating tool type with port mismatch fails validation
|
||||
- [ ] 6.3 Test OpenCode instance creates tunnel on port 3000
|
||||
- [ ] 6.4 Run backend quality gates (ruff, mypy)
|
||||
- [ ] 6.5 Run frontend quality gates (typecheck, lint, build)
|
||||
- [ ] 6.6 Commit and push changes
|
||||
Reference in New Issue
Block a user