From 7f79e014562bf9d43682713f6e4c34d7e43fbba7 Mon Sep 17 00:00:00 2001 From: MasterPtato Date: Tue, 2 Sep 2025 11:00:45 -0700 Subject: [PATCH] fix(guard): remove incorrect connectability check --- README.md | 9 +- docker/template/grafana-dashboards/cache.json | 4 +- .../core/guard/server/src/routing/actor.rs | 247 ------------------ packages/core/guard/server/src/routing/mod.rs | 7 - .../server/src/routing/pegboard_gateway.rs | 62 +---- .../pegboard/src/ops/actor/get_address.rs | 63 ----- .../services/pegboard/src/ops/actor/mod.rs | 1 - .../pegboard/src/workflows/actor/mod.rs | 14 +- .../pegboard/src/workflows/actor/runtime.rs | 37 +-- pnpm-lock.yaml | 9 +- scripts/tests/package.json | 5 +- sdks/rust/tunnel-protocol/build.rs | 89 +------ 12 files changed, 49 insertions(+), 498 deletions(-) delete mode 100644 packages/core/guard/server/src/routing/actor.rs delete mode 100644 packages/services/pegboard/src/ops/actor/get_address.rs diff --git a/README.md b/README.md index a00c94ba53..319543bc2b 100644 --- a/README.md +++ b/README.md @@ -40,15 +40,14 @@ Public-facing projects: - **Rivet Engine** (you are here): Engine that powers Rivet Actors at scale - **[RivetKit](https://github.com/rivet-gg/rivetkit)**: Lightweight TypeScript library for building Rivet Actors — works with Redis or Rivet Engine -- **[Rivet Studio](/frontend/apps/studio)**: Like Postman, but for Rivet Actors -- **[Rivet Hub](/frontend/apps/hub)**: UI for Rivet Engine +- **[Rivet Hub](/frontend/)**: UI for Rivet Engine - **[Rivet Documentation](/site/src/content/docs)** Projects powering Rivet Engine: -- **[Pegboard](packages/edge/services/pegboard/)**: Actor orchestrator -- **[Guard](packages/edge/infra/guard/)**: Proxy for routing traffic to Rivet Actors -- **[Chirp](packages/common/chirp-workflow/)**: Core workflow engine that powers Rivet +- **[Pegboard](packages/services/pegboard/)**: Actor orchestrator +- **[Guard](packages/core/guard/)**: Proxy for routing traffic to Rivet Actors +- **[Gasoline](packages/common/gasoline/)**: Core durable execution engine that powers Rivet ## Get Started diff --git a/docker/template/grafana-dashboards/cache.json b/docker/template/grafana-dashboards/cache.json index 5d762023ea..f5806c749e 100644 --- a/docker/template/grafana-dashboards/cache.json +++ b/docker/template/grafana-dashboards/cache.json @@ -1170,7 +1170,7 @@ "timepicker": {}, "timezone": "browser", "title": "Rivet Guard", - "uid": "cen785ige8fswd", + "uid": "cen785ige8fswd2", "version": 1, "weekStart": "" -} +} \ No newline at end of file diff --git a/packages/core/guard/server/src/routing/actor.rs b/packages/core/guard/server/src/routing/actor.rs deleted file mode 100644 index 599102b9c6..0000000000 --- a/packages/core/guard/server/src/routing/actor.rs +++ /dev/null @@ -1,247 +0,0 @@ -//use std::time::Duration; -// -//use anyhow::Result; -//use gas::prelude::*; -//use hyper::header::HeaderName; -//use rivet_guard_core::proxy_service::{RouteConfig, RouteTarget, RoutingOutput, RoutingTimeout}; -//use rivet_key_data::generated::pegboard_runner_address_v1::Data as AddressKeyData; -//use udb_util::{SERIALIZABLE, TxnExt}; -// -//use crate::errors; -// -//const ACTOR_READY_TIMEOUT: Duration = Duration::from_secs(10); -//pub(crate) const X_RIVET_ACTOR: HeaderName = HeaderName::from_static("x-rivet-actor"); -//pub(crate) const X_RIVET_ADDR: HeaderName = HeaderName::from_static("x-rivet-addr"); -// -///// Route requests to actor services based on hostname and path -//#[tracing::instrument(skip_all)] -//pub async fn route_request( -// ctx: &StandaloneCtx, -// target: &str, -// _host: &str, -// path: &str, -// headers: &hyper::HeaderMap, -//) -> Result> { -// // Check target -// if target != "actor" { -// return Ok(None); -// } -// -// // Find actor to route to -// let actor_id_str = headers.get(X_RIVET_ACTOR).ok_or_else(|| { -// crate::errors::MissingHeader { -// header: X_RIVET_ACTOR.to_string(), -// } -// .build() -// })?; -// let actor_id = Id::parse(actor_id_str.to_str()?)?; -// -// // Route to peer dc where the actor lives -// if actor_id.label() != ctx.config().dc_label() { -// tracing::debug!(peer_dc_label=?actor_id.label(), "re-routing actor to peer dc"); -// -// let peer_dc = ctx -// .config() -// .dc_for_label(actor_id.label()) -// .context("dc with the given label not found")?; -// -// return Ok(Some(RoutingOutput::Route(RouteConfig { -// targets: vec![RouteTarget { -// actor_id: Some(actor_id), -// host: peer_dc -// .guard_url -// .host() -// .context("peer dc guard_url has no host")? -// .to_string(), -// port: peer_dc -// .guard_url -// .port() -// .context("peer dc guard_url has no port")?, -// path: path.to_owned(), -// }], -// timeout: RoutingTimeout { -// routing_timeout: 10, -// }, -// }))); -// } -// -// let addr_name = headers.get(X_RIVET_ADDR).ok_or_else(|| { -// crate::errors::MissingHeader { -// header: X_RIVET_ADDR.to_string(), -// } -// .build() -// })?; -// let addr_name = addr_name.to_str()?; -// -// // Now that we have the actor_id and addr_name, lookup the actor -// if let Some(target) = find_actor(ctx, actor_id, addr_name, path).await? { -// Ok(Some(RoutingOutput::Route(RouteConfig { -// targets: vec![target], -// timeout: RoutingTimeout { -// routing_timeout: 10, -// }, -// }))) -// } else { -// tracing::debug!( -// ?actor_id, -// ?addr_name, -// "attempted to route to actor not found" -// ); -// -// Err(errors::ActorNotFound { -// actor_id, -// addr: addr_name.to_string(), -// } -// .build()) -// } -//} -// -//struct FoundActor { -// workflow_id: Id, -// sleeping: bool, -// destroyed: bool, -//} -// -///// Find an actor by actor_id and addr_name - this would call into the actor registry -//#[tracing::instrument(skip_all, fields(%actor_id, %addr_name, %path))] -//async fn find_actor( -// ctx: &StandaloneCtx, -// actor_id: Id, -// addr_name: &str, -// path: &str, -//) -> Result> { -// // TODO: Optimize this down to a single FDB call -// -// // Create subs before checking if actor exists/is not destroyed -// let mut ready_sub = ctx -// .subscribe::(("actor_id", actor_id)) -// .await?; -// let mut fail_sub = ctx -// .subscribe::(("actor_id", actor_id)) -// .await?; -// let mut destroy_sub = ctx -// .subscribe::(("actor_id", actor_id)) -// .await?; -// -// let actor_res = tokio::time::timeout( -// Duration::from_secs(5), -// ctx.udb()? -// .run(|tx, _mc| async move { -// let txs = tx.subspace(pegboard::keys::subspace()); -// -// let workflow_id_key = pegboard::keys::actor::WorkflowIdKey::new(actor_id); -// let sleep_ts_key = pegboard::keys::actor::SleepTsKey::new(actor_id); -// let destroy_ts_key = pegboard::keys::actor::DestroyTsKey::new(actor_id); -// -// let (workflow_id_entry, sleeping, destroyed) = tokio::try_join!( -// txs.read_opt(&workflow_id_key, SERIALIZABLE), -// txs.exists(&sleep_ts_key, SERIALIZABLE), -// txs.exists(&destroy_ts_key, SERIALIZABLE), -// )?; -// -// let Some(workflow_id) = workflow_id_entry else { -// return Ok(None); -// }; -// -// Ok(Some(FoundActor { -// workflow_id, -// sleeping, -// destroyed, -// })) -// }) -// .custom_instrument(tracing::info_span!("actor_exists_tx")), -// ) -// .await??; -// -// let Some(actor) = actor_res else { -// return Err(errors::ActorNotFound { -// actor_id, -// addr: addr_name.to_string(), -// } -// .build()); -// }; -// -// if actor.destroyed { -// return Err(errors::ActorDestroyed { actor_id }.build()); -// } -// -// // Wake actor if sleeping -// if actor.sleeping { -// ctx.signal(pegboard::workflows::actor::Wake {}) -// .to_workflow_id(actor.workflow_id) -// .send() -// .await?; -// } -// -// // Fetch address. Will return None if actor is not ready yet. -// let addr = if let Some(addr) = fetch_addr(ctx, actor_id, addr_name).await? { -// addr -// } else { -// tracing::info!(?actor_id, "waiting for actor to become ready"); -// -// // Wait for ready, fail, or destroy -// tokio::select! { -// res = ready_sub.next() => { res?; }, -// res = fail_sub.next() => { -// let msg = res?; -// return Err(msg.error.clone().build()); -// } -// res = destroy_sub.next() => { -// res?; -// return Err(pegboard::errors::Actor::DestroyedWhileWaitingForReady.build()); -// } -// // Ready timeout -// _ = tokio::time::sleep(ACTOR_READY_TIMEOUT) => { -// return Err(errors::ActorReadyTimeout { actor_id }.build()); -// } -// } -// -// // Fetch address again after ready -// let Some(addr) = fetch_addr(ctx, actor_id, addr_name).await? else { -// return Err(errors::ActorNotFound { -// actor_id, -// addr: addr_name.to_string(), -// } -// .build()); -// }; -// -// addr -// }; -// -// tracing::debug!(?actor_id, ?addr, "actor ready"); -// -// // Validate addr type -// let AddressKeyData::Http(addr) = addr else { -// return Err(crate::errors::WrongAddrProtocol { -// addr_name: addr_name.into(), -// expected: "http", -// received: match addr { -// AddressKeyData::Http(_) => unreachable!(), -// AddressKeyData::Tcp(_) => "tcp", -// AddressKeyData::Udp(_) => "udp", -// }, -// } -// .build()); -// }; -// -// Ok(Some(RouteTarget { -// actor_id: Some(actor_id), -// host: addr.hostname, -// port: addr.port, -// path: path.to_owned(), -// })) -//} -// -//#[tracing::instrument(skip_all, fields(?actor_id))] -//async fn fetch_addr( -// ctx: &StandaloneCtx, -// actor_id: Id, -// addr_name: &str, -//) -> Result> { -// let get_address_fut = ctx.op(pegboard::ops::actor::get_address::Input { -// actor_id, -// address_name: addr_name.into(), -// }); -// let get_address = tokio::time::timeout(Duration::from_secs(5), get_address_fut).await??; -// Ok(get_address) -//} diff --git a/packages/core/guard/server/src/routing/mod.rs b/packages/core/guard/server/src/routing/mod.rs index 125fae9fa4..b8b0d757f6 100644 --- a/packages/core/guard/server/src/routing/mod.rs +++ b/packages/core/guard/server/src/routing/mod.rs @@ -7,7 +7,6 @@ use rivet_guard_core::RoutingFn; use crate::errors; -//pub(crate) mod actor; mod api_peer; mod api_public; pub mod pegboard_gateway; @@ -36,12 +35,6 @@ pub fn create_routing_function(ctx: StandaloneCtx) -> RoutingFn { // Read target if let Some(target) = headers.get(X_RIVET_TARGET).and_then(|x| x.to_str().ok()) { - // if let Some(routing_output) = - // actor::route_request(&ctx, target, host, path, headers).await? - // { - // return Ok(routing_output); - // } - if let Some(routing_output) = runner_ws::route_request(&ctx, target, host, path).await? { diff --git a/packages/core/guard/server/src/routing/pegboard_gateway.rs b/packages/core/guard/server/src/routing/pegboard_gateway.rs index bc22cb298b..fe8a5b2fe5 100644 --- a/packages/core/guard/server/src/routing/pegboard_gateway.rs +++ b/packages/core/guard/server/src/routing/pegboard_gateway.rs @@ -154,28 +154,20 @@ async fn find_actor( } // Check if actor is connectable and get runner_id - let runner_info = { - let get_runner_fut = ctx.op(pegboard::ops::actor::get_runner::Input { - actor_ids: vec![actor_id], - }); - let output = tokio::time::timeout(Duration::from_secs(5), get_runner_fut).await??; - output.actors.into_iter().find(|a| a.actor_id == actor_id) - }; - - let Some(runner_info) = runner_info else { - return Err(errors::ActorNotFound { - actor_id, - port_name: port_name.to_string(), - } - .build()); - }; - - if !runner_info.is_connectable { + let get_runner_fut = ctx.op(pegboard::ops::actor::get_runner::Input { + actor_ids: vec![actor_id], + }); + let res = tokio::time::timeout(Duration::from_secs(5), get_runner_fut).await??; + let runner_info = res.actors.into_iter().next().filter(|x| x.is_connectable); + + let runner_id = if let Some(runner_info) = runner_info { + runner_info.runner_id + } else { tracing::info!(?actor_id, "waiting for actor to become ready"); // Wait for ready, fail, or destroy tokio::select! { - res = ready_sub.next() => { res?; }, + res = ready_sub.next() => { res?.runner_id }, res = fail_sub.next() => { let msg = res?; return Err(msg.error.clone().build()); @@ -185,45 +177,19 @@ async fn find_actor( return Err(pegboard::errors::Actor::DestroyedWhileWaitingForReady.build()); } // Ready timeout - _ = tokio::time::sleep(ACTOR_READY_TIMEOUT) => { + _ = tokio::time::sleep(ACTOR_READY_TIMEOUT) => { return Err(errors::ActorReadyTimeout { actor_id }.build()); } } + }; - // TODO: Is this needed? Can't we just re-check the actor exists if it fails to connect? - // Verify actor is connectable again - let runner_info = { - let get_runner_fut = ctx.op(pegboard::ops::actor::get_runner::Input { - actor_ids: vec![actor_id], - }); - let output = tokio::time::timeout(Duration::from_secs(5), get_runner_fut).await??; - output.actors.into_iter().find(|a| a.actor_id == actor_id) - }; - - let Some(runner_info) = runner_info else { - return Err(errors::ActorNotFound { - actor_id, - port_name: port_name.to_string(), - } - .build()); - }; - - if !runner_info.is_connectable { - return Err(errors::ActorNotFound { - actor_id, - port_name: port_name.to_string(), - } - .build()); - }; - } - - tracing::debug!(?actor_id, runner_id = ?runner_info.runner_id, "actor ready"); + tracing::debug!(?actor_id, ?runner_id, "actor ready"); // Return pegboard-gateway instance let gateway = pegboard_gateway::PegboardGateway::new( ctx.clone(), actor_id, - runner_info.runner_id, + runner_id, port_name.to_string(), ); Ok(Some(RoutingOutput::CustomServe(std::sync::Arc::new( diff --git a/packages/services/pegboard/src/ops/actor/get_address.rs b/packages/services/pegboard/src/ops/actor/get_address.rs deleted file mode 100644 index 494de62e2b..0000000000 --- a/packages/services/pegboard/src/ops/actor/get_address.rs +++ /dev/null @@ -1,63 +0,0 @@ -use gas::prelude::*; -use rivet_key_data::generated::pegboard_runner_address_v1::Data as AddressKeyData; -use udb_util::{FormalKey, SERIALIZABLE}; -use universaldb as udb; - -use crate::keys; - -#[derive(Debug)] -pub struct Input { - pub actor_id: Id, - pub address_name: String, -} - -#[operation] -pub async fn pegboard_actor_get_address( - ctx: &OperationCtx, - input: &Input, -) -> Result> { - let address = ctx - .udb()? - .run(|tx, _mc| async move { - let runner_id_key = keys::actor::RunnerIdKey::new(input.actor_id); - let connectable_key = keys::actor::ConnectableKey::new(input.actor_id); - let (runner_id_entry, connectable_entry) = tokio::try_join!( - tx.get(&keys::subspace().pack(&runner_id_key), SERIALIZABLE), - tx.get(&keys::subspace().pack(&connectable_key), SERIALIZABLE), - )?; - - let Some(runner_id_entry) = runner_id_entry else { - // Actor does not exist - return Ok(None); - }; - - if connectable_entry.is_none() { - // Actor is not ready yet - return Ok(None); - } - - let runner_id = runner_id_key - .deserialize(&runner_id_entry) - .map_err(|x| udb::FdbBindingError::CustomError(x.into()))?; - - let address_key = keys::runner::AddressKey::new(runner_id, input.address_name.clone()); - let address_entry = tx - .get(&keys::subspace().pack(&address_key), SERIALIZABLE) - .await?; - - let Some(address_entry) = address_entry else { - // Address does not exist - return Ok(None); - }; - - let address = address_key - .deserialize(&address_entry) - .map_err(|x| udb::FdbBindingError::CustomError(x.into()))?; - - Ok(Some(address)) - }) - .custom_instrument(tracing::info_span!("actor_get_address_tx")) - .await?; - - Ok(address) -} diff --git a/packages/services/pegboard/src/ops/actor/mod.rs b/packages/services/pegboard/src/ops/actor/mod.rs index d4e2cec86f..45e7391f9a 100644 --- a/packages/services/pegboard/src/ops/actor/mod.rs +++ b/packages/services/pegboard/src/ops/actor/mod.rs @@ -1,6 +1,5 @@ pub mod create; pub mod get; -pub mod get_address; pub mod get_for_key; pub mod get_reservation_for_key; pub mod get_runner; diff --git a/packages/services/pegboard/src/workflows/actor/mod.rs b/packages/services/pegboard/src/workflows/actor/mod.rs index d9d958d23f..a512312465 100644 --- a/packages/services/pegboard/src/workflows/actor/mod.rs +++ b/packages/services/pegboard/src/workflows/actor/mod.rs @@ -307,10 +307,12 @@ pub async fn pegboard_actor(ctx: &mut WorkflowCtx, input: &Input) -> Result<()> }) .await?; - ctx.msg(Ready {}) - .tag("actor_id", input.actor_id) - .send() - .await?; + ctx.msg(Ready { + runner_id: state.runner_id, + }) + .tag("actor_id", input.actor_id) + .send() + .await?; } protocol::ActorState::Stopped { code, .. } => { if let Some(res) = @@ -516,7 +518,9 @@ pub struct Failed { } #[message("pegboard_actor_ready")] -pub struct Ready {} +pub struct Ready { + pub runner_id: Id, +} #[signal("pegboard_actor_allocate")] #[derive(Debug)] diff --git a/packages/services/pegboard/src/workflows/actor/runtime.rs b/packages/services/pegboard/src/workflows/actor/runtime.rs index 8fb44c154e..6a6a93a14e 100644 --- a/packages/services/pegboard/src/workflows/actor/runtime.rs +++ b/packages/services/pegboard/src/workflows/actor/runtime.rs @@ -268,11 +268,16 @@ async fn allocate_actor( metrics::ACTOR_ALLOCATE_DURATION .record(dt, &[KeyValue::new("did_reserve", res.is_ok().to_string())]); - if let Ok(res) = &res { - state.sleep_ts = None; - state.pending_allocation_ts = None; - state.runner_id = Some(res.runner_id); - state.runner_workflow_id = Some(res.runner_workflow_id); + match &res { + Ok(res) => { + state.sleep_ts = None; + state.pending_allocation_ts = None; + state.runner_id = Some(res.runner_id); + state.runner_workflow_id = Some(res.runner_workflow_id); + } + Err(pending_allocation_ts) => { + state.pending_allocation_ts = Some(*pending_allocation_ts); + } } Ok(res) @@ -365,11 +370,6 @@ pub async fn spawn_actor( "failed to allocate (no availability), waiting for allocation", ); - ctx.activity(SetPendingAllocationInput { - pending_allocation_ts, - }) - .await?; - // If allocation fails, the allocate txn already inserted this actor into the queue. Now we wait for // an `Allocate` signal match ctx.listen::().await? { @@ -545,23 +545,6 @@ pub async fn reschedule_actor( } } -#[derive(Debug, Serialize, Deserialize, Hash)] -pub struct SetPendingAllocationInput { - pending_allocation_ts: i64, -} - -#[activity(SetPendingAllocation)] -pub async fn set_pending_allocation( - ctx: &ActivityCtx, - input: &SetPendingAllocationInput, -) -> Result<()> { - let mut state = ctx.state::()?; - - state.pending_allocation_ts = Some(input.pending_allocation_ts); - - Ok(()) -} - #[derive(Debug, Serialize, Deserialize, Hash)] pub struct ClearPendingAllocationInput { actor_id: Id, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bccdc652e0..ffec1ba4ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -608,6 +608,9 @@ importers: '@types/node': specifier: ^24.3.0 version: 24.3.0 + ws: + specifier: 8.18.3 + version: 8.18.3 sdks/typescript/api-full: dependencies: @@ -11339,7 +11342,7 @@ snapshots: eslint: 8.26.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.8.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.8.2))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.26.0) eslint-plugin-react: 7.37.5(eslint@8.26.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.26.0) @@ -11361,7 +11364,7 @@ snapshots: dependencies: debug: 4.4.1 eslint: 8.26.0 - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.8.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.8.2))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0) glob: 7.2.3 is-glob: 4.0.3 resolve: 1.22.10 @@ -11380,7 +11383,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.8.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.8.2))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 diff --git a/scripts/tests/package.json b/scripts/tests/package.json index 1cbb128fac..6bf031bd33 100644 --- a/scripts/tests/package.json +++ b/scripts/tests/package.json @@ -11,6 +11,7 @@ "license": "ISC", "packageManager": "pnpm@10.13.1", "devDependencies": { - "@types/node": "^24.3.0" + "@types/node": "^24.3.0", + "ws": "8.18.3" } -} +} \ No newline at end of file diff --git a/sdks/rust/tunnel-protocol/build.rs b/sdks/rust/tunnel-protocol/build.rs index f43ed92983..453be2c763 100644 --- a/sdks/rust/tunnel-protocol/build.rs +++ b/sdks/rust/tunnel-protocol/build.rs @@ -1,8 +1,4 @@ -use std::{ - env, fs, - path::{Path, PathBuf}, - process::Command, -}; +use std::{env, fs, path::Path}; use indoc::formatdoc; @@ -56,78 +52,6 @@ mod rust { } } -mod typescript { - use super::*; - - pub fn generate_sdk(schema_dir: &Path) { - let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let workspace_root = Path::new(&manifest_dir) - .parent() - .and_then(|p| p.parent()) - .and_then(|p| p.parent()) - .expect("Failed to find workspace root"); - - let sdk_dir = workspace_root - .join("sdks") - .join("typescript") - .join("tunnel-protocol"); - let src_dir = sdk_dir.join("src"); - - let highest_version_path = super::find_highest_version(schema_dir); - - let _ = fs::remove_dir_all(&src_dir); - if let Err(e) = fs::create_dir_all(&src_dir) { - panic!("Failed to create SDK directory: {}", e); - } - - let output = - Command::new(workspace_root.join("node_modules/@bare-ts/tools/dist/bin/cli.js")) - .arg("compile") - .arg("--generator") - .arg("ts") - .arg(highest_version_path) - .arg("-o") - .arg(src_dir.join("index.ts")) - .output() - .expect("Failed to execute bare compiler for TypeScript"); - - if !output.status.success() { - panic!( - "BARE TypeScript generation failed: {}", - String::from_utf8_lossy(&output.stderr), - ); - } - } -} - -fn find_highest_version(schema_dir: &Path) -> PathBuf { - let mut highest_version = 0; - let mut highest_version_path = PathBuf::new(); - - for entry in fs::read_dir(schema_dir).unwrap().flatten() { - if !entry.path().is_dir() { - let path = entry.path(); - let bare_name = path - .file_name() - .unwrap() - .to_str() - .unwrap() - .split_once('.') - .unwrap() - .0; - - if let Ok(version) = bare_name[1..].parse::() { - if version > highest_version { - highest_version = version; - highest_version_path = path; - } - } - } - } - - highest_version_path -} - fn main() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let workspace_root = Path::new(&manifest_dir) @@ -144,15 +68,4 @@ fn main() { println!("cargo:rerun-if-changed={}", schema_dir.display()); rust::generate_sdk(&schema_dir); - - // Check if cli.js exists before attempting TypeScript generation - let cli_js_path = workspace_root.join("node_modules/@bare-ts/tools/dist/bin/cli.js"); - if cli_js_path.exists() { - typescript::generate_sdk(&schema_dir); - } else { - println!( - "cargo:warning=TypeScript SDK generation skipped: cli.js not found at {}. Run `pnpm install` to install.", - cli_js_path.display() - ); - } }