mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
9.2 KiB
9.2 KiB
OpenAPI Migration Plan for VibeTunnel
Overview
This document outlines the plan to adopt OpenAPI 3.1 for VibeTunnel's REST API to achieve type safety and consistency between the TypeScript server and Swift clients.
Goals
- Single source of truth - Define API contracts once in OpenAPI spec
- Type safety - Generate TypeScript and Swift types from the spec
- Eliminate inconsistencies - Fix type mismatches between platforms
- API documentation - Auto-generate API docs from the spec
- Gradual adoption - Migrate endpoint by endpoint without breaking changes
Current Issues
- Session types differ completely between Mac app and server
- Git repository types have different field names and optional/required mismatches
- No standardized error response format
- Manual type definitions duplicated across platforms
- Runtime parsing errors due to type mismatches
Implementation Plan
Phase 1: Setup and Infrastructure (Week 1)
1.1 Install Dependencies
# In web directory
pnpm add -D @hey-api/openapi-ts @apidevtools/swagger-cli @stoplight/spectral-cli
1.2 Create Initial OpenAPI Spec
Create web/openapi/openapi.yaml:
openapi: 3.1.0
info:
title: VibeTunnel API
version: 1.0.0
description: Terminal sharing and remote access API
servers:
- url: http://localhost:4020
description: Local development server
1.3 Setup Code Generation
TypeScript Generation (web/package.json):
{
"scripts": {
"generate:api": "openapi-ts -i openapi/openapi.yaml -o src/generated/api",
"validate:api": "spectral lint openapi/openapi.yaml",
"prebuild": "npm run generate:api"
}
}
Swift Generation (Xcode Build Phase):
- Add
swift-openapi-generatorto Package.swift - Add build phase to run before compilation:
cd "$SRCROOT/../web" && \
swift-openapi-generator generate \
openapi/openapi.yaml \
--mode types \
--mode client \
--output-directory "$SRCROOT/Generated/OpenAPI"
1.4 Create Shared Components
Define reusable schemas in web/openapi/components/:
# components/errors.yaml
ErrorResponse:
type: object
required: [error, timestamp]
properties:
error:
type: string
description: Human-readable error message
code:
type: string
description: Machine-readable error code
enum: [
'INVALID_REQUEST',
'NOT_FOUND',
'UNAUTHORIZED',
'SERVER_ERROR'
]
timestamp:
type: string
format: date-time
Phase 2: Migrate Git Endpoints (Week 2)
Start with Git endpoints as they're well-defined and isolated.
2.1 Define Git Schemas
# openapi/paths/git.yaml
/api/git/repository-info:
get:
operationId: getRepositoryInfo
tags: [git]
parameters:
- name: path
in: query
required: true
schema:
type: string
responses:
'200':
description: Repository information
content:
application/json:
schema:
$ref: '../components/schemas.yaml#/GitRepositoryInfo'
# components/schemas.yaml
GitRepositoryInfo:
type: object
required: [isGitRepo, hasChanges, modifiedCount, untrackedCount, stagedCount, addedCount, deletedCount, aheadCount, behindCount, hasUpstream]
properties:
isGitRepo:
type: boolean
repoPath:
type: string
currentBranch:
type: string
nullable: true
remoteUrl:
type: string
nullable: true
githubUrl:
type: string
nullable: true
hasChanges:
type: boolean
modifiedCount:
type: integer
minimum: 0
untrackedCount:
type: integer
minimum: 0
stagedCount:
type: integer
minimum: 0
addedCount:
type: integer
minimum: 0
deletedCount:
type: integer
minimum: 0
aheadCount:
type: integer
minimum: 0
behindCount:
type: integer
minimum: 0
hasUpstream:
type: boolean
2.2 Update Server Implementation
// src/server/routes/git.ts
import { paths } from '../../generated/api';
type GitRepositoryInfo = paths['/api/git/repository-info']['get']['responses']['200']['content']['application/json'];
router.get('/git/repository-info', async (req, res) => {
const response: GitRepositoryInfo = {
isGitRepo: true,
repoPath: result.repoPath,
// ... ensure all required fields are included
};
res.json(response);
});
2.3 Update Mac Client
// Use generated types
import OpenAPIGenerated
let response = try await client.getRepositoryInfo(path: filePath)
let info = response.body.json // Fully typed!
Phase 3: Migrate Session Endpoints (Week 3)
Session endpoints are more complex due to WebSocket integration.
3.1 Standardize Session Types
SessionInfo:
type: object
required: [id, name, workingDir, status, createdAt, pid]
properties:
id:
type: string
format: uuid
name:
type: string
workingDir:
type: string
status:
type: string
enum: [starting, running, exited]
exitCode:
type: integer
nullable: true
createdAt:
type: string
format: date-time
lastActivity:
type: string
format: date-time
pid:
type: integer
nullable: true
command:
type: array
items:
type: string
3.2 Create Session Operations
/api/sessions:
get:
operationId: listSessions
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/SessionInfo'
post:
operationId: createSession
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateSessionRequest'
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/SessionInfo'
Phase 4: Runtime Validation (Week 4)
4.1 Add Request Validation Middleware
// src/server/middleware/openapi-validator.ts
import { OpenAPIValidator } from 'express-openapi-validator';
export const openapiValidator = OpenAPIValidator.middleware({
apiSpec: './openapi/openapi.yaml',
validateRequests: true,
validateResponses: true,
});
// Apply to routes
app.use('/api', openapiValidator);
4.2 Add Response Validation in Development
// src/server/utils/validated-response.ts
export function validatedJson<T>(res: Response, data: T): void {
if (process.env.NODE_ENV === 'development') {
// Validate against OpenAPI schema
validateResponse(res.req, data);
}
res.json(data);
}
Phase 5: Documentation and Testing (Week 5)
5.1 Generate API Documentation
# Add to package.json
"docs:api": "npx @redocly/cli build-docs openapi/openapi.yaml -o dist/api-docs.html"
5.2 Add Contract Tests
// src/test/contract/git-api.test.ts
import { matchesSchema } from './schema-matcher';
test('GET /api/git/repository-info matches schema', async () => {
const response = await request(app)
.get('/api/git/repository-info')
.query({ path: '/test/repo' });
expect(response.body).toMatchSchema('GitRepositoryInfo');
});
Migration Checklist
Endpoints to Migrate
-
Git APIs (Phase 2)
- GET /api/git/repo-info
- GET /api/git/repository-info
- GET /api/git/remote
- GET /api/git/status
- POST /api/git/event
- GET /api/git/follow
-
Session APIs (Phase 3)
- GET /api/sessions
- POST /api/sessions
- GET /api/sessions/:id
- DELETE /api/sessions/:id
- POST /api/sessions/:id/resize
- POST /api/sessions/:id/input
- GET /api/sessions/:id/stream (SSE)
-
Repository APIs (Phase 4)
- GET /api/repositories/discover
- GET /api/repositories/branches
-
Worktree APIs (Phase 4)
- GET /api/worktrees
- POST /api/worktrees
- DELETE /api/worktrees/:branch
- POST /api/worktrees/switch
Success Metrics
- Zero runtime type errors between Mac app and server
- 100% API documentation coverage
- Contract tests for all endpoints
- Reduced code - Remove manual type definitions
- Developer velocity - Faster API development with code generation
Long-term Considerations
Future Enhancements
- GraphQL Gateway - Add GraphQL layer on top of REST for complex queries
- API Versioning - Use OpenAPI to manage v1/v2 migrations
- Client SDKs - Generate SDKs for other platforms (iOS, CLI tools)
- Mock Server - Use OpenAPI spec to run mock server for testing
Breaking Changes
When making breaking changes:
- Version the API (e.g., /api/v2/)
- Deprecate old endpoints with sunset dates
- Generate migration guides from schema differences