/**
* Application Launcher Service - Frontend client
* Launches Unity3D, Resonite, VRChat, VRoid Studio, etc. with multi-desktop support
*/
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8354'
export interface LaunchAppRequest {
desktop_number?: number
project_path?: string
fullscreen?: boolean
monitor?: number
}
export interface LaunchAppResponse {
success: boolean
status?: 'launched' | 'already_running'
app_id: string
app_name?: string
pid?: number
desktop?: number
monitor?: number
error?: string
message?: string
suggested_env_var?: string
}
export interface AppStatus {
running: boolean
pid?: number
desktop?: number
monitor?: number
fullscreen?: boolean
}
class AppLauncherService {
private baseUrl: string
constructor(baseUrl: string = API_BASE_URL) {
this.baseUrl = baseUrl
}
/**
* Launch an application
*/
async launchApp(
appId: string,
options?: LaunchAppRequest
): Promise<LaunchAppResponse> {
try {
const response = await fetch(`${this.baseUrl}/api/apps/${appId}/launch`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(options || {}),
})
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `HTTP ${response.status}: ${errorText}`,
app_id: appId,
}
}
return await response.json()
} catch (error) {
console.error(`Failed to launch app ${appId}:`, error)
return {
success: false,
error: error instanceof Error ? error.message : String(error),
app_id: appId,
}
}
}
/**
* Stop a running application
*/
async stopApp(appId: string): Promise<LaunchAppResponse> {
try {
const response = await fetch(`${this.baseUrl}/api/apps/${appId}/stop`, {
method: 'POST',
})
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `HTTP ${response.status}: ${errorText}`,
app_id: appId,
}
}
return await response.json()
} catch (error) {
console.error(`Failed to stop app ${appId}:`, error)
return {
success: false,
error: error instanceof Error ? error.message : String(error),
app_id: appId,
}
}
}
/**
* Get status of all applications
*/
async getStatus(): Promise<Record<string, AppStatus>> {
try {
const response = await fetch(`${this.baseUrl}/api/apps/status`)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const data = await response.json()
return data.applications || {}
} catch (error) {
console.error('Failed to get app status:', error)
return {}
}
}
// Convenience methods for specific apps
async launchUnity3D(projectPath?: string, desktop?: number, monitor?: number) {
return this.launchApp('unity3d', {
project_path: projectPath,
desktop_number: desktop,
monitor: monitor,
})
}
async launchResonite(desktop?: number, monitor?: number) {
return this.launchApp('resonite', {
desktop_number: desktop,
monitor: monitor,
})
}
async launchVRChat(desktop?: number, monitor?: number) {
return this.launchApp('vrchat', {
desktop_number: desktop,
monitor: monitor,
})
}
async launchVRoid(desktop?: number, monitor?: number) {
return this.launchApp('vroid', {
desktop_number: desktop,
monitor: monitor,
})
}
}
export const appLauncherService = new AppLauncherService()
export default appLauncherService