1use std::any::Any;
2use std::cell::RefCell;
3use std::collections::{HashMap, HashSet};
4use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs};
5use std::rc::Rc;
6
7pub mod orm;
8pub mod privileged_agent;
9
10pub mod messages;
11pub mod serde_pax;
12
13use messages::LLMRequest;
14use orm::{MessageType, ReloadType};
15use pax_manifest::pax_runtime_api::Property;
16use privileged_agent::WebSocketConnection;
17use url::Url;
18
19use core::fmt::Debug;
20
21pub use pax_manifest;
22use pax_manifest::{
23 server::*, ComponentDefinition, PaxManifest, TypeId, UniqueTemplateNodeIdentifier,
24};
25use reqwasm::http::Response;
26pub use serde_pax::error::{Error, Result};
27pub use serde_pax::se::{to_pax, Serializer};
28
29pub const INITIAL_MANIFEST_FILE_NAME: &str = "initial-manifest.json";
30
31type Factories = HashMap<String, Box<fn(ComponentDefinition) -> Box<dyn Any>>>;
32use crate::orm::PaxManifestORM;
33
34pub struct DesigntimeManager {
35 orm: PaxManifestORM,
36 factories: Factories,
37 priv_agent_connection: Rc<RefCell<WebSocketConnection>>,
38 pub_pax_connection: Rc<RefCell<WebSocketConnection>>,
39 project_query: Option<String>,
40 response_queue: Rc<RefCell<Vec<DesigntimeResponseMessage>>>,
41 last_rendered_manifest_version: Property<usize>,
42 pub publish_state: Property<Option<PublishResponse>>,
43}
44
45pub enum DesigntimeResponseMessage {
46 LLMResponse(ComponentDefinition),
47 PublishResponse(PublishResponse),
48}
49
50impl Debug for DesigntimeManager {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.debug_struct("DesigntimeManager").finish()
53 }
54}
55
56const VERSION_PREFIX: &str = "/v0";
57const ENDPOINT_PUBLISH: &str = "/v0/publish";
58
59const PROD_PUB_PAX_SERVER: &str = "https://pub.pax.dev";
60
61fn get_server_base_url() -> String {
62 option_env!("PUB_PAX_SERVER")
64 .unwrap_or(PROD_PUB_PAX_SERVER)
65 .to_string()
66}
67
68impl DesigntimeManager {
69 pub fn get_last_rendered_manifest_version(&self) -> Property<usize> {
70 self.last_rendered_manifest_version.clone()
71 }
72
73 pub fn set_last_rendered_manifest_version(&self, version: usize) {
74 self.last_rendered_manifest_version.set(version);
75 }
76
77 pub fn get_llm_messages(&mut self, request_id: u64) -> Vec<String> {
78 self.orm.get_messages(request_id)
79 }
80
81 pub fn new_with_local_addr(manifest: PaxManifest, local_addr: &str) -> Self {
82 let priv_agent = Rc::new(RefCell::new(
83 WebSocketConnection::new(local_addr, None)
84 .expect("couldn't connect to privileged agent"),
85 ));
86
87 let address: String = get_server_base_url();
88 let pub_pax = Rc::new(RefCell::new(
89 WebSocketConnection::new(&address, Some(VERSION_PREFIX))
90 .expect("couldn't connect to privileged agent"),
91 ));
92
93 let orm = PaxManifestORM::new(manifest);
94 let factories = HashMap::new();
95 DesigntimeManager {
96 orm,
97 factories,
98 priv_agent_connection: priv_agent,
99 pub_pax_connection: pub_pax,
100 project_query: None,
101 response_queue: Rc::new(RefCell::new(Vec::new())),
102 last_rendered_manifest_version: Property::new(0),
103 publish_state: Default::default(),
104 }
105 }
106 pub fn new(manifest: PaxManifest) -> Self {
107 Self::new_with_local_addr(manifest, "ws://localhost:8080")
108 }
109
110 pub fn set_project(&mut self, project_query: String) {
111 self.project_query = Some(project_query);
112 }
113
114 pub fn send_file_to_static_dir(&self, name: &str, data: Vec<u8>) -> anyhow::Result<()> {
115 self.priv_agent_connection
116 .borrow_mut()
117 .send_file_to_static_dir(name, data)?;
118 Ok(())
119 }
120
121 pub fn get_new_message_listener(&self) -> Property<Vec<MessageType>> {
122 self.orm.new_message.clone()
123 }
124
125 pub fn get_manifest_loaded_from_server_prop(&self) -> Property<bool> {
126 self.orm.manifest_loaded_from_server.clone()
127 }
128
129 pub fn send_component_update(&mut self, type_id: &TypeId) -> anyhow::Result<()> {
130 self.orm.send_component_update(type_id);
131 let component = self.orm.get_component(type_id)?;
132 self.priv_agent_connection
133 .borrow_mut()
134 .send_component_update(component)?;
135
136 Ok(())
137 }
138
139 pub fn llm_request(&mut self, prompt: &str, request_id: u64) -> anyhow::Result<()> {
140 let manifest = self.orm.get_manifest().clone();
141
142 let llm_request = LLMRequest {
143 manifest: manifest.clone(),
144 prompt: prompt.to_string(),
145 request_id,
146 };
147
148 self.pub_pax_connection
149 .borrow_mut()
150 .send_llm_request(llm_request)?;
151
152 Ok(())
153 }
154
155 pub fn publish_project(&mut self) {
156 let manifest = self.orm.get_manifest().clone();
157
158 let publish_request = PublishRequest {
159 manifest: manifest.clone(),
160 };
161
162 let queue_cloned = self.response_queue.clone();
163
164 wasm_bindgen_futures::spawn_local(async move {
165 let url = get_server_base_url() + ENDPOINT_PUBLISH;
166
167 let response = reqwasm::http::Request::post(&url)
168 .header("Content-Type", "application/json")
169 .body(serde_json::to_string(&publish_request).unwrap())
170 .send()
171 .await;
172
173 log::info!("response_text: {:?}", response);
174
175 let pub_response = match response {
176 Ok(resp) => {
177 let pub_response: PublishResponse = resp.json().await.unwrap();
178
179 let pub_response = if let PublishResponse::Success(prs) = pub_response {
180 prs
181 } else {
182 unimplemented!()
183 };
184 log::info!("publish success: {:?}", &pub_response.pull_request_url);
185 PublishResponse::Success(pub_response)
186 }
187 Err(msg) => PublishResponse::Error(ResponseError {
188 message: msg.to_string(),
189 }),
190 };
191
192 queue_cloned
193 .borrow_mut()
194 .push(DesigntimeResponseMessage::PublishResponse(pub_response));
195 });
196 }
197
198 pub fn add_factory(
199 &mut self,
200 type_id: String,
201 factory: Box<fn(ComponentDefinition) -> Box<dyn Any>>,
202 ) {
203 self.factories.insert(type_id, factory);
204 }
205
206 pub fn get_manifest(&self) -> &PaxManifest {
207 self.orm.get_manifest()
208 }
209
210 pub fn take_reload_queue(&mut self) -> HashSet<ReloadType> {
211 self.orm.take_reload_queue()
212 }
213
214 pub fn reload(&mut self) {
215 self.orm.insert_reload(ReloadType::Tree);
216 self.orm.increment_manifest_version();
217 }
218
219 pub fn set_userland_root_component_type_id(&mut self, type_id: &TypeId) {
220 self.orm.set_userland_root_component_type_id(type_id);
221 self.orm.increment_manifest_version();
222 self.orm.insert_reload(ReloadType::Tree);
223 }
224
225 pub fn get_last_written_manifest_version(&self) -> Property<usize> {
226 self.orm.get_manifest_version()
227 }
228
229 pub fn get_orm(&self) -> &PaxManifestORM {
230 &self.orm
231 }
232
233 pub fn get_orm_mut(&mut self) -> &mut PaxManifestORM {
234 &mut self.orm
235 }
236
237 pub fn handle_recv(&mut self) -> anyhow::Result<()> {
238 self.priv_agent_connection
239 .borrow_mut()
240 .handle_recv(&mut self.orm)?;
241
242 self.pub_pax_connection
243 .borrow_mut()
244 .handle_recv(&mut self.orm)?;
245
246 let response_queue = {
247 let mut queue = self.response_queue.borrow_mut();
248 queue.drain(..).collect::<Vec<DesigntimeResponseMessage>>()
249 };
250 for response in response_queue {
251 self.handle_response(response);
252 }
253 Ok(())
254 }
255
256 pub fn handle_response(&mut self, response: DesigntimeResponseMessage) {
257 match response {
258 DesigntimeResponseMessage::LLMResponse(component) => {
259 log::info!("handling LLM response");
260 let _ = self.orm.swap_main_component(component).map_err(|e| {
261 log::error!("Error swapping main component for LLM response: {:?}", e);
262 });
263 }
264 DesigntimeResponseMessage::PublishResponse(response) => {
265 log::info!("received publish response");
266 self.publish_state.set(Some(response));
267 }
268 }
269 }
270}
271
272pub enum Args {}
273
274pub struct NodeWithBounds {
275 pub uni: UniqueTemplateNodeIdentifier,
276 pub x: f64,
277 pub y: f64,
278}