#[cfg(target_arch = "wasm32")]
pub(crate) use wit::*;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) use crate::testing::host_calls::*;
#[cfg(target_arch = "wasm32")]
mod wit {
use alloc::string::String;
use alloc::vec::Vec;
use crate::api::Target;
use crate::api::account::{AccountState, Machine, Pane, Project};
use crate::api::capabilities::Capabilities;
use crate::api::view::Theme;
use crate::bindings::daemon::standard::plugin::{
daemon_context, daemon_net, daemon_panes, daemon_process, daemon_watch,
};
use crate::bindings::ui::standard::plugin as wit;
use crate::error::{Error, Result};
use crate::geometry::{Geometry, Rect};
use crate::surface::{Model, RegionLayout};
fn error(error: wit::types::CallError) -> Error {
error.into()
}
fn target(to: &Target) -> wit::types::Target {
match to {
Target::All => wit::types::Target::All,
Target::Viewers => wit::types::Target::Viewers,
Target::Daemons => wit::types::Target::Daemons,
Target::Machine(id) => wit::types::Target::Machine(id.clone()),
Target::Singleton => wit::types::Target::Singleton,
}
}
pub(crate) fn values_get(key: &str) -> Result<Option<String>> {
wit::values::get(key).map_err(error)
}
pub(crate) fn values_set(key: &str, value: &str) -> Result<()> {
wit::values::set(key, value).map_err(error)
}
pub(crate) fn values_delete(key: &str) -> Result<()> {
wit::values::delete(key).map_err(error)
}
pub(crate) fn values_keys(prefix: &str) -> Result<Vec<String>> {
wit::values::keys(prefix).map_err(error)
}
pub(crate) fn values_watch(prefix: &str) -> Result<()> {
wit::values::watch(prefix).map_err(error)
}
pub(crate) fn live_publish(key: &str, payload: &str) -> Result<()> {
wit::live::publish(key, payload).map_err(error)
}
pub(crate) fn live_delete(key: &str) -> Result<()> {
wit::live::delete(key).map_err(error)
}
pub(crate) fn live_subscribe(prefix: &str) -> Result<()> {
wit::live::subscribe(prefix).map_err(error)
}
pub(crate) fn live_unsubscribe(prefix: &str) -> Result<()> {
wit::live::unsubscribe(prefix).map_err(error)
}
pub(crate) fn events_emit(name: &str, payload: &str, to: &Target) -> Result<()> {
wit::events::emit(name, payload, &target(to)).map_err(error)
}
pub(crate) fn events_on(pattern: &str) -> Result<()> {
wit::events::on(pattern).map_err(error)
}
pub(crate) fn events_off(pattern: &str) -> Result<()> {
wit::events::off(pattern).map_err(error)
}
pub(crate) fn call(
method: &str,
payload: &str,
to: &Target,
timeout_ms: Option<u32>,
) -> Result<String> {
wit::calls::call(method, payload, &target(to), timeout_ms).map_err(error)
}
pub(crate) fn call_send(method: &str, payload: &str, to: &Target) -> Result<()> {
wit::calls::send(method, payload, &target(to)).map_err(error)
}
pub(crate) fn call_async(
method: &str,
payload: &str,
to: &Target,
timeout_ms: Option<u32>,
) -> Result<u64> {
wit::calls::call_async(method, payload, &target(to), timeout_ms).map_err(error)
}
pub(crate) fn config() -> String {
wit::config::get()
}
pub(crate) fn health_set(state: crate::api::health::Health, message: &str) {
use crate::api::health::Health;
let state = match state {
Health::Ok => wit::health::HealthState::Ok,
Health::Degraded => wit::health::HealthState::Degraded,
Health::Failed => wit::health::HealthState::Failed,
};
wit::health::set(state, message);
}
pub(crate) fn secret(name: &str) -> Result<Option<String>> {
wit::secrets::get(name).map_err(error)
}
pub(crate) fn pane(pane: wit::account::Pane) -> Pane {
pane.into()
}
pub(crate) fn account_state() -> Result<AccountState> {
let state = wit::account::state().map_err(error)?;
Ok(AccountState {
machines: state
.machines
.into_iter()
.map(|machine| Machine {
id: machine.id,
name: machine.name,
online: machine.online,
})
.collect(),
projects: state
.projects
.into_iter()
.map(|project| Project {
id: project.id,
name: project.name,
machine: project.machine,
path: project.path,
})
.collect(),
panes: state.panes.into_iter().map(pane).collect(),
})
}
pub(crate) fn account_watch() -> Result<()> {
wit::account::watch().map_err(error)
}
pub(crate) fn claim(key: &str, ttl_ms: u32) -> Result<bool> {
wit::claims::claim(key, ttl_ms).map_err(error)
}
pub(crate) fn release(key: &str) -> Result<()> {
wit::claims::release(key).map_err(error)
}
pub(crate) fn surface_layout(id: &str) -> Result<RegionLayout> {
let layout = wit::surface::layout(id).map_err(error)?;
let g = layout.geometry;
Ok(RegionLayout {
len: layout.len,
header_len: layout.header_len,
slot_len: layout.slot_len,
model: match layout.model {
wit::surface::Model::Cells => Model::Cells,
wit::surface::Model::Pixels => Model::Pixels,
},
geometry: Geometry {
cols: g.cols,
rows: g.rows,
px_w: g.px_w,
px_h: g.px_h,
cell_px_w: g.cell_px_w,
cell_px_h: g.cell_px_h,
},
})
}
pub(crate) fn surface_attach(id: &str, region: &[u8]) -> Result<()> {
wit::surface::attach(id, region).map_err(error)
}
pub(crate) fn surface_commit(id: &str, slot: u8, dirty: &[Rect]) -> Result<()> {
let dirty: Vec<wit::surface::Rect> = dirty
.iter()
.map(|rect| wit::surface::Rect {
x: rect.x,
y: rect.y,
w: rect.w,
h: rect.h,
})
.collect();
wit::surface::commit(id, slot, &dirty).map_err(error)
}
pub(crate) fn surface_detach(id: &str) -> Result<()> {
wit::surface::detach(id).map_err(error)
}
pub(crate) fn is_driving() -> bool {
wit::view::is_driving()
}
pub(crate) fn theme() -> Theme {
let theme = wit::view::theme();
let mut palette = crate::api::view::XTERM_PALETTE;
for (slot, colour) in palette.iter_mut().zip(&theme.palette) {
*slot = *colour;
}
Theme {
fg: theme.fg,
bg: theme.bg,
recede_fg: theme.recede_fg,
recede_bg: theme.recede_bg,
accent: theme.accent,
palette,
}
}
pub(crate) fn surface_machine(surface: &str) -> Option<String> {
wit::view::surface_machine(surface)
}
pub(crate) fn surface_project(surface: &str) -> Option<String> {
wit::view::surface_project(surface)
}
pub(crate) fn surface_tint(surface: &str) -> Option<u32> {
wit::view::surface_tint(surface)
}
pub(crate) fn identity_tint(id: &str) -> Option<u32> {
wit::view::identity_tint(id)
}
pub(crate) fn surface_request_size(id: &str, cols: u32, rows: u32) -> Result<()> {
wit::surface::request_size(id, cols, rows).map_err(error)
}
pub(crate) fn surface_set_label(id: &str, label: Option<&str>) -> Result<()> {
wit::surface::set_label(id, label).map_err(error)
}
pub(crate) fn surface_set_caret(id: &str, caret: Option<(u32, u32)>) -> Result<()> {
let caret = caret.map(|(col, row)| wit::surface::Caret { col, row });
wit::surface::set_caret(id, caret).map_err(error)
}
pub(crate) fn capabilities() -> Capabilities {
let caps = wit::view::capabilities();
Capabilities {
graphics: caps.graphics,
kitty: caps.graphics,
frame_rate: caps.fps,
cell_px: (caps.cell_px_w, caps.cell_px_h),
pixel_scale: caps.pixel_scale.max(1),
}
}
pub(crate) fn focused_pane() -> Option<String> {
wit::view::focused_pane()
}
pub(crate) fn surface_pane(surface: &str) -> Option<crate::api::view::PaneRef> {
wit::view::surface_pane(surface).map(|pane| crate::api::view::PaneRef {
id: pane.id,
generation: pane.generation,
})
}
pub(crate) fn request_frame() {
wit::view::request_frame();
}
pub(crate) fn wake_at(at_ms: u64) {
wit::view::wake_at(at_ms);
}
pub(crate) fn machine_id() -> Option<String> {
wit::view::machine_id()
}
pub(crate) fn instance_id() -> String {
wit::view::instance_id()
}
pub(crate) fn wall_ms() -> u64 {
wit::view::wall_ms()
}
pub(crate) fn utc_offset_minutes() -> i32 {
wit::view::utc_offset_minutes()
}
pub(crate) fn time_zone() -> Option<String> {
wit::view::time_zone()
}
pub(crate) fn daemon_machine_id() -> String {
daemon_context::machine_id()
}
pub(crate) fn daemon_account_id() -> Option<String> {
daemon_context::account_id()
}
pub(crate) fn daemon_lease_epoch() -> Option<u64> {
daemon_context::lease_epoch()
}
pub(crate) fn daemon_release() -> Option<crate::daemon::Release> {
daemon_context::release().map(|release| crate::daemon::Release {
channel: release.channel,
version: release.version,
git_sha: release.git_sha,
})
}
pub(crate) fn daemon_environment() -> Vec<(String, String)> {
daemon_context::environment()
}
pub(crate) fn url_open(url: &str) -> Result<()> {
wit::url::open(url).map_err(error)
}
pub(crate) fn surface_open(id: &str) -> Result<()> {
wit::surface::open(id).map_err(error)
}
pub(crate) fn focus_pane(pane: &str) -> Result<()> {
wit::view::focus_pane(pane).map_err(error)
}
pub(crate) fn surface_close(id: &str) -> Result<()> {
wit::surface::close(id).map_err(error)
}
pub(crate) fn process_spawn(program: &str, args: &[String], cwd: Option<&str>) -> Result<u32> {
daemon_process::spawn(program, args, cwd)
.map(|spawned| spawned.pid)
.map_err(error)
}
pub(crate) fn process_run(command: &crate::daemon::process::Command) -> Result<u32> {
use crate::daemon::process::Stdio;
let stdio = |stdio: Stdio| match stdio {
Stdio::Null => daemon_process::Stdio::Null,
Stdio::Piped => daemon_process::Stdio::Piped,
Stdio::Log => daemon_process::Stdio::Log,
};
daemon_process::run(&daemon_process::Command {
program: command.program.clone(),
args: command.args.clone(),
env: command.env.clone(),
env_remove: command.env_remove.clone(),
clear_env: command.clear_env,
cwd: command.cwd.clone(),
stdin: stdio(command.stdin),
stdout: stdio(command.stdout),
stderr: stdio(command.stderr),
})
.map(|spawned| spawned.pid)
.map_err(error)
}
pub(crate) fn process_write(pid: u32, bytes: &[u8]) -> Result<()> {
daemon_process::write(pid, bytes).map_err(error)
}
pub(crate) fn process_close_stdin(pid: u32) -> Result<()> {
daemon_process::close_stdin(pid).map_err(error)
}
pub(crate) fn process_try_wait(pid: u32) -> Result<Option<i32>> {
daemon_process::try_wait(pid).map_err(error)
}
pub(crate) fn process_wait(pid: u32) -> Result<i32> {
daemon_process::wait(pid).map_err(error)
}
pub(crate) fn process_kill(pid: u32) -> Result<()> {
daemon_process::kill(pid).map_err(error)
}
pub(crate) fn watch(path: &str, options: &crate::daemon::watch::Options) -> Result<u32> {
daemon_watch::watch(
path,
&daemon_watch::WatchOptions {
recursive: options.recursive,
exclude: options.exclude.clone(),
},
)
.map_err(error)
}
pub(crate) fn unwatch(handle: u32) -> Result<()> {
daemon_watch::unwatch(handle).map_err(error)
}
pub(crate) fn websocket_open(url: &str, headers: &[(String, String)]) -> Result<u32> {
daemon_net::websocket_open(url, headers).map_err(error)
}
pub(crate) fn websocket_send(socket: u32, text: &str) -> Result<()> {
daemon_net::websocket_send(socket, text).map_err(error)
}
pub(crate) fn websocket_close(socket: u32) {
daemon_net::websocket_close(socket);
}
pub(crate) fn panes() -> Result<Vec<Pane>> {
Ok(daemon_panes::panes()
.map_err(error)?
.into_iter()
.map(pane)
.collect())
}
pub(crate) fn pane_create(
cwd: &str,
command: Option<&str>,
env: &[(String, String)],
) -> Result<(String, u64)> {
daemon_panes::create(cwd, command, env)
.map(|created| (created.id, created.generation))
.map_err(error)
}
pub(crate) fn pane_create_with(
cwd: &str,
command: Option<&str>,
env: &[(String, String)],
title: Option<&str>,
) -> Result<(String, u64)> {
daemon_panes::create_with(&daemon_panes::NewPane {
cwd: cwd.into(),
command: command.map(Into::into),
env: env.to_vec(),
title: title.map(Into::into),
})
.map(|created| (created.id, created.generation))
.map_err(error)
}
pub(crate) fn pane_input(pane: &str, bytes: &[u8]) -> Result<()> {
daemon_panes::input(pane, bytes).map_err(error)
}
pub(crate) fn pane_close(pane: &str) -> Result<()> {
daemon_panes::close(pane).map_err(error)
}
pub(crate) fn panes_subscribe(on: bool) -> Result<()> {
if on {
daemon_panes::subscribe().map_err(error)
} else {
daemon_panes::unsubscribe().map_err(error)
}
}
pub(crate) fn pane_wait(pane: &str, timeout_ms: u32) -> Result<bool> {
daemon_panes::wait(pane, timeout_ms).map_err(error)
}
}
impl From<crate::bindings::ui::standard::plugin::account::Pane> for crate::api::account::Pane {
fn from(pane: crate::bindings::ui::standard::plugin::account::Pane) -> Self {
use crate::api::account::AgentStatus;
use crate::bindings::ui::standard::plugin::account::AgentStatus as Wit;
Self {
id: pane.id,
generation: pane.generation,
machine: pane.machine,
project: pane.project,
title: pane.title,
cwd: pane.cwd,
cols: pane.cols,
rows: pane.rows,
program: pane.program,
agent: pane.agent,
agent_status: match pane.agent_status {
Wit::None => AgentStatus::None,
Wit::Working => AgentStatus::Working,
Wit::Idle => AgentStatus::Idle,
Wit::WaitingForInput => AgentStatus::WaitingForInput,
},
}
}
}
impl From<crate::bindings::ui::standard::plugin::types::CallError> for crate::Error {
fn from(error: crate::bindings::ui::standard::plugin::types::CallError) -> Self {
use crate::bindings::ui::standard::plugin::types::CallError;
match error {
CallError::GrantDenied(grant) => Self::GrantDenied { grant },
CallError::RateLimited => Self::RateLimited,
CallError::Unavailable(reason) => Self::Unavailable(reason),
CallError::Invalid(reason) => Self::Invalid(reason),
}
}
}
impl From<crate::Error> for crate::bindings::ui::standard::plugin::types::CallError {
fn from(error: crate::Error) -> Self {
use crate::bindings::ui::standard::plugin::types::CallError;
match error {
crate::Error::GrantDenied { grant } => CallError::GrantDenied(grant),
crate::Error::RateLimited => CallError::RateLimited,
crate::Error::Unavailable(reason) => CallError::Unavailable(reason),
other => CallError::Invalid(alloc::format!("{other}")),
}
}
}