Add transport-agnostic RPC layer for proxy app

Introduces yaak-rpc (shared RPC infrastructure) and yaak-proxy-lib
(proxy app logic decoupled from any frontend). A single Tauri command
dispatches all RPC calls through a typed router, with TypeScript types
auto-generated via ts-rs and a generic rpc() function for type-safe
calls from the frontend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gregory Schier
2026-03-08 11:27:51 -07:00
parent 4c37e62146
commit 6f8c4c06bb
11 changed files with 332 additions and 67 deletions

View File

@@ -0,0 +1,13 @@
[package]
name = "yaak-proxy-lib"
version = "0.0.0"
edition = "2024"
authors = ["Gregory Schier"]
publish = false
[dependencies]
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
ts-rs = { workspace = true }
yaak-proxy = { workspace = true }
yaak-rpc = { workspace = true }

View File

@@ -0,0 +1,9 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ProxyStartRequest = { port: number | null, };
export type ProxyStartResponse = { port: number, alreadyRunning: boolean, };
export type ProxyStopRequest = Record<string, never>;
export type RpcSchema = { proxy_start: [ProxyStartRequest, ProxyStartResponse], proxy_stop: [ProxyStopRequest, boolean], };

View File

@@ -0,0 +1,81 @@
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use ts_rs::TS;
use yaak_proxy::ProxyHandle;
use yaak_rpc::{RpcError, define_rpc};
// -- Context shared across all RPC handlers --
pub struct ProxyCtx {
handle: Mutex<Option<ProxyHandle>>,
}
impl ProxyCtx {
pub fn new() -> Self {
Self {
handle: Mutex::new(None),
}
}
}
// -- Request/response types --
#[derive(Deserialize, TS)]
#[ts(export, export_to = "gen_rpc.ts")]
pub struct ProxyStartRequest {
pub port: Option<u16>,
}
#[derive(Serialize, TS)]
#[ts(export, export_to = "gen_rpc.ts")]
#[serde(rename_all = "camelCase")]
pub struct ProxyStartResponse {
pub port: u16,
pub already_running: bool,
}
#[derive(Deserialize, TS)]
#[ts(export, export_to = "gen_rpc.ts")]
pub struct ProxyStopRequest {}
// -- Handlers --
fn proxy_start(ctx: &ProxyCtx, req: ProxyStartRequest) -> Result<ProxyStartResponse, RpcError> {
let mut handle = ctx
.handle
.lock()
.map_err(|_| RpcError { message: "lock poisoned".into() })?;
if let Some(existing) = handle.as_ref() {
return Ok(ProxyStartResponse {
port: existing.port,
already_running: true,
});
}
let proxy_handle = yaak_proxy::start_proxy(req.port.unwrap_or(0))
.map_err(|e| RpcError { message: e })?;
let port = proxy_handle.port;
*handle = Some(proxy_handle);
Ok(ProxyStartResponse {
port,
already_running: false,
})
}
fn proxy_stop(ctx: &ProxyCtx, _req: ProxyStopRequest) -> Result<bool, RpcError> {
let mut handle = ctx
.handle
.lock()
.map_err(|_| RpcError { message: "lock poisoned".into() })?;
Ok(handle.take().is_some())
}
// -- Router + Schema --
define_rpc! {
ProxyCtx;
"proxy_start" => proxy_start(ProxyStartRequest) -> ProxyStartResponse,
"proxy_stop" => proxy_stop(ProxyStopRequest) -> bool,
}