merge: integrate main restructuring into dev

- Resolve 57 merge conflicts from codebase restructure
- Port dev feature code to new directory structure:
  * Update import paths to use @/ aliases
  * Add backward-compatible API signatures (createInstance, startInstance, deleteInstance)
  * Add missing type exports (ProjectWithRepos, InstanceHealth, Branch, BranchesResponse)
  * Extend Session and GitRepository types for dev features
  * Extend TerminalComponent props for mobile terminal wrapper
  * Add missing icon names (bell, drag, undo)

Quality gates: tsc pass (0 errors), build pass, 127/131 tests pass
(4 pre-existing failures unrelated to merge)
This commit is contained in:
Developer
2026-06-03 09:23:06 +00:00
279 changed files with 24254 additions and 21693 deletions
+1
View File
@@ -36,6 +36,7 @@ All responses are JSON. Error responses follow this format:
- [Config Profiles](config-profiles.md) - Config profile management with git mounts
- [Users](users.md) - User management and settings
- [Tool Types](tool-types.md) - Tool type management
- [Config Profiles](config-profiles.md) - Config profile management for tool instances
- [SSH Keys](ssh-keys.md) - SSH key management
## Testing
+402 -134
View File
@@ -1,165 +1,433 @@
# Config Profiles
# Config Profiles API
## Overview
Config profile management endpoints for customizing tool instances.
Config profiles allow users to define reusable configuration sets for tool instances. Profiles can include environment variables, files, mounts, and git repository mounts. They support profile includes for composition and can be scoped to specific projects or tool types.
## Authentication
## Git Mounts
All endpoints require authentication (session cookie).
Git mounts allow you to mount files or directories from git repositories into tool instances at startup.
---
### Git Mount Object
## GET /config-profiles
**Description:** List all config profiles for the current user.
### Query Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tool_type_id` | `string` | No | Filter by tool type compatibility (currently returns all profiles) |
### Response
#### Success (200 OK)
```json
{
"repo_id": "550e8400-e29b-41d4-a716-446655440000",
"source_path": ".",
"target_path": "/app/config",
"branch": "main"
}
```
**Fields:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `repo_id` | string (UUID) | Yes | ID of the git repository to mount from |
| `source_path` | string | No | Path within the repository (default: "."). Supports glob patterns like "*.json" or "configs/**" |
| `target_path` | string | Yes | Absolute path inside the container where files will be mounted |
| `branch` | string | No | Branch or tag to checkout before mounting (default: current branch) |
### Path Validation
- `source_path`: Must be relative (no leading `/`). Cannot contain `..` (path traversal)
- `target_path`: Must be absolute (starts with `/`). Cannot contain `..`
### Glob Patterns
The `source_path` supports standard glob patterns:
- `*.json` - Match all JSON files in root
- `configs/**` - Match all files in configs directory recursively
- `src/*.py` - Match all Python files in src directory
- `.` - Mount entire repository (default)
**Limits:**
- Maximum 100 matches per glob pattern
- Only matches within the repository boundary
### Branch Behavior
When a `branch` is specified:
1. System attempts to checkout the branch in the existing clone
2. If branch doesn't exist locally, attempts to fetch from remote and checkout
3. If checkout fails, logs warning and continues with current branch
4. No branch specified: uses current checked-out branch
**Auto-clone:** If repository is not cloned locally, the system will automatically clone it using the repository's configured SSH key.
## Endpoints
### List Config Profiles
```
GET /config-profiles
```
Query parameters:
- `project_id` (optional): Filter by project compatibility
- `tool_type_id` (optional): Filter by tool type compatibility
Response includes `git_mounts` array in each profile.
### Create Config Profile
```
POST /config-profiles
```
Request body:
```json
{
"name": "My Profile",
"git_mounts": [
"profiles": [
{
"repo_id": "550e8400-e29b-41d4-a716-446655440000",
"source_path": "configs/*.json",
"target_path": "/app/config",
"branch": "main"
"id": "uuid",
"user_id": "uuid",
"name": "my-profile",
"description": "My custom profile",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
}
```
Validation:
- All referenced repositories must exist
- Repositories must belong to the same project (if profile has project_id)
- source_path and target_path must pass path validation
---
### Update Config Profile
## POST /config-profiles
```
PUT /config-profiles/{id}
```
**Description:** Create a new config profile.
Same request body as create. Partial updates supported (omit fields to keep current values).
### Request
### Preview Resolved Profile
#### Request Body
```
GET /config-profiles/{id}/preview
```
Returns the fully resolved profile with all includes merged. Git mounts from included profiles are merged with override rules (later profiles override earlier ones with same repo_id + target_path combo).
Response:
```json
{
"profile_id": "550e8400-e29b-41d4-a716-446655440000",
"profile_name": "My Profile",
"env_vars": {},
"runtime_hints": {},
"mounts": [],
"git_mounts": [
{
"repo_id": "550e8400-e29b-41d4-a716-446655440000",
"source_path": "configs/*.json",
"target_path": "/app/config",
"branch": "main"
}
],
"files": {},
"overrides": {
"env_vars": {},
"runtime_hints": {},
"files": {},
"mounts": {}
},
"included_profiles": []
"name": "my-profile",
"description": "My custom profile"
}
```
## Error Handling
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | `string` | Yes | Unique profile name (max 255 chars) |
| `description` | `string` | No | Optional description |
Git mount errors during instance startup are non-blocking:
- Missing repository: Mount skipped, warning logged
- Clone failure: Mount skipped, warning logged
- Invalid paths: Mount skipped, warning logged
- Branch checkout failure: Falls back to current branch, warning logged
### Response
Instance startup continues normally even if some git mounts fail.
#### Success (201 Created)
## Profile Resolution
Returns created profile.
When a profile includes other profiles, git mounts are merged:
- Same `repo_id` + `target_path` combo: later profile overrides
- Different combos: both are kept
- Branch conflicts: later profile wins
#### Error (409 Conflict)
Example:
```json
{
"detail": "config profile with name 'my-profile' already exists"
}
```
Base Profile: git_mounts = [{repo_a, /app, main}]
Included Profile: git_mounts = [{repo_a, /app, develop}, {repo_b, /data}]
Resolved: git_mounts = [{repo_a, /app, develop}, {repo_b, /data}]
#### Error (422 Unprocessable Entity)
```json
{
"detail": "Profile name cannot be empty"
}
```
---
## GET /config-profiles/{profile_id}
**Description:** Get a config profile with its includes and mounts.
### Response
#### Success (200 OK)
```json
{
"id": "uuid",
"user_id": "uuid",
"name": "my-profile",
"description": "My custom profile",
"includes": [
{
"id": "uuid",
"profile_id": "uuid",
"included_profile_id": "uuid",
"included_profile_name": "base-profile",
"order_index": 0,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"mounts": [
{
"id": "uuid",
"profile_id": "uuid",
"target_path": "/etc/config",
"mode": "rw",
"files": {"test.txt": "hello"},
"order_index": 0,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
],
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
```
---
## PUT /config-profiles/{profile_id}
**Description:** Update a config profile.
### Request
#### Request Body
```json
{
"name": "updated-name",
"description": "Updated description"
}
```
### Response
#### Success (200 OK)
Returns updated profile.
---
## DELETE /config-profiles/{profile_id}
**Description:** Delete a config profile and all its includes and mounts.
### Response
#### Success (204 No Content)
---
## GET /config-profiles/defaults
**Description:** Get the current user's default profile assignments per tool type.
### Response
#### Success (200 OK)
```json
{
"default_profiles": {
"code-server": "profile-uuid-1",
"jupyter-notebook": "profile-uuid-2"
}
}
```
---
## PUT /config-profiles/defaults
**Description:** Set the current user's default profile assignments per tool type.
### Request
#### Request Body
```json
{
"default_profiles": {
"code-server": "profile-uuid-1",
"jupyter-notebook": "profile-uuid-2"
}
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `default_profiles` | `object` | Yes | Mapping of tool_type_id to profile_id |
### Response
#### Success (200 OK)
Returns updated default profiles.
#### Error (404 Not Found)
```json
{
"detail": "profile {profile_id} not found"
}
```
---
## GET /config-profiles/defaults/{tool_type_id}
**Description:** Get the default profile ID for a specific tool type.
### Response
#### Success (200 OK)
```json
{
"tool_type_id": "code-server",
"profile_id": "profile-uuid-1"
}
```
---
## GET /config-profiles/{profile_id}/includes
**Description:** List all includes for a config profile.
### Response
#### Success (200 OK)
```json
{
"includes": [
{
"id": "uuid",
"profile_id": "uuid",
"included_profile_id": "uuid",
"included_profile_name": "base-profile",
"order_index": 0,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
}
```
---
## POST /config-profiles/{profile_id}/includes
**Description:** Add an include to a config profile.
### Request
#### Request Body
```json
{
"included_profile_id": "uuid",
"order_index": 0
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `included_profile_id` | `string` | Yes | UUID of the profile to include |
| `order_index` | `integer` | No | Order for include resolution (default: 0) |
### Response
#### Success (201 Created)
Returns created include.
#### Error (400 Bad Request)
```json
{
"detail": "a profile cannot include itself"
}
```
```json
{
"detail": "adding this include would create a circular reference"
}
```
---
## PUT /config-profiles/{profile_id}/includes/{include_id}
**Description:** Update the order index of a profile include.
### Request
#### Request Body
```json
{
"order_index": 5
}
```
### Response
#### Success (200 OK)
Returns updated include.
---
## DELETE /config-profiles/{profile_id}/includes/{include_id}
**Description:** Remove an include from a config profile.
### Response
#### Success (204 No Content)
---
## GET /config-profiles/{profile_id}/mounts
**Description:** List all mounts for a config profile.
### Response
#### Success (200 OK)
```json
{
"mounts": [
{
"id": "uuid",
"profile_id": "uuid",
"target_path": "/etc/config",
"mode": "rw",
"files": {"test.txt": "hello"},
"order_index": 0,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
}
```
---
## POST /config-profiles/{profile_id}/mounts
**Description:** Add a mount to a config profile.
### Request
#### Request Body
```json
{
"target_path": "/etc/config",
"mode": "rw",
"files": {"test.txt": "hello"},
"order_index": 0
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `target_path` | `string` | Yes | Absolute target path (must start with /) |
| `mode` | `string` | No | Mount mode: "rw" or "ro" (default: "rw") |
| `files` | `object` | No | Files as {path: content} |
| `order_index` | `integer` | No | Order for mount resolution (default: 0) |
### Response
#### Success (201 Created)
Returns created mount.
#### Error (422 Unprocessable Entity)
```json
{
"detail": "Target path must be absolute (start with /)"
}
```
---
## PUT /config-profiles/{profile_id}/mounts/{mount_id}
**Description:** Update a mount in a config profile.
### Request
#### Request Body
```json
{
"target_path": "/new/path",
"files": {"test.txt": "updated"},
"order_index": 2
}
```
### Response
#### Success (200 OK)
Returns updated mount.
---
## DELETE /config-profiles/{profile_id}/mounts/{mount_id}
**Description:** Remove a mount from a config profile.
### Response
#### Success (204 No Content)