feat(v2): add frontend remote machine integration

remote-bridge.ts adapter for machine management IPC. machines.svelte.ts
store for remote machine state. Layout store extended with
remoteMachineId on Pane interface. agent-bridge.ts and pty-bridge.ts
route to remote commands when remoteMachineId is set. SettingsDialog
gains Remote Machines section. Sidebar auto-groups remote panes by
machine label.
This commit is contained in:
Hibryda 2026-03-06 19:05:53 +01:00
parent 0b39133d66
commit 5503340e87
7 changed files with 481 additions and 5 deletions

View file

@ -11,13 +11,21 @@ export interface AgentQueryOptions {
max_turns?: number;
max_budget_usd?: number;
resume_session_id?: string;
remote_machine_id?: string;
}
export async function queryAgent(options: AgentQueryOptions): Promise<void> {
if (options.remote_machine_id) {
const { remote_machine_id: machineId, ...agentOptions } = options;
return invoke('remote_agent_query', { machineId, options: agentOptions });
}
return invoke('agent_query', { options });
}
export async function stopAgent(sessionId: string): Promise<void> {
export async function stopAgent(sessionId: string, remoteMachineId?: string): Promise<void> {
if (remoteMachineId) {
return invoke('remote_agent_stop', { machineId: remoteMachineId, sessionId });
}
return invoke('agent_stop', { sessionId });
}

View file

@ -7,21 +7,35 @@ export interface PtyOptions {
args?: string[];
cols?: number;
rows?: number;
remote_machine_id?: string;
}
export async function spawnPty(options: PtyOptions): Promise<string> {
if (options.remote_machine_id) {
const { remote_machine_id: machineId, ...ptyOptions } = options;
return invoke<string>('remote_pty_spawn', { machineId, options: ptyOptions });
}
return invoke<string>('pty_spawn', { options });
}
export async function writePty(id: string, data: string): Promise<void> {
export async function writePty(id: string, data: string, remoteMachineId?: string): Promise<void> {
if (remoteMachineId) {
return invoke('remote_pty_write', { machineId: remoteMachineId, id, data });
}
return invoke('pty_write', { id, data });
}
export async function resizePty(id: string, cols: number, rows: number): Promise<void> {
export async function resizePty(id: string, cols: number, rows: number, remoteMachineId?: string): Promise<void> {
if (remoteMachineId) {
return invoke('remote_pty_resize', { machineId: remoteMachineId, id, cols, rows });
}
return invoke('pty_resize', { id, cols, rows });
}
export async function killPty(id: string): Promise<void> {
export async function killPty(id: string, remoteMachineId?: string): Promise<void> {
if (remoteMachineId) {
return invoke('remote_pty_kill', { machineId: remoteMachineId, id });
}
return invoke('pty_kill', { id });
}

View file

@ -0,0 +1,122 @@
// Remote Machine Bridge — Tauri IPC adapter for multi-machine management
import { invoke } from '@tauri-apps/api/core';
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
export interface RemoteMachineConfig {
label: string;
url: string;
token: string;
auto_connect: boolean;
}
export interface RemoteMachineInfo {
id: string;
label: string;
url: string;
status: string;
auto_connect: boolean;
}
// --- Machine management ---
export async function listRemoteMachines(): Promise<RemoteMachineInfo[]> {
return invoke('remote_list');
}
export async function addRemoteMachine(config: RemoteMachineConfig): Promise<string> {
return invoke('remote_add', { config });
}
export async function removeRemoteMachine(machineId: string): Promise<void> {
return invoke('remote_remove', { machineId });
}
export async function connectRemoteMachine(machineId: string): Promise<void> {
return invoke('remote_connect', { machineId });
}
export async function disconnectRemoteMachine(machineId: string): Promise<void> {
return invoke('remote_disconnect', { machineId });
}
// --- Remote event listeners ---
export interface RemoteSidecarMessage {
machineId: string;
sessionId?: string;
event?: Record<string, unknown>;
}
export interface RemotePtyData {
machineId: string;
sessionId?: string;
data?: string;
}
export interface RemotePtyExit {
machineId: string;
sessionId?: string;
}
export interface RemoteMachineEvent {
machineId: string;
payload?: unknown;
error?: unknown;
}
export async function onRemoteSidecarMessage(
callback: (msg: RemoteSidecarMessage) => void,
): Promise<UnlistenFn> {
return listen<RemoteSidecarMessage>('remote-sidecar-message', (event) => {
callback(event.payload);
});
}
export async function onRemotePtyData(
callback: (msg: RemotePtyData) => void,
): Promise<UnlistenFn> {
return listen<RemotePtyData>('remote-pty-data', (event) => {
callback(event.payload);
});
}
export async function onRemotePtyExit(
callback: (msg: RemotePtyExit) => void,
): Promise<UnlistenFn> {
return listen<RemotePtyExit>('remote-pty-exit', (event) => {
callback(event.payload);
});
}
export async function onRemoteMachineReady(
callback: (msg: RemoteMachineEvent) => void,
): Promise<UnlistenFn> {
return listen<RemoteMachineEvent>('remote-machine-ready', (event) => {
callback(event.payload);
});
}
export async function onRemoteMachineDisconnected(
callback: (msg: RemoteMachineEvent) => void,
): Promise<UnlistenFn> {
return listen<RemoteMachineEvent>('remote-machine-disconnected', (event) => {
callback(event.payload);
});
}
export async function onRemoteStateSync(
callback: (msg: RemoteMachineEvent) => void,
): Promise<UnlistenFn> {
return listen<RemoteMachineEvent>('remote-state-sync', (event) => {
callback(event.payload);
});
}
export async function onRemoteError(
callback: (msg: RemoteMachineEvent) => void,
): Promise<UnlistenFn> {
return listen<RemoteMachineEvent>('remote-error', (event) => {
callback(event.payload);
});
}