forked from 1b-agent/opencadstudio-upstream
Add the typed document facade and plugin IPC adapter. Prepare bulk edits before mutation, preserve undo and dirty state, keep profile elevations, validate numeric inputs and session bindings, and generate Python constructors with typed results. Reuse existing entity transforms and profile conversion. Compute mass properties and closed-surface bounds in cadkernel; pin its main revision 6a5e6fc2822f6af6b2611cd605b44437b35158f0. Keep plugin API metadata at version 6. Validation: 646 native application tests, 597 kernel tests, 12 document API tests, 91 plugin API tests, 3 Python tests, default/IPC feature builds and wasm32 check passed.
216 lines
7.2 KiB
Rust
216 lines
7.2 KiB
Rust
//! Request/response envelopes exchanged between the host and a plugin process.
|
|
//!
|
|
//! A single bidirectional socket is used. Each side sends either a request
|
|
//! (expecting a response) or a response (to a previous request). This lets the
|
|
//! host handle plugin RPCs inline while it waits for the result of a host→plugin
|
|
//! request such as `Dispatch`, avoiding the need for two sockets or threads.
|
|
//!
|
|
//! Compatibility rule: new variants are appended at the end of every public enum
|
|
//! (`HostRequest`, `HostResponse`, `PluginRequest`, `PluginResponse`,
|
|
//! `RunnerHandshake`) so older plugins keep their bincode discriminant indices.
|
|
//! The V4 additions are a separate frame layer in [`crate::ipc::v4`] and do not
|
|
//! alter these enums.
|
|
//!
|
|
//! The pre-shared runner token is delivered through [`PLUGIN_TOKEN_ENV`]
|
|
//! (`OCS_PLUGIN_TOKEN`). The runner must present the same token immediately
|
|
//! after connecting or the host closes the connection.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::host::{CommandSource, CommandStep};
|
|
use crate::manifest::ApiVersion;
|
|
use crate::ribbon::owned::{OwnedPluginManifest, OwnedRibbonGroup};
|
|
|
|
pub use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
|
pub use acadrust::{CadDocument, EntityType, Handle};
|
|
|
|
/// Events the host forwards to an active plugin `InteractiveCommand`.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum InteractiveEvent {
|
|
Point([f64; 3]),
|
|
Enter,
|
|
ObjectPick { handle: Handle, pt: [f64; 3] },
|
|
}
|
|
|
|
/// Initial handshake sent by the plugin runner immediately after connecting.
|
|
///
|
|
/// The runner proves it was spawned by this host by presenting a pre-shared
|
|
/// token delivered through the `OCS_PLUGIN_TOKEN` environment variable. A
|
|
/// mismatch causes the host to close the connection.
|
|
///
|
|
/// `TokenV4` is appended as the last variant so the existing `Token(String)`
|
|
/// variant keeps its bincode discriminant index (0), preserving V2/V3 wire
|
|
/// compatibility.
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum RunnerHandshake {
|
|
Token(String),
|
|
TokenV4 { token: String, protocol_version: u32 },
|
|
}
|
|
|
|
/// Environment variable through which the host passes the pre-shared
|
|
/// authentication token to the plugin runner child process.
|
|
pub const PLUGIN_TOKEN_ENV: &str = "OCS_PLUGIN_TOKEN";
|
|
|
|
/// Requests the host sends to the plugin runner.
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum HostRequest {
|
|
GetManifest,
|
|
GetRibbon,
|
|
Dispatch {
|
|
cmd: String,
|
|
},
|
|
InteractiveEvent {
|
|
command_id: u64,
|
|
event: InteractiveEvent,
|
|
},
|
|
GetPrompt {
|
|
command_id: u64,
|
|
},
|
|
NeedsEntityPick {
|
|
command_id: u64,
|
|
},
|
|
Shutdown,
|
|
ExecuteCode {
|
|
command_id: u64,
|
|
source: CommandSource,
|
|
code: String,
|
|
tab_index: usize,
|
|
},
|
|
}
|
|
|
|
/// Responses the plugin runner sends back for `HostRequest`.
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum HostResponse {
|
|
Bool(bool),
|
|
CommandStep(Box<CommandStep>),
|
|
Text(String),
|
|
Ribbon(Vec<OwnedRibbonGroup>),
|
|
Manifest(OwnedPluginManifest),
|
|
Error(String),
|
|
CodeExecutionResult(crate::host::ExecutionResult),
|
|
}
|
|
|
|
/// Requests the plugin runner sends to the host.
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum PluginRequest {
|
|
PushInfo(String),
|
|
PushOutput(String),
|
|
PushError(String),
|
|
AddEntity(EntityType),
|
|
/// Replace the existing entity carrying this entity's handle in place.
|
|
UpdateEntity(EntityType),
|
|
/// Delete the entity with `handle`.
|
|
RemoveEntity {
|
|
handle: Handle,
|
|
},
|
|
BumpGeometry,
|
|
ReadRecord {
|
|
handle: Handle,
|
|
app_name: String,
|
|
},
|
|
WriteRecord {
|
|
handle: Handle,
|
|
record: ExtendedDataRecord,
|
|
},
|
|
RemoveRecord {
|
|
handle: Handle,
|
|
app_name: String,
|
|
},
|
|
PushUndo {
|
|
label: String,
|
|
},
|
|
SetDirty,
|
|
StartInteractive {
|
|
command_id: u64,
|
|
},
|
|
DocumentSnapshot,
|
|
/// Ask the host to create/refresh a shared-memory document view and return
|
|
/// the file path + current version.
|
|
OpenDocumentView,
|
|
/// Add multiple entities in a single request.
|
|
AddEntities(Vec<EntityType>),
|
|
/// V4: ask the host to create/refresh a tab-keyed shared-memory document
|
|
/// view and return the file path + current version.
|
|
OpenDocumentViewV4 { tab_id: u64 },
|
|
/// V4: ask the host to close the tab-keyed shared-memory document view.
|
|
CloseDocumentViewV4 { tab_id: u64 },
|
|
/// V4: ask the host for the stable tab identifier of the active tab.
|
|
GetTabId,
|
|
/// V5: ask the host for the filesystem path of the document in `tab_id`.
|
|
DocumentPath { tab_id: u64 },
|
|
/// DocApi v2 (`ocs_doc_api`): carry one bincode-serialized `DocApiEnvelope`
|
|
/// (one write op OR a read-only query batch). Appended at the end to keep
|
|
/// existing discriminants stable. The host routes this to the DocApi executor.
|
|
/// `serde_bytes` keeps the payload a single length-prefixed bulk copy (the
|
|
/// outer frame would otherwise walk it byte-by-byte).
|
|
DocApiRequest { tab_id: u64, #[serde(with = "serde_bytes")] bytes: Vec<u8> },
|
|
}
|
|
|
|
/// Responses the host sends back for `PluginRequest`.
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum PluginResponse {
|
|
Ok,
|
|
Bool(bool),
|
|
Handle(Handle),
|
|
Record(Option<ExtendedDataRecord>),
|
|
Document(Box<CadDocument>),
|
|
Error(String),
|
|
/// Path to the memory-mapped file and the current snapshot version.
|
|
DocumentView {
|
|
path: String,
|
|
version: u64,
|
|
},
|
|
Handles(Vec<Handle>),
|
|
/// V4: path to the tab-keyed memory-mapped file and current version.
|
|
DocumentViewV4 {
|
|
path: String,
|
|
version: u64,
|
|
},
|
|
/// V4: stable tab identifier of the active tab.
|
|
TabId(u64),
|
|
/// V5: filesystem path of the document in the requested tab, if any.
|
|
DocumentPath(Option<std::ffi::OsString>),
|
|
/// DocApi v2 (`ocs_doc_api`): the bincode-serialized `Result<Receipt, ApiError>` answering a
|
|
/// `PluginRequest::DocApiRequest`. Appended at the end (discriminant stability).
|
|
/// `serde_bytes` keeps the payload a single length-prefixed bulk copy.
|
|
DocApiResponse { #[serde(with = "serde_bytes")] bytes: Vec<u8> },
|
|
}
|
|
|
|
/// Messages sent from the host to the plugin runner.
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum HostToPlugin {
|
|
Request(HostRequest),
|
|
Response(Box<PluginResponse>),
|
|
}
|
|
|
|
/// Messages sent from the plugin runner to the host.
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum PluginToHost {
|
|
Request(Box<PluginRequest>),
|
|
Response(HostResponse),
|
|
}
|
|
|
|
/// Convenience helper for manifest serialization.
|
|
impl From<&'static crate::manifest::PluginManifest> for OwnedPluginManifest {
|
|
fn from(m: &'static crate::manifest::PluginManifest) -> Self {
|
|
Self {
|
|
id: m.id.to_string(),
|
|
name: m.name.to_string(),
|
|
version: m.version.to_string(),
|
|
description: m.description.to_string(),
|
|
api_version: m.api_version.major,
|
|
ribbon_order: m.ribbon_order,
|
|
xdata_apps: m.xdata_apps.iter().map(|s| s.to_string()).collect(),
|
|
command_prefixes: m.command_prefixes.iter().map(|s| s.to_string()).collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl OwnedPluginManifest {
|
|
pub fn api_version(&self) -> ApiVersion {
|
|
ApiVersion {
|
|
major: self.api_version,
|
|
}
|
|
}
|
|
}
|