Major restructuring: - Split monolithic gatewayStore into 5 focused stores: - connectionStore: WebSocket connection and gateway lifecycle - configStore: quickConfig, workspaceInfo, MCP services - agentStore: clones, usage stats, agent management - handStore: hands, approvals, triggers, hand runs - workflowStore: workflows, workflow runs, execution - Update all components to use new stores with selector pattern - Remove
232 lines
5.8 KiB
TypeScript
232 lines
5.8 KiB
TypeScript
/**
|
|
* Viking Server Manager - Local OpenViking Server Management
|
|
*
|
|
* Manages a local OpenViking server instance for privacy-first deployment.
|
|
* All data is stored locally in ~/.openviking/ - nothing is uploaded to remote servers.
|
|
*
|
|
* Usage:
|
|
* const manager = getVikingServerManager();
|
|
*
|
|
* // Check server status
|
|
* const status = await manager.getStatus();
|
|
*
|
|
* // Start server if not running
|
|
* if (!status.running) {
|
|
* await manager.start();
|
|
* }
|
|
*
|
|
* // Server is now available at http://127.0.0.1:1933
|
|
*/
|
|
|
|
import { invoke } from '@tauri-apps/api/core';
|
|
|
|
// === Types ===
|
|
|
|
export interface VikingServerStatus {
|
|
running: boolean;
|
|
port: number;
|
|
pid?: number;
|
|
dataDir?: string;
|
|
version?: string;
|
|
error?: string;
|
|
}
|
|
|
|
export interface VikingServerConfig {
|
|
port?: number;
|
|
dataDir?: string;
|
|
configFile?: string;
|
|
}
|
|
|
|
// === Default Configuration ===
|
|
|
|
const DEFAULT_CONFIG: Required<VikingServerConfig> = {
|
|
port: 1933,
|
|
dataDir: '', // Will use default ~/.openviking/workspace
|
|
configFile: '', // Will use default ~/.openviking/ov.conf
|
|
};
|
|
|
|
// === Server Manager Class ===
|
|
|
|
export class VikingServerManager {
|
|
private status: VikingServerStatus | null = null;
|
|
private startPromise: Promise<VikingServerStatus> | null = null;
|
|
|
|
/**
|
|
* Get current server status
|
|
*/
|
|
async getStatus(): Promise<VikingServerStatus> {
|
|
try {
|
|
this.status = await invoke<VikingServerStatus>('viking_server_status');
|
|
return this.status;
|
|
} catch (err) {
|
|
console.error('[VikingServerManager] Failed to get status:', err);
|
|
return {
|
|
running: false,
|
|
port: DEFAULT_CONFIG.port,
|
|
error: err instanceof Error ? err.message : String(err),
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start local OpenViking server
|
|
* If server is already running, returns current status
|
|
*/
|
|
async start(config?: VikingServerConfig): Promise<VikingServerStatus> {
|
|
// Prevent concurrent start attempts
|
|
if (this.startPromise) {
|
|
return this.startPromise;
|
|
}
|
|
|
|
// Check if already running
|
|
const currentStatus = await this.getStatus();
|
|
if (currentStatus.running) {
|
|
console.log('[VikingServerManager] Server already running on port', currentStatus.port);
|
|
return currentStatus;
|
|
}
|
|
|
|
this.startPromise = this.doStart(config);
|
|
|
|
try {
|
|
const result = await this.startPromise;
|
|
return result;
|
|
} finally {
|
|
this.startPromise = null;
|
|
}
|
|
}
|
|
|
|
private async doStart(config?: VikingServerConfig): Promise<VikingServerStatus> {
|
|
const fullConfig = { ...DEFAULT_CONFIG, ...config };
|
|
|
|
console.log('[VikingServerManager] Starting local server on port', fullConfig.port);
|
|
|
|
try {
|
|
const status = await invoke<VikingServerStatus>('viking_server_start', {
|
|
config: {
|
|
port: fullConfig.port,
|
|
dataDir: fullConfig.dataDir || undefined,
|
|
configFile: fullConfig.configFile || undefined,
|
|
},
|
|
});
|
|
|
|
this.status = status;
|
|
console.log('[VikingServerManager] Server started:', status);
|
|
return status;
|
|
} catch (err) {
|
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
console.error('[VikingServerManager] Failed to start server:', errorMsg);
|
|
|
|
this.status = {
|
|
running: false,
|
|
port: fullConfig.port,
|
|
error: errorMsg,
|
|
};
|
|
|
|
return this.status;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop local OpenViking server
|
|
*/
|
|
async stop(): Promise<void> {
|
|
console.log('[VikingServerManager] Stopping server');
|
|
|
|
try {
|
|
await invoke('viking_server_stop');
|
|
this.status = {
|
|
running: false,
|
|
port: DEFAULT_CONFIG.port,
|
|
};
|
|
console.log('[VikingServerManager] Server stopped');
|
|
} catch (err) {
|
|
console.error('[VikingServerManager] Failed to stop server:', err);
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Restart local OpenViking server
|
|
*/
|
|
async restart(config?: VikingServerConfig): Promise<VikingServerStatus> {
|
|
console.log('[VikingServerManager] Restarting server');
|
|
|
|
try {
|
|
const status = await invoke<VikingServerStatus>('viking_server_restart', {
|
|
config: config ? {
|
|
port: config.port,
|
|
dataDir: config.dataDir,
|
|
configFile: config.configFile,
|
|
} : undefined,
|
|
});
|
|
|
|
this.status = status;
|
|
console.log('[VikingServerManager] Server restarted:', status);
|
|
return status;
|
|
} catch (err) {
|
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
console.error('[VikingServerManager] Failed to restart server:', errorMsg);
|
|
|
|
this.status = {
|
|
running: false,
|
|
port: config?.port || DEFAULT_CONFIG.port,
|
|
error: errorMsg,
|
|
};
|
|
|
|
return this.status;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure server is running, starting if necessary
|
|
* This is the main entry point for ensuring availability
|
|
*/
|
|
async ensureRunning(config?: VikingServerConfig): Promise<boolean> {
|
|
const status = await this.getStatus();
|
|
|
|
if (status.running) {
|
|
return true;
|
|
}
|
|
|
|
const startResult = await this.start(config);
|
|
return startResult.running;
|
|
}
|
|
|
|
/**
|
|
* Get the server URL for HTTP client connections
|
|
*/
|
|
getServerUrl(port?: number): string {
|
|
const actualPort = port || this.status?.port || DEFAULT_CONFIG.port;
|
|
return `http://127.0.0.1:${actualPort}`;
|
|
}
|
|
|
|
/**
|
|
* Check if server is available (cached status)
|
|
*/
|
|
isRunning(): boolean {
|
|
return this.status?.running ?? false;
|
|
}
|
|
|
|
/**
|
|
* Clear cached status (force refresh on next call)
|
|
*/
|
|
clearCache(): void {
|
|
this.status = null;
|
|
}
|
|
}
|
|
|
|
// === Singleton ===
|
|
|
|
let _instance: VikingServerManager | null = null;
|
|
|
|
export function getVikingServerManager(): VikingServerManager {
|
|
if (!_instance) {
|
|
_instance = new VikingServerManager();
|
|
}
|
|
return _instance;
|
|
}
|
|
|
|
export function resetVikingServerManager(): void {
|
|
_instance = null;
|
|
}
|