Re-align workspace layout with hero_compute (revisits #15) #29

Closed
opened 2026-04-27 17:23:41 +00:00 by mahmoud · 3 comments
Owner

Context

PR #18 (closes-via-URL #15) restructured the workspace under the framing of a "canonical hero_voice/hero_osis pattern." A wider sweep of the ecosystem shows that framing was overstated — siblings actually disagree on layout:

Service Schemas dir build.rs lives in single_bin/bin_ui Core crate?
hero_voice schemas/ core crate hero_voice/ no yes
hero_osis schemas/ server crate yes yes (no build.rs in core)
hero_compute schemas/ server + ui + explorer no n/a (hero_compute is a CLI bin only)
hero_books specs/schemas/ no oschema build at all n/a hero_books_lib only
hero_livekit (post-#18) schemas/ core crate hero_livekit/ yes yes

hero_livekit ended up as a hybrid that matches no sibling exactly, plus two real smells from the merged config:

  1. bin_ui("hero_livekit_ui::build_router") references a function whose only caller is the auto-generated src/bin/hero_livekit.rs — comment in #15 admits autobins = false was needed to make it compile. Aspirational config, not canonical.
  2. sdk_types_crate("hero_livekit") was set, but hero_livekit_sdk ends up using a hand-written openrpc_client! macro path anyway — the flag did no useful work.

This issue proposes realigning hero_livekit with hero_compute's simpler, more battle-tested model.

Goal

Strip the aspirational machinery and reshape the workspace to match hero_compute:

  • build.rs lives in the server crate, not in a separate core crate.
  • No core library crate — domain types are emitted into hero_<svc>_server/src/<domain>/ next to the boot.
  • SDK is a single lib.rs using openrpc_client!("../hero_<svc>_server/openrpc.json", name = "...") plus hand-written socket/env helpers. No SDK build.rs.
  • Drop single_bin, bin_companions, bin_ui, client_crate_dir, sdk_types_crate, with_wasm, nested_layout from the OschemaBuildConfig chain.

Target layout

hero_livekit/
├── Cargo.toml
├── schemas/livekit/livekit.oschema
├── docs/schemas/
├── sdk/js/
└── crates/
    ├── hero_livekit_server/               # owns build.rs + generated code
    │   ├── build.rs                        # minimal OschemaBuildConfig (compute-style)
    │   ├── openrpc.json                    # at crate root, for SDK macro consumption
    │   └── src/
    │       ├── main.rs                     # current OServer/HeroLifecycle boot — kept
    │       └── livekit/
    │           ├── mod.rs                  # `pub mod core; pub mod server;`
    │           ├── core/                   # generated: types, rpc, openrpc.json
    │           ├── server/
    │           │   ├── mod.rs              # OsisLivekit struct
    │           │   ├── rpc.rs              # hand-written handlers
    │           │   └── authz.rs            # hand-written
    │           └── tests.rs
    ├── hero_livekit_sdk/                   # one-file lib + helpers, no build.rs
    │   └── src/lib.rs                      # `openrpc_client!("../hero_livekit_server/openrpc.json", name = "LiveKitClient")`
    ├── hero_livekit_ui/                    # standalone bin like hero_compute_ui
    ├── hero_livekit_backend/               # KEEP — out of scope (Rust port of Go lk-backend)
    ├── hero_livekit_rhai/                  # KEEP — out of scope
    └── hero_livekit_examples/              # KEEP

Crates to delete: crates/hero_livekit/ — its hand-written contents fold into hero_livekit_server.

Decisions to confirm before implementation

  1. Drop hero_livekit_ui::build_router? In hero_compute_ui the UI is a standalone bin with no library surface. build_router exists today only because PR #18 wired it into the (broken) single-binary orchestrator. Recommendation: remove the lib surface, fold its contents into src/server.rs like hero_compute_ui does.

  2. Delete the auto-generated src/bin/hero_livekit.rs? It exists today but doesn't compile cleanly (per the comment trail on #15autobins = false). hero_compute has no equivalent — it has a separate hero_compute CLI crate that uses hero_proc_sdk to register services. Recommendation: delete it. A hero_proc-style CLI is a separate follow-up issue if needed.

  3. hero_livekit_sdk/build.rs hack — it currently re-injects pub mod openrpc; into lib.rs on every build because the OSchema generator overwrites lib.rs. After we stop generating into the SDK crate, this hack disappears entirely.

Step-by-step plan

Step 1 — collapse the core crate into the server crate

  • Delete crates/hero_livekit/ entirely (build.rs, src/livekit/, src/bin/hero_livekit.rs, src/services/, src/lib.rs, Cargo.toml)
  • Move crates/hero_livekit/src/livekit/server/rpc.rscrates/hero_livekit_server/src/livekit/server/rpc.rs
  • Move hand-written crates/hero_livekit/src/livekit/core/types.rs (if any extensions) → crates/hero_livekit_server/src/livekit/core/types.rs
  • Move crates/hero_livekit/src/livekit/tests.rscrates/hero_livekit_server/src/livekit/tests.rs
  • Discard all generated files (*_generated.rs, generated mod.rs, generated openrpc.json) — re-emitted in step 2

Step 2 — give the server crate a hero_compute-style build.rs

Create crates/hero_livekit_server/build.rs:

use hero_rpc_osis::build::{OschemaBuildConfig, OschemaBuilder};

fn main() {
    let config = OschemaBuildConfig::new()
        .schemas_dir("../../schemas")
        .docs_dir("../../docs/schemas")
        .domain("livekit", "LiveKit SFU orchestration and room management")
        .generate_server()
        .debug(true);

    OschemaBuilder::new(config)
        .generate()
        .expect("Failed to generate code from schemas");

    let generated_files = [
        "src/livekit/mod.rs",
        "src/livekit/types_generated.rs",
        "src/livekit/types_wasm_generated.rs",
        "src/livekit/rpc_generated.rs",
        "src/livekit/osis_server_generated.rs",
    ];
    for file in &generated_files {
        let path = std::path::Path::new(file);
        if path.exists() {
            let _ = std::process::Command::new("rustfmt")
                .args(["--edition", "2024"])
                .arg(path)
                .status();
        }
    }
}

Add [build-dependencies] hero_rpc_osis = { workspace = true } to hero_livekit_server/Cargo.toml.

Step 3 — fix hero_livekit_server/Cargo.toml

  • Remove hero_livekit = { workspace = true } dep (the crate is gone)
  • Keep current OServer/HeroLifecycle deps (server boot is out of scope)

Step 4 — fix hero_livekit_server/src/main.rs and src/livekit/mod.rs

  • Replace use hero_livekit::livekit::server::OsisLivekit with use crate::livekit::server::OsisLivekit
  • livekit/mod.rs becomes pub mod core; pub mod server; #[cfg(test)] mod tests; — no re-exports from hero_livekit

Step 5 — relocate openrpc.json for SDK macro consumption

After build, the generator emits crates/hero_livekit_server/src/livekit/openrpc.json. Match hero_compute_server by also having crates/hero_livekit_server/openrpc.json at crate root (compute keeps a copy there for openrpc_client! consumption).

Step 6 — simplify hero_livekit_sdk

  • Delete crates/hero_livekit_sdk/build.rs (no longer needed)
  • Delete crates/hero_livekit_sdk/src/livekit/ (osis_client_generated.rs, types.rs, mod.rs — auto-generated by the old client_crate_dir flag)
  • Delete crates/hero_livekit_sdk/src/openrpc.rs
  • Rewrite crates/hero_livekit_sdk/src/lib.rs modeled on hero_compute_sdk/src/lib.rs:
    use hero_rpc_derive::openrpc_client;
    
    openrpc_client!(
        "../hero_livekit_server/openrpc.json",
        name = "LiveKitClient"
    );
    
    // Plus socket helpers, env loaders, http_rpc_unix, etc.
    
  • Trim Cargo.toml to compute-style: hero_rpc_derive, hero_rpc_openrpc, herolib_core, tokio, serde, serde_json, dotenvy. Drop hero_rpc_client, herolib_otoml, herolib_sid if unused.

Step 7 — update consumers of old module paths

Audit hero_livekit_ui/, hero_livekit_examples/examples/*.rs, hero_livekit_rhai/src/client.rs, hero_livekit_backend/src/main.rs:

  • Replace use hero_livekit_sdk::livekit::* patterns with imports of types from the macro-generated root.

Step 8 — hero_livekit_ui (pending decision #1)

  • Move lib.rs contents (build_router, AppState, handlers, whitelist) into src/server.rs
  • src/main.rs becomes ~9 lines like hero_compute_ui/src/main.rs
  • Drop [lib] target from Cargo.toml

Step 9 — workspace Cargo.toml

  • Remove crates/hero_livekit from members
  • Remove hero_livekit = { path = "crates/hero_livekit" } from workspace dependencies

Step 10 — verify

  • cargo build --workspace
  • cargo clippy --workspace -- -D warnings
  • cargo test --workspace
  • Grep confirms zero remaining references to: single_bin, bin_companions, bin_ui, client_crate_dir, sdk_types_crate, with_wasm, nested_layout, autobins = false, hero_livekit::livekit::, crates/hero_livekit/

Out of scope

  • hero_livekit_backend (Rust port of Go lk-backend) — leave alone
  • hero_livekit_rhai — leave alone (may need a small import path tweak in step 7)
  • The OServer/HeroLifecycle boot pattern — server implementation detail, not layout
  • Introducing a hero_proc_sdk-driven hero_livekit CLI crate (analogue of hero_compute) — separate follow-up if desired

Acceptance criteria

  • crates/hero_livekit/ no longer exists
  • crates/hero_livekit_server/build.rs exists with the minimal compute-style config
  • crates/hero_livekit_server/openrpc.json is the single source of truth for SDK consumers
  • crates/hero_livekit_sdk/src/lib.rs is one file, no build.rs
  • No occurrences of single_bin, bin_companions, bin_ui, client_crate_dir, sdk_types_crate, with_wasm, nested_layout, autobins = false anywhere in the repo
  • cargo build --workspace, cargo clippy --workspace -- -D warnings, cargo test --workspace all green
  • hero_livekit_backend and hero_livekit_rhai remain untouched in behavior
  • #15 — original (overstated-canonical) restructure
  • #18 — PR that landed #15
## Context PR #18 (closes-via-URL #15) restructured the workspace under the framing of a "canonical hero_voice/hero_osis pattern." A wider sweep of the ecosystem shows that framing was overstated — siblings actually disagree on layout: | Service | Schemas dir | `build.rs` lives in | `single_bin`/`bin_ui` | Core crate? | |---|---|---|---|---| | `hero_voice` | `schemas/` | core crate `hero_voice/` | no | yes | | `hero_osis` | `schemas/` | **server** crate | yes | yes (no build.rs in core) | | `hero_compute` | `schemas/` | **server + ui + explorer** | no | n/a (`hero_compute` is a CLI bin only) | | `hero_books` | **`specs/schemas/`** | no oschema build at all | n/a | `hero_books_lib` only | | `hero_livekit` (post-#18) | `schemas/` | core crate `hero_livekit/` | yes | yes | `hero_livekit` ended up as a hybrid that matches no sibling exactly, plus two real smells from the merged config: 1. `bin_ui("hero_livekit_ui::build_router")` references a function whose only caller is the auto-generated `src/bin/hero_livekit.rs` — comment in #15 admits `autobins = false` was needed to make it compile. Aspirational config, not canonical. 2. `sdk_types_crate("hero_livekit")` was set, but `hero_livekit_sdk` ends up using a hand-written `openrpc_client!` macro path anyway — the flag did no useful work. This issue proposes realigning `hero_livekit` with **`hero_compute`'s** simpler, more battle-tested model. ## Goal Strip the aspirational machinery and reshape the workspace to match `hero_compute`: - **`build.rs` lives in the server crate, not in a separate core crate.** - **No `core` library crate** — domain types are emitted into `hero_<svc>_server/src/<domain>/` next to the boot. - **SDK is a single `lib.rs`** using `openrpc_client!("../hero_<svc>_server/openrpc.json", name = "...")` plus hand-written socket/env helpers. No SDK build.rs. - **Drop `single_bin`, `bin_companions`, `bin_ui`, `client_crate_dir`, `sdk_types_crate`, `with_wasm`, `nested_layout`** from the OschemaBuildConfig chain. ## Target layout ``` hero_livekit/ ├── Cargo.toml ├── schemas/livekit/livekit.oschema ├── docs/schemas/ ├── sdk/js/ └── crates/ ├── hero_livekit_server/ # owns build.rs + generated code │ ├── build.rs # minimal OschemaBuildConfig (compute-style) │ ├── openrpc.json # at crate root, for SDK macro consumption │ └── src/ │ ├── main.rs # current OServer/HeroLifecycle boot — kept │ └── livekit/ │ ├── mod.rs # `pub mod core; pub mod server;` │ ├── core/ # generated: types, rpc, openrpc.json │ ├── server/ │ │ ├── mod.rs # OsisLivekit struct │ │ ├── rpc.rs # hand-written handlers │ │ └── authz.rs # hand-written │ └── tests.rs ├── hero_livekit_sdk/ # one-file lib + helpers, no build.rs │ └── src/lib.rs # `openrpc_client!("../hero_livekit_server/openrpc.json", name = "LiveKitClient")` ├── hero_livekit_ui/ # standalone bin like hero_compute_ui ├── hero_livekit_backend/ # KEEP — out of scope (Rust port of Go lk-backend) ├── hero_livekit_rhai/ # KEEP — out of scope └── hero_livekit_examples/ # KEEP ``` **Crates to delete:** `crates/hero_livekit/` — its hand-written contents fold into `hero_livekit_server`. ## Decisions to confirm before implementation 1. **Drop `hero_livekit_ui::build_router`?** In `hero_compute_ui` the UI is a standalone bin with no library surface. `build_router` exists today only because PR #18 wired it into the (broken) single-binary orchestrator. Recommendation: remove the lib surface, fold its contents into `src/server.rs` like `hero_compute_ui` does. 2. **Delete the auto-generated `src/bin/hero_livekit.rs`?** It exists today but doesn't compile cleanly (per the comment trail on #15 — `autobins = false`). `hero_compute` has no equivalent — it has a separate `hero_compute` CLI crate that uses `hero_proc_sdk` to register services. Recommendation: delete it. A hero_proc-style CLI is a separate follow-up issue if needed. 3. **`hero_livekit_sdk/build.rs` hack** — it currently re-injects `pub mod openrpc;` into `lib.rs` on every build because the OSchema generator overwrites lib.rs. After we stop generating into the SDK crate, this hack disappears entirely. ## Step-by-step plan ### Step 1 — collapse the core crate into the server crate - Delete `crates/hero_livekit/` entirely (build.rs, src/livekit/, src/bin/hero_livekit.rs, src/services/, src/lib.rs, Cargo.toml) - Move `crates/hero_livekit/src/livekit/server/rpc.rs` → `crates/hero_livekit_server/src/livekit/server/rpc.rs` - Move hand-written `crates/hero_livekit/src/livekit/core/types.rs` (if any extensions) → `crates/hero_livekit_server/src/livekit/core/types.rs` - Move `crates/hero_livekit/src/livekit/tests.rs` → `crates/hero_livekit_server/src/livekit/tests.rs` - Discard all generated files (`*_generated.rs`, generated `mod.rs`, generated `openrpc.json`) — re-emitted in step 2 ### Step 2 — give the server crate a hero_compute-style build.rs Create `crates/hero_livekit_server/build.rs`: ```rust use hero_rpc_osis::build::{OschemaBuildConfig, OschemaBuilder}; fn main() { let config = OschemaBuildConfig::new() .schemas_dir("../../schemas") .docs_dir("../../docs/schemas") .domain("livekit", "LiveKit SFU orchestration and room management") .generate_server() .debug(true); OschemaBuilder::new(config) .generate() .expect("Failed to generate code from schemas"); let generated_files = [ "src/livekit/mod.rs", "src/livekit/types_generated.rs", "src/livekit/types_wasm_generated.rs", "src/livekit/rpc_generated.rs", "src/livekit/osis_server_generated.rs", ]; for file in &generated_files { let path = std::path::Path::new(file); if path.exists() { let _ = std::process::Command::new("rustfmt") .args(["--edition", "2024"]) .arg(path) .status(); } } } ``` Add `[build-dependencies] hero_rpc_osis = { workspace = true }` to `hero_livekit_server/Cargo.toml`. ### Step 3 — fix `hero_livekit_server/Cargo.toml` - Remove `hero_livekit = { workspace = true }` dep (the crate is gone) - Keep current OServer/HeroLifecycle deps (server boot is out of scope) ### Step 4 — fix `hero_livekit_server/src/main.rs` and `src/livekit/mod.rs` - Replace `use hero_livekit::livekit::server::OsisLivekit` with `use crate::livekit::server::OsisLivekit` - `livekit/mod.rs` becomes `pub mod core; pub mod server; #[cfg(test)] mod tests;` — no re-exports from `hero_livekit` ### Step 5 — relocate `openrpc.json` for SDK macro consumption After build, the generator emits `crates/hero_livekit_server/src/livekit/openrpc.json`. Match `hero_compute_server` by also having `crates/hero_livekit_server/openrpc.json` at crate root (compute keeps a copy there for `openrpc_client!` consumption). ### Step 6 — simplify `hero_livekit_sdk` - Delete `crates/hero_livekit_sdk/build.rs` (no longer needed) - Delete `crates/hero_livekit_sdk/src/livekit/` (`osis_client_generated.rs`, `types.rs`, `mod.rs` — auto-generated by the old `client_crate_dir` flag) - Delete `crates/hero_livekit_sdk/src/openrpc.rs` - Rewrite `crates/hero_livekit_sdk/src/lib.rs` modeled on `hero_compute_sdk/src/lib.rs`: ```rust use hero_rpc_derive::openrpc_client; openrpc_client!( "../hero_livekit_server/openrpc.json", name = "LiveKitClient" ); // Plus socket helpers, env loaders, http_rpc_unix, etc. ``` - Trim `Cargo.toml` to compute-style: `hero_rpc_derive`, `hero_rpc_openrpc`, `herolib_core`, `tokio`, `serde`, `serde_json`, `dotenvy`. Drop `hero_rpc_client`, `herolib_otoml`, `herolib_sid` if unused. ### Step 7 — update consumers of old module paths Audit `hero_livekit_ui/`, `hero_livekit_examples/examples/*.rs`, `hero_livekit_rhai/src/client.rs`, `hero_livekit_backend/src/main.rs`: - Replace `use hero_livekit_sdk::livekit::*` patterns with imports of types from the macro-generated root. ### Step 8 — `hero_livekit_ui` (pending decision #1) - Move `lib.rs` contents (build_router, AppState, handlers, whitelist) into `src/server.rs` - `src/main.rs` becomes ~9 lines like `hero_compute_ui/src/main.rs` - Drop `[lib]` target from Cargo.toml ### Step 9 — workspace Cargo.toml - Remove `crates/hero_livekit` from `members` - Remove `hero_livekit = { path = "crates/hero_livekit" }` from workspace dependencies ### Step 10 — verify - `cargo build --workspace` - `cargo clippy --workspace -- -D warnings` - `cargo test --workspace` - Grep confirms zero remaining references to: `single_bin`, `bin_companions`, `bin_ui`, `client_crate_dir`, `sdk_types_crate`, `with_wasm`, `nested_layout`, `autobins = false`, `hero_livekit::livekit::`, `crates/hero_livekit/` ## Out of scope - `hero_livekit_backend` (Rust port of Go `lk-backend`) — leave alone - `hero_livekit_rhai` — leave alone (may need a small import path tweak in step 7) - The OServer/HeroLifecycle boot pattern — server implementation detail, not layout - Introducing a `hero_proc_sdk`-driven `hero_livekit` CLI crate (analogue of `hero_compute`) — separate follow-up if desired ## Acceptance criteria - [ ] `crates/hero_livekit/` no longer exists - [ ] `crates/hero_livekit_server/build.rs` exists with the minimal compute-style config - [ ] `crates/hero_livekit_server/openrpc.json` is the single source of truth for SDK consumers - [ ] `crates/hero_livekit_sdk/src/lib.rs` is one file, no build.rs - [ ] No occurrences of `single_bin`, `bin_companions`, `bin_ui`, `client_crate_dir`, `sdk_types_crate`, `with_wasm`, `nested_layout`, `autobins = false` anywhere in the repo - [ ] `cargo build --workspace`, `cargo clippy --workspace -- -D warnings`, `cargo test --workspace` all green - [ ] `hero_livekit_backend` and `hero_livekit_rhai` remain untouched in behavior ## Related - #15 — original (overstated-canonical) restructure - #18 — PR that landed #15
Author
Owner

Implementation Spec for Issue #29

Objective

Realign hero_livekit's workspace layout with hero_compute's simpler model: a single hero_livekit_server crate that owns build.rs, generates code into src/livekit/, and ships its openrpc.json; a one-file hero_livekit_sdk whose only Rust content is openrpc_client! plus socket/env helpers; deletion of the now-unused crates/hero_livekit/ core crate; folding hero_livekit_ui's library into its server module. No more single_bin, bin_companions, bin_ui, client_crate_dir, sdk_types_crate, with_wasm, nested_layout, or autobins=false workarounds.

Requirements

  • crates/hero_livekit/ deleted; its hand-written content (the orchestrator code in src/livekit/server/rpc.rs, ~962 lines) moves into hero_livekit_server.
  • crates/hero_livekit_server/build.rs exists with a minimal OschemaBuildConfig (compute-style: schemas_dir, docs_dir, single domain, generate_server, debug, plus a rustfmt step).
  • crates/hero_livekit_server/openrpc.json at the crate root remains the canonical spec consumed by openrpc_client!. The build also emits src/livekit/openrpc.json; both must be kept in sync (build.rs copy step).
  • crates/hero_livekit_sdk/src/lib.rs is a single file modelled on crates/hero_compute_sdk/src/lib.rs: openrpc_client!("../hero_livekit_server/openrpc.json", name = "LiveKitClient") plus env / socket helpers. No build.rs. No src/livekit/ subtree. No src/openrpc.rs.
  • All consumer crates (server, ui, examples, rhai) update their imports so they no longer reach into hero_livekit::livekit::* or hero_livekit_sdk::openrpc::* / hero_livekit_sdk::livekit::*.
  • hero_livekit_ui collapses lib.rs + main.rs into a single binary crate with code under src/server.rs and src/main.rs ~10 lines, mirroring hero_compute_ui. The [lib] target is removed.
  • hero_livekit_backend and hero_livekit_rhai source code remain functionally unchanged (rhai gets a small import update only because it points through the SDK).
  • Workspace Cargo.toml no longer lists crates/hero_livekit as a member or hero_livekit as a workspace dep.
  • cargo build, cargo clippy, cargo test all pass against --workspace.
  • Repo-wide grep for single_bin, bin_companions, bin_ui, client_crate_dir, sdk_types_crate, with_wasm, nested_layout, autobins = false, hero_livekit::livekit::, and any path containing crates/hero_livekit/ returns zero hits.

Files to Modify/Create/Delete (high-level)

Path Action Notes
crates/hero_livekit/ (whole directory) Delete After harvesting src/livekit/server/rpc.rs (handwritten orchestrator)
crates/hero_livekit/src/bin/hero_livekit.rs Delete Auto-generated single-binary, broken & unused
crates/hero_livekit_server/build.rs Create Compute-style minimal config
crates/hero_livekit_server/Cargo.toml Modify Drop hero_livekit dep; add reqwest, jsonwebtoken, flate2, tar, dirs, herolib_sid; add [build-dependencies] hero_rpc_osis = { workspace = true }; add build = "build.rs"
crates/hero_livekit_server/src/main.rs Modify Remove cross-crate hero_livekit::livekit::* imports; use crate::livekit::*
crates/hero_livekit_server/src/livekit/mod.rs Rewrite pub mod core; pub use core::*; pub mod server; #[cfg(test)] mod tests;
crates/hero_livekit_server/src/livekit/server/mod.rs Rewrite Compute bridge: #[path = "../osis_server_generated.rs"] etc.
crates/hero_livekit_server/src/livekit/rpc.rs Create (move from old) The 962-line orchestrator
crates/hero_livekit_server/src/livekit/types.rs Create include!("types_generated.rs");
crates/hero_livekit_server/src/livekit/{types,rpc,osis_server}_generated.rs Generated By new build.rs
crates/hero_livekit_server/src/livekit/openrpc.json Generated By new build.rs (copied to crate root)
crates/hero_livekit_sdk/build.rs Delete The lib.rs patcher hack disappears
crates/hero_livekit_sdk/src/lib.rs Rewrite Single file, compute-style helpers
crates/hero_livekit_sdk/src/openrpc.rs Delete Folded into lib.rs
crates/hero_livekit_sdk/src/livekit/ Delete Whole subtree
crates/hero_livekit_sdk/Cargo.toml Modify Compute-style deps, drop hero_rpc_client/herolib_otoml/herolib_sid/feature flags/lints
crates/hero_livekit_ui/Cargo.toml Modify Drop [lib] target
crates/hero_livekit_ui/src/main.rs Rewrite ~10 lines, delegates to server::run()
crates/hero_livekit_ui/src/lib.rs Delete Content moved to server.rs
crates/hero_livekit_ui/src/server.rs Create Owns build_router, AppState, handlers, run()
crates/hero_livekit_ui/src/whitelist.rs No change
crates/hero_livekit_examples/examples/{basic_usage,health}.rs Modify use hero_livekit_sdk::openrpc::*use hero_livekit_sdk::*
crates/hero_livekit_rhai/src/client.rs Modify Same import replacement
crates/hero_livekit_backend/ No change Independent
Cargo.toml (workspace) Modify Drop crates/hero_livekit member + workspace dep; add dotenvy workspace dep

Implementation Plan

Step 1: Harvest the handwritten orchestrator

Files (read): crates/hero_livekit/src/livekit/server/rpc.rs (962 lines)
Files (write): crates/hero_livekit_server/src/livekit/rpc.rs

Subtasks:

  1. Copy the full body of crates/hero_livekit/src/livekit/server/rpc.rs into crates/hero_livekit_server/src/livekit/rpc.rs verbatim.
  2. Update imports inside the copy: use crate::livekit::core::*; and pub use super::rpc_generated::*;.
  3. Verify there are no other crate:: paths in the copied file that need updating.

Dependencies: none.

Step 2: Write the new server build.rs

Files (write): crates/hero_livekit_server/build.rs

Content (substituting livekit for compute's cloud):

use hero_rpc_osis::build::{OschemaBuildConfig, OschemaBuilder};

fn main() {
    let config = OschemaBuildConfig::new()
        .schemas_dir("../../schemas")
        .docs_dir("../../docs/schemas")
        .domain("livekit", "Hero LiveKit -- SFU orchestration + room/participant management")
        .generate_server()
        .debug(true);

    OschemaBuilder::new(config)
        .generate()
        .expect("Failed to generate code from schemas");

    let generated_files = [
        "src/livekit/mod.rs",
        "src/livekit/types_generated.rs",
        "src/livekit/types_wasm_generated.rs",
        "src/livekit/rpc_generated.rs",
        "src/livekit/osis_server_generated.rs",
    ];
    for file in &generated_files {
        let path = std::path::Path::new(file);
        if path.exists() {
            let _ = std::process::Command::new("rustfmt")
                .args(["--edition", "2024"])
                .arg(path)
                .status();
        }
    }

    let _ = std::fs::copy("src/livekit/openrpc.json", "openrpc.json");
    println!("cargo:rerun-if-changed=src/livekit/openrpc.json");
}

No .with_wasm(), no .client_crate_dir(...), no .sdk_dir(...), no .single_bin(...). The simplest possible config plus a sync-copy of openrpc.json to the crate root.

Dependencies: none — but the build won't succeed until Step 3 (Cargo.toml has the build-dep).

Step 3: Update hero_livekit_server/Cargo.toml

Files (modify): crates/hero_livekit_server/Cargo.toml

Subtasks:

  1. Remove hero_livekit = { workspace = true }.
  2. Add build = "build.rs" to [package].
  3. Add deps required by the moved rpc.rs: reqwest, jsonwebtoken, flate2, tar, dirs = "6", herolib_sid = { workspace = true }. Keep hero_rpc_server (still used by main.rs).
  4. Add [build-dependencies] hero_rpc_osis = { workspace = true }.
  5. Drop the [features] block (single domain, no flag needed).

Dependencies: Step 1.

Step 4: Rewrite hero_livekit_server src/livekit module + main.rs

Files (modify/create):

  • crates/hero_livekit_server/src/livekit/mod.rs
  • crates/hero_livekit_server/src/livekit/server/mod.rs
  • crates/hero_livekit_server/src/livekit/types.rs
  • crates/hero_livekit_server/src/livekit/core/mod.rs
  • crates/hero_livekit_server/src/main.rs
  • crates/hero_livekit_server/src/lib.rs

Subtasks:

  1. src/livekit/mod.rs: pub mod core; pub use core::*; pub mod server; #[cfg(test)] mod tests;
  2. src/livekit/server/mod.rs: compute bridge with #[path = "../osis_server_generated.rs"], #[path = "../rpc_generated.rs"], #[path = "../rpc.rs"] modules.
  3. src/livekit/types.rs: include!("types_generated.rs");
  4. src/livekit/core/mod.rs: cfg-gated bridge to types.rs (native) and types_wasm_generated.rs (wasm).
  5. src/main.rs: keep all OServer/HeroLifecycle wiring; use crate::livekit::* not hero_livekit::livekit::*. Drop mod services; if src/services/ deleted.
  6. src/lib.rs: keep pub mod livekit;; drop pub mod services; if deleted.

Dependencies: Step 1, Step 2.

Step 5: Delete the old core crate

Files (delete): crates/hero_livekit/ (entire directory)

Subtasks:

  1. Verify Step 1's harvest is complete and committed in working tree.
  2. Confirm no other handwritten code lives in crates/hero_livekit/ (verified during spec generation: lib.rs, services/mod.rs, mod.rs files, types.rs, bin/hero_livekit.rs are all generated boilerplate or empty stubs; tests.rs is identical to the copy already in hero_livekit_server).
  3. rm -rf crates/hero_livekit/.

Dependencies: Step 1.

Step 6: Rewrite hero_livekit_sdk

Files (delete): crates/hero_livekit_sdk/build.rs, crates/hero_livekit_sdk/src/openrpc.rs, crates/hero_livekit_sdk/src/livekit/ (entire).

Files (modify): crates/hero_livekit_sdk/Cargo.toml, crates/hero_livekit_sdk/src/lib.rs.

Subtasks:

  1. Rewrite lib.rs modelled on hero_compute_sdk/src/lib.rs:
    • use hero_rpc_derive::openrpc_client;
    • pub use herolib_core::openrpc::{OpenRpcError, OpenRpcTransport};
    • openrpc_client!("../hero_livekit_server/openrpc.json", name = "LiveKitClient");
    • Helpers (substituting livekit for compute's cloud/compute): env_file_path, load_env, socket_dir, server_socket_path, ui_socket_path, HERO_PROXY_PORT, PROXY_SERVER_RPC_PREFIX, proxy_server_rpc_url, wrap_ipv6, normalize_rpc_method, read_http_response, http_rpc_tcp, http_rpc_unix.
    • Drop explorer-related helpers (no explorer in livekit).
    • Defer SERVER_RPC_PATH / connect_server_socket — current callers use LiveKitClient::connect_socket(...) which works as-is. Add the constant only if a real consumer needs it.
  2. Update Cargo.toml:
    [dependencies]
    hero_rpc_derive = { workspace = true }
    hero_rpc_openrpc = { workspace = true }
    herolib_core = { workspace = true, default-features = false }
    tokio = { workspace = true }
    serde = { workspace = true }
    serde_json = { workspace = true }
    dotenvy = { workspace = true }
    
    Drop hero_rpc_client, herolib_otoml, herolib_sid, [features], [lints.clippy].
  3. Delete the listed files.

Verification: openrpc_client!("../hero_livekit_server/openrpc.json", ...) resolves from crates/hero_livekit_sdk/ up one to crates/hero_livekit_server/openrpc.json. ✓ already exists.

Dependencies: none (parallel with Steps 2–4).

Step 7: Update consumer crates' imports

Files (modify):

  • crates/hero_livekit_examples/examples/basic_usage.rs
  • crates/hero_livekit_examples/examples/health.rs
  • crates/hero_livekit_rhai/src/client.rs

Subtasks:

  1. Replace use hero_livekit_sdk::openrpc::*; (or pub use hero_livekit_sdk::openrpc::*;) with use hero_livekit_sdk::*; (or pub use hero_livekit_sdk::*;).
  2. Optionally replace local socket_path() helpers with hero_livekit_sdk::server_socket_path().
  3. Method names emitted by the macro are unchanged — no rename work.

Dependencies: Step 6.

Step 8: Collapse hero_livekit_ui into a binary-only crate

Files (delete): crates/hero_livekit_ui/src/lib.rs
Files (modify): crates/hero_livekit_ui/Cargo.toml, crates/hero_livekit_ui/src/main.rs
Files (create): crates/hero_livekit_ui/src/server.rs (and optionally src/assets.rs)

Subtasks:

  1. Move the entire body of lib.rs (UiMeta, AppState, BasePath, IndexTemplate, JoinTemplate, socket_dir, service_socket_path, ui_socket_path, build_router, bind_unix_socket, all handlers) into src/server.rs.
  2. Wrap the orchestration logic from current main.rs into pub async fn run() -> anyhow::Result<()> in server.rs.
  3. Update imports in server.rs: use hero_livekit_sdk::*;.
  4. Rewrite src/main.rs (~10 lines): declare modules + server::run().await.
  5. Cargo.toml: remove [lib] section; keep [[bin]] and all current deps.

Dependencies: Step 6.

Step 9: Update workspace Cargo.toml

Files (modify): Cargo.toml (workspace root)

Subtasks:

  1. Remove "crates/hero_livekit", from [workspace] members.
  2. Remove hero_livekit = { path = "crates/hero_livekit" } from [workspace.dependencies].
  3. Add dotenvy = "0.15" to [workspace.dependencies].
  4. After verifying with cargo tree, optionally remove hero_rpc_client from workspace deps (only used by old SDK).

Dependencies: Step 5, Step 6.

Step 10: Verify

Subtasks:

  1. cargo build --workspace.
  2. cargo clippy --workspace --all-targets -- -D warnings.
  3. cargo test --workspace.
  4. grep -rn 'single_bin\|bin_companions\|bin_ui\|client_crate_dir\|sdk_types_crate\|with_wasm\|nested_layout\|autobins = false' . returns zero hits.
  5. grep -rn 'hero_livekit::livekit::' crates/ returns zero hits.
  6. grep -rn 'hero_livekit_sdk::livekit::\|hero_livekit_sdk::openrpc' crates/ returns zero hits.
  7. find crates/hero_livekit -type f returns nothing.
  8. cargo run --example basic_usage (against a local hero_livekit_server) as a smoke test.

Dependencies: all prior.

Parallelization Notes

  • Step 1 → Step 5 is the critical path (harvest then delete). Steps 2, 3, 4 follow Step 1 sequentially on the server crate.
  • Step 6 (SDK) runs in parallel with Steps 2–4 — different crate.
  • Step 7 (consumer imports) depends on Step 6.
  • Step 8 (UI) depends on Step 6, independent of Steps 1–5.
  • Step 9 depends on Step 5 and Step 6.
  • Step 10 is the final gate.

Acceptance Criteria

  • crates/hero_livekit/ directory does not exist.
  • crates/hero_livekit_server/build.rs matches the compute pattern (no single_bin, bin_companions, bin_ui, client_crate_dir, sdk_types_crate, with_wasm, nested_layout, sdk_dir).
  • crates/hero_livekit_server/src/livekit/rpc.rs contains the orchestrator (install, configure, start, stop, restart, status, room/participant ops, token signing, lifecycle triggers).
  • crates/hero_livekit_server/openrpc.json exists at the crate root and is consumed by openrpc_client! in the SDK.
  • crates/hero_livekit_sdk/src/lib.rs is a single file, no pub mod declarations beyond macro re-exports.
  • crates/hero_livekit_sdk/build.rs does not exist.
  • crates/hero_livekit_sdk/src/livekit/ and crates/hero_livekit_sdk/src/openrpc.rs do not exist.
  • crates/hero_livekit_ui/src/lib.rs does not exist; [lib] removed from its Cargo.toml.
  • crates/hero_livekit_ui/src/main.rs is ~10 lines and delegates to server::run().
  • Workspace Cargo.toml lists exactly: hero_livekit_server, hero_livekit_sdk, hero_livekit_backend, hero_livekit_ui, hero_livekit_examples, hero_livekit_rhai.
  • hero_livekit_backend source code is byte-identical to before.
  • hero_livekit_rhai only difference is the import line in client.rs.
  • cargo build --workspace, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace all green.
  • No reference to any of: single_bin, bin_companions, bin_ui, client_crate_dir, sdk_types_crate, with_wasm, nested_layout, autobins = false, hero_livekit::livekit::, hero_livekit_sdk::openrpc::, hero_livekit_sdk::livekit::.

Notes / risks / caveats

  1. SERVER_RPC_PATH constant: hero_livekit's OServer::run_cli + register_domain may serve a different HTTP path than hero_compute's /api/root/cloud/rpc. Current callers use LiveKitClient::connect_socket(...) which works as-is — recommend omitting SERVER_RPC_PATH/connect_server_socket from the new SDK and adding them later only if a real consumer needs them.

  2. openrpc.json dual location: the build.rs emits src/livekit/openrpc.json; the SDK macro reads crates/hero_livekit_server/openrpc.json (crate root). The new build.rs adds a std::fs::copy step to keep them in sync.

  3. Deleting crates/hero_livekit/src/livekit/server/rpc.rs is destructive: it is the only handwritten code in the entire core crate. Step 1 must succeed before Step 5 runs.

  4. #[cfg(feature = "livekit")] cleanup: the moved rpc.rs contains no feature-flag guards; safe to drop the livekit feature flag.

  5. Generated types_wasm_generated.rs: emitted by default; the cfg-gated core/mod.rs keeps it harmless on native targets.

  6. openrpc.client.generated.rs at the server crate root: side-effect file from the SDK macro. Existing behavior; no action.

  7. Cargo lock churn: removing hero_rpc_client, herolib_otoml and adding dotenvy will reshuffle Cargo.lock. Expected.

  8. Tests touching /tmp: crates/hero_livekit_server/src/livekit/tests.rs writes to /tmp/osis_test_* — already works in the server crate; the new bridge module exports OsisLivekit correctly.

  9. Backwards compatibility: anything that imported hero_livekit::livekit::core::Room directly from the old core crate breaks. Repo-wide grep confirms only consumers within this workspace did so, and they're all updated in Steps 4 & 7. External consumers (none known) would migrate to hero_livekit_sdk::Room.

## Implementation Spec for Issue #29 ### Objective Realign hero_livekit's workspace layout with hero_compute's simpler model: a single `hero_livekit_server` crate that owns `build.rs`, generates code into `src/livekit/`, and ships its `openrpc.json`; a one-file `hero_livekit_sdk` whose only Rust content is `openrpc_client!` plus socket/env helpers; deletion of the now-unused `crates/hero_livekit/` core crate; folding `hero_livekit_ui`'s library into its server module. No more `single_bin`, `bin_companions`, `bin_ui`, `client_crate_dir`, `sdk_types_crate`, `with_wasm`, `nested_layout`, or `autobins=false` workarounds. ### Requirements - `crates/hero_livekit/` deleted; its hand-written content (the orchestrator code in `src/livekit/server/rpc.rs`, ~962 lines) moves into `hero_livekit_server`. - `crates/hero_livekit_server/build.rs` exists with a minimal `OschemaBuildConfig` (compute-style: schemas_dir, docs_dir, single domain, generate_server, debug, plus a rustfmt step). - `crates/hero_livekit_server/openrpc.json` at the crate root remains the canonical spec consumed by `openrpc_client!`. The build also emits `src/livekit/openrpc.json`; both must be kept in sync (build.rs copy step). - `crates/hero_livekit_sdk/src/lib.rs` is a single file modelled on `crates/hero_compute_sdk/src/lib.rs`: `openrpc_client!("../hero_livekit_server/openrpc.json", name = "LiveKitClient")` plus env / socket helpers. No `build.rs`. No `src/livekit/` subtree. No `src/openrpc.rs`. - All consumer crates (server, ui, examples, rhai) update their imports so they no longer reach into `hero_livekit::livekit::*` or `hero_livekit_sdk::openrpc::*` / `hero_livekit_sdk::livekit::*`. - `hero_livekit_ui` collapses `lib.rs + main.rs` into a single binary crate with code under `src/server.rs` and `src/main.rs` ~10 lines, mirroring `hero_compute_ui`. The `[lib]` target is removed. - `hero_livekit_backend` and `hero_livekit_rhai` source code remain functionally unchanged (rhai gets a small import update only because it points through the SDK). - Workspace `Cargo.toml` no longer lists `crates/hero_livekit` as a member or `hero_livekit` as a workspace dep. - `cargo build`, `cargo clippy`, `cargo test` all pass against `--workspace`. - Repo-wide grep for `single_bin`, `bin_companions`, `bin_ui`, `client_crate_dir`, `sdk_types_crate`, `with_wasm`, `nested_layout`, `autobins = false`, `hero_livekit::livekit::`, and any path containing `crates/hero_livekit/` returns zero hits. ### Files to Modify/Create/Delete (high-level) | Path | Action | Notes | |---|---|---| | `crates/hero_livekit/` (whole directory) | **Delete** | After harvesting `src/livekit/server/rpc.rs` (handwritten orchestrator) | | `crates/hero_livekit/src/bin/hero_livekit.rs` | Delete | Auto-generated single-binary, broken & unused | | `crates/hero_livekit_server/build.rs` | **Create** | Compute-style minimal config | | `crates/hero_livekit_server/Cargo.toml` | **Modify** | Drop `hero_livekit` dep; add `reqwest`, `jsonwebtoken`, `flate2`, `tar`, `dirs`, `herolib_sid`; add `[build-dependencies] hero_rpc_osis = { workspace = true }`; add `build = "build.rs"` | | `crates/hero_livekit_server/src/main.rs` | **Modify** | Remove cross-crate `hero_livekit::livekit::*` imports; use `crate::livekit::*` | | `crates/hero_livekit_server/src/livekit/mod.rs` | **Rewrite** | `pub mod core; pub use core::*; pub mod server; #[cfg(test)] mod tests;` | | `crates/hero_livekit_server/src/livekit/server/mod.rs` | **Rewrite** | Compute bridge: `#[path = "../osis_server_generated.rs"]` etc. | | `crates/hero_livekit_server/src/livekit/rpc.rs` | **Create (move from old)** | The 962-line orchestrator | | `crates/hero_livekit_server/src/livekit/types.rs` | **Create** | `include!("types_generated.rs");` | | `crates/hero_livekit_server/src/livekit/{types,rpc,osis_server}_generated.rs` | Generated | By new build.rs | | `crates/hero_livekit_server/src/livekit/openrpc.json` | Generated | By new build.rs (copied to crate root) | | `crates/hero_livekit_sdk/build.rs` | **Delete** | The lib.rs patcher hack disappears | | `crates/hero_livekit_sdk/src/lib.rs` | **Rewrite** | Single file, compute-style helpers | | `crates/hero_livekit_sdk/src/openrpc.rs` | **Delete** | Folded into lib.rs | | `crates/hero_livekit_sdk/src/livekit/` | **Delete** | Whole subtree | | `crates/hero_livekit_sdk/Cargo.toml` | **Modify** | Compute-style deps, drop `hero_rpc_client`/`herolib_otoml`/`herolib_sid`/feature flags/lints | | `crates/hero_livekit_ui/Cargo.toml` | **Modify** | Drop `[lib]` target | | `crates/hero_livekit_ui/src/main.rs` | **Rewrite** | ~10 lines, delegates to `server::run()` | | `crates/hero_livekit_ui/src/lib.rs` | **Delete** | Content moved to `server.rs` | | `crates/hero_livekit_ui/src/server.rs` | **Create** | Owns build_router, AppState, handlers, run() | | `crates/hero_livekit_ui/src/whitelist.rs` | No change | | | `crates/hero_livekit_examples/examples/{basic_usage,health}.rs` | **Modify** | `use hero_livekit_sdk::openrpc::*` → `use hero_livekit_sdk::*` | | `crates/hero_livekit_rhai/src/client.rs` | **Modify** | Same import replacement | | `crates/hero_livekit_backend/` | No change | Independent | | `Cargo.toml` (workspace) | **Modify** | Drop `crates/hero_livekit` member + workspace dep; add `dotenvy` workspace dep | ### Implementation Plan #### Step 1: Harvest the handwritten orchestrator Files (read): `crates/hero_livekit/src/livekit/server/rpc.rs` (962 lines) Files (write): `crates/hero_livekit_server/src/livekit/rpc.rs` Subtasks: 1. Copy the full body of `crates/hero_livekit/src/livekit/server/rpc.rs` into `crates/hero_livekit_server/src/livekit/rpc.rs` verbatim. 2. Update imports inside the copy: `use crate::livekit::core::*;` and `pub use super::rpc_generated::*;`. 3. Verify there are no other `crate::` paths in the copied file that need updating. Dependencies: none. #### Step 2: Write the new server build.rs Files (write): `crates/hero_livekit_server/build.rs` Content (substituting `livekit` for compute's `cloud`): ```rust use hero_rpc_osis::build::{OschemaBuildConfig, OschemaBuilder}; fn main() { let config = OschemaBuildConfig::new() .schemas_dir("../../schemas") .docs_dir("../../docs/schemas") .domain("livekit", "Hero LiveKit -- SFU orchestration + room/participant management") .generate_server() .debug(true); OschemaBuilder::new(config) .generate() .expect("Failed to generate code from schemas"); let generated_files = [ "src/livekit/mod.rs", "src/livekit/types_generated.rs", "src/livekit/types_wasm_generated.rs", "src/livekit/rpc_generated.rs", "src/livekit/osis_server_generated.rs", ]; for file in &generated_files { let path = std::path::Path::new(file); if path.exists() { let _ = std::process::Command::new("rustfmt") .args(["--edition", "2024"]) .arg(path) .status(); } } let _ = std::fs::copy("src/livekit/openrpc.json", "openrpc.json"); println!("cargo:rerun-if-changed=src/livekit/openrpc.json"); } ``` No `.with_wasm()`, no `.client_crate_dir(...)`, no `.sdk_dir(...)`, no `.single_bin(...)`. The simplest possible config plus a sync-copy of `openrpc.json` to the crate root. Dependencies: none — but the build won't succeed until Step 3 (Cargo.toml has the build-dep). #### Step 3: Update hero_livekit_server/Cargo.toml Files (modify): `crates/hero_livekit_server/Cargo.toml` Subtasks: 1. Remove `hero_livekit = { workspace = true }`. 2. Add `build = "build.rs"` to `[package]`. 3. Add deps required by the moved `rpc.rs`: `reqwest`, `jsonwebtoken`, `flate2`, `tar`, `dirs = "6"`, `herolib_sid = { workspace = true }`. Keep `hero_rpc_server` (still used by `main.rs`). 4. Add `[build-dependencies] hero_rpc_osis = { workspace = true }`. 5. Drop the `[features]` block (single domain, no flag needed). Dependencies: Step 1. #### Step 4: Rewrite hero_livekit_server src/livekit module + main.rs Files (modify/create): - `crates/hero_livekit_server/src/livekit/mod.rs` - `crates/hero_livekit_server/src/livekit/server/mod.rs` - `crates/hero_livekit_server/src/livekit/types.rs` - `crates/hero_livekit_server/src/livekit/core/mod.rs` - `crates/hero_livekit_server/src/main.rs` - `crates/hero_livekit_server/src/lib.rs` Subtasks: 1. `src/livekit/mod.rs`: `pub mod core; pub use core::*; pub mod server; #[cfg(test)] mod tests;` 2. `src/livekit/server/mod.rs`: compute bridge with `#[path = "../osis_server_generated.rs"]`, `#[path = "../rpc_generated.rs"]`, `#[path = "../rpc.rs"]` modules. 3. `src/livekit/types.rs`: `include!("types_generated.rs");` 4. `src/livekit/core/mod.rs`: cfg-gated bridge to `types.rs` (native) and `types_wasm_generated.rs` (wasm). 5. `src/main.rs`: keep all OServer/HeroLifecycle wiring; use `crate::livekit::*` not `hero_livekit::livekit::*`. Drop `mod services;` if `src/services/` deleted. 6. `src/lib.rs`: keep `pub mod livekit;`; drop `pub mod services;` if deleted. Dependencies: Step 1, Step 2. #### Step 5: Delete the old core crate Files (delete): `crates/hero_livekit/` (entire directory) Subtasks: 1. Verify Step 1's harvest is complete and committed in working tree. 2. Confirm no other handwritten code lives in `crates/hero_livekit/` (verified during spec generation: lib.rs, services/mod.rs, mod.rs files, types.rs, bin/hero_livekit.rs are all generated boilerplate or empty stubs; tests.rs is identical to the copy already in hero_livekit_server). 3. `rm -rf crates/hero_livekit/`. Dependencies: Step 1. #### Step 6: Rewrite hero_livekit_sdk Files (delete): `crates/hero_livekit_sdk/build.rs`, `crates/hero_livekit_sdk/src/openrpc.rs`, `crates/hero_livekit_sdk/src/livekit/` (entire). Files (modify): `crates/hero_livekit_sdk/Cargo.toml`, `crates/hero_livekit_sdk/src/lib.rs`. Subtasks: 1. Rewrite `lib.rs` modelled on `hero_compute_sdk/src/lib.rs`: - `use hero_rpc_derive::openrpc_client;` - `pub use herolib_core::openrpc::{OpenRpcError, OpenRpcTransport};` - `openrpc_client!("../hero_livekit_server/openrpc.json", name = "LiveKitClient");` - Helpers (substituting `livekit` for compute's `cloud`/`compute`): `env_file_path`, `load_env`, `socket_dir`, `server_socket_path`, `ui_socket_path`, `HERO_PROXY_PORT`, `PROXY_SERVER_RPC_PREFIX`, `proxy_server_rpc_url`, `wrap_ipv6`, `normalize_rpc_method`, `read_http_response`, `http_rpc_tcp`, `http_rpc_unix`. - **Drop** explorer-related helpers (no explorer in livekit). - **Defer** `SERVER_RPC_PATH` / `connect_server_socket` — current callers use `LiveKitClient::connect_socket(...)` which works as-is. Add the constant only if a real consumer needs it. 2. Update `Cargo.toml`: ```toml [dependencies] hero_rpc_derive = { workspace = true } hero_rpc_openrpc = { workspace = true } herolib_core = { workspace = true, default-features = false } tokio = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } dotenvy = { workspace = true } ``` Drop `hero_rpc_client`, `herolib_otoml`, `herolib_sid`, `[features]`, `[lints.clippy]`. 3. Delete the listed files. Verification: `openrpc_client!("../hero_livekit_server/openrpc.json", ...)` resolves from `crates/hero_livekit_sdk/` up one to `crates/hero_livekit_server/openrpc.json`. ✓ already exists. Dependencies: none (parallel with Steps 2–4). #### Step 7: Update consumer crates' imports Files (modify): - `crates/hero_livekit_examples/examples/basic_usage.rs` - `crates/hero_livekit_examples/examples/health.rs` - `crates/hero_livekit_rhai/src/client.rs` Subtasks: 1. Replace `use hero_livekit_sdk::openrpc::*;` (or `pub use hero_livekit_sdk::openrpc::*;`) with `use hero_livekit_sdk::*;` (or `pub use hero_livekit_sdk::*;`). 2. Optionally replace local `socket_path()` helpers with `hero_livekit_sdk::server_socket_path()`. 3. Method names emitted by the macro are unchanged — no rename work. Dependencies: Step 6. #### Step 8: Collapse hero_livekit_ui into a binary-only crate Files (delete): `crates/hero_livekit_ui/src/lib.rs` Files (modify): `crates/hero_livekit_ui/Cargo.toml`, `crates/hero_livekit_ui/src/main.rs` Files (create): `crates/hero_livekit_ui/src/server.rs` (and optionally `src/assets.rs`) Subtasks: 1. Move the entire body of `lib.rs` (UiMeta, AppState, BasePath, IndexTemplate, JoinTemplate, socket_dir, service_socket_path, ui_socket_path, build_router, bind_unix_socket, all handlers) into `src/server.rs`. 2. Wrap the orchestration logic from current `main.rs` into `pub async fn run() -> anyhow::Result<()>` in `server.rs`. 3. Update imports in `server.rs`: `use hero_livekit_sdk::*;`. 4. Rewrite `src/main.rs` (~10 lines): declare modules + `server::run().await`. 5. Cargo.toml: remove `[lib]` section; keep `[[bin]]` and all current deps. Dependencies: Step 6. #### Step 9: Update workspace Cargo.toml Files (modify): `Cargo.toml` (workspace root) Subtasks: 1. Remove `"crates/hero_livekit",` from `[workspace] members`. 2. Remove `hero_livekit = { path = "crates/hero_livekit" }` from `[workspace.dependencies]`. 3. Add `dotenvy = "0.15"` to `[workspace.dependencies]`. 4. After verifying with `cargo tree`, optionally remove `hero_rpc_client` from workspace deps (only used by old SDK). Dependencies: Step 5, Step 6. #### Step 10: Verify Subtasks: 1. `cargo build --workspace`. 2. `cargo clippy --workspace --all-targets -- -D warnings`. 3. `cargo test --workspace`. 4. `grep -rn 'single_bin\|bin_companions\|bin_ui\|client_crate_dir\|sdk_types_crate\|with_wasm\|nested_layout\|autobins = false' .` returns zero hits. 5. `grep -rn 'hero_livekit::livekit::' crates/` returns zero hits. 6. `grep -rn 'hero_livekit_sdk::livekit::\|hero_livekit_sdk::openrpc' crates/` returns zero hits. 7. `find crates/hero_livekit -type f` returns nothing. 8. `cargo run --example basic_usage` (against a local hero_livekit_server) as a smoke test. Dependencies: all prior. ### Parallelization Notes - **Step 1 → Step 5** is the critical path (harvest then delete). Steps 2, 3, 4 follow Step 1 sequentially on the server crate. - **Step 6 (SDK)** runs in parallel with Steps 2–4 — different crate. - **Step 7 (consumer imports)** depends on Step 6. - **Step 8 (UI)** depends on Step 6, independent of Steps 1–5. - **Step 9** depends on Step 5 and Step 6. - **Step 10** is the final gate. ### Acceptance Criteria - [ ] `crates/hero_livekit/` directory does not exist. - [ ] `crates/hero_livekit_server/build.rs` matches the compute pattern (no `single_bin`, `bin_companions`, `bin_ui`, `client_crate_dir`, `sdk_types_crate`, `with_wasm`, `nested_layout`, `sdk_dir`). - [ ] `crates/hero_livekit_server/src/livekit/rpc.rs` contains the orchestrator (install, configure, start, stop, restart, status, room/participant ops, token signing, lifecycle triggers). - [ ] `crates/hero_livekit_server/openrpc.json` exists at the crate root and is consumed by `openrpc_client!` in the SDK. - [ ] `crates/hero_livekit_sdk/src/lib.rs` is a single file, no `pub mod` declarations beyond macro re-exports. - [ ] `crates/hero_livekit_sdk/build.rs` does not exist. - [ ] `crates/hero_livekit_sdk/src/livekit/` and `crates/hero_livekit_sdk/src/openrpc.rs` do not exist. - [ ] `crates/hero_livekit_ui/src/lib.rs` does not exist; `[lib]` removed from its `Cargo.toml`. - [ ] `crates/hero_livekit_ui/src/main.rs` is ~10 lines and delegates to `server::run()`. - [ ] Workspace `Cargo.toml` lists exactly: `hero_livekit_server`, `hero_livekit_sdk`, `hero_livekit_backend`, `hero_livekit_ui`, `hero_livekit_examples`, `hero_livekit_rhai`. - [ ] `hero_livekit_backend` source code is byte-identical to before. - [ ] `hero_livekit_rhai` only difference is the import line in `client.rs`. - [ ] `cargo build --workspace`, `cargo clippy --workspace --all-targets -- -D warnings`, `cargo test --workspace` all green. - [ ] No reference to any of: `single_bin`, `bin_companions`, `bin_ui`, `client_crate_dir`, `sdk_types_crate`, `with_wasm`, `nested_layout`, `autobins = false`, `hero_livekit::livekit::`, `hero_livekit_sdk::openrpc::`, `hero_livekit_sdk::livekit::`. ### Notes / risks / caveats 1. **`SERVER_RPC_PATH` constant**: hero_livekit's `OServer::run_cli` + `register_domain` may serve a different HTTP path than hero_compute's `/api/root/cloud/rpc`. Current callers use `LiveKitClient::connect_socket(...)` which works as-is — recommend omitting `SERVER_RPC_PATH`/`connect_server_socket` from the new SDK and adding them later only if a real consumer needs them. 2. **`openrpc.json` dual location**: the build.rs emits `src/livekit/openrpc.json`; the SDK macro reads `crates/hero_livekit_server/openrpc.json` (crate root). The new build.rs adds a `std::fs::copy` step to keep them in sync. 3. **Deleting `crates/hero_livekit/src/livekit/server/rpc.rs` is destructive**: it is the only handwritten code in the entire core crate. Step 1 must succeed before Step 5 runs. 4. **`#[cfg(feature = "livekit")]` cleanup**: the moved `rpc.rs` contains no feature-flag guards; safe to drop the `livekit` feature flag. 5. **Generated `types_wasm_generated.rs`**: emitted by default; the cfg-gated `core/mod.rs` keeps it harmless on native targets. 6. **`openrpc.client.generated.rs` at the server crate root**: side-effect file from the SDK macro. Existing behavior; no action. 7. **Cargo lock churn**: removing `hero_rpc_client`, `herolib_otoml` and adding `dotenvy` will reshuffle `Cargo.lock`. Expected. 8. **Tests touching `/tmp`**: `crates/hero_livekit_server/src/livekit/tests.rs` writes to `/tmp/osis_test_*` — already works in the server crate; the new bridge module exports `OsisLivekit` correctly. 9. **Backwards compatibility**: anything that imported `hero_livekit::livekit::core::Room` directly from the old core crate breaks. Repo-wide grep confirms only consumers within this workspace did so, and they're all updated in Steps 4 & 7. External consumers (none known) would migrate to `hero_livekit_sdk::Room`.
Author
Owner

Test Results

  • cargo build --workspace: PASS
  • cargo clippy --workspace --all-targets -- -D warnings: PASS
  • cargo test --workspace: PASS

Test breakdown

Crate Tests Result
hero_livekit_server (lib) 15 (4 CRUD + 11 authz) PASS
hero_livekit_server (bin) 15 (4 CRUD + 11 authz) PASS
hero_livekit_sdk (doc-test) 1 PASS
hero_livekit_server (doc-tests) 0 / 2 ignored PASS
hero_livekit_ui, hero_livekit_rhai, hero_livekit_examples, hero_livekit_backend 0 PASS

Total: 31 passed, 0 failed, 2 doc-tests ignored.

Acceptance criteria — all green

  • crates/hero_livekit/ no longer exists
  • crates/hero_livekit_server/build.rs exists with the minimal compute-style config
  • crates/hero_livekit_server/openrpc.json is the single source of truth for SDK consumers
  • crates/hero_livekit_sdk/src/lib.rs is one file, no build.rs, no livekit/ subtree
  • Repo-wide grep confirms zero references to single_bin, bin_companions, bin_ui, client_crate_dir, sdk_types_crate, with_wasm, nested_layout, autobins = false
  • Zero references to hero_livekit::livekit::*
  • Zero references to hero_livekit_sdk::openrpc::* or hero_livekit_sdk::livekit::*
  • Final workspace members: hero_livekit_server, hero_livekit_sdk, hero_livekit_backend, hero_livekit_ui, hero_livekit_examples, hero_livekit_rhai
  • hero_livekit_backend source unchanged
  • hero_livekit_rhai source unchanged except for the SDK import path

Deviations from the spec

  • Build.rs writes livekit/mod.rs and livekit/core/mod.rs after the OschemaBuilder run. The current generator emits a flat-layout mod.rs containing pub use self as core; and pub use self as server;, both of which are invalid Rust syntax. The build.rs now overwrites mod.rs with the canonical nested-bridge form (matching hero_compute_server) and creates core/mod.rs (the cfg-gated bridge to types.rs / types_wasm_generated.rs). This is the same shape hero_compute_server ships, but committed via build.rs rather than by hand.
  • Build.rs patches osis_server_generated.rs to fix an incorrect include_str!("../core/openrpc.json") emitted by the generator. The replacement points at openrpc.json next to the file, which is where the generator writes it.
  • Workspace herolib_core dep updated to default-features = false, features = ["openrpc-transport"] to match the SDK override (matches hero_compute's workspace).
  • Workspace dotenvy = "0.15" added (used by the SDK).
  • Removed dead WhitelistState::new() async ctor in hero_livekit_ui/src/whitelist.rs (callers use new_blocking() only; cargo clippy -- -D warnings flagged it).
## Test Results - `cargo build --workspace`: PASS - `cargo clippy --workspace --all-targets -- -D warnings`: PASS - `cargo test --workspace`: PASS ### Test breakdown | Crate | Tests | Result | |---|---|---| | hero_livekit_server (lib) | 15 (4 CRUD + 11 authz) | PASS | | hero_livekit_server (bin) | 15 (4 CRUD + 11 authz) | PASS | | hero_livekit_sdk (doc-test) | 1 | PASS | | hero_livekit_server (doc-tests) | 0 / 2 ignored | PASS | | hero_livekit_ui, hero_livekit_rhai, hero_livekit_examples, hero_livekit_backend | 0 | PASS | **Total: 31 passed, 0 failed, 2 doc-tests ignored.** ### Acceptance criteria — all green - `crates/hero_livekit/` no longer exists - `crates/hero_livekit_server/build.rs` exists with the minimal compute-style config - `crates/hero_livekit_server/openrpc.json` is the single source of truth for SDK consumers - `crates/hero_livekit_sdk/src/lib.rs` is one file, no `build.rs`, no `livekit/` subtree - Repo-wide grep confirms zero references to `single_bin`, `bin_companions`, `bin_ui`, `client_crate_dir`, `sdk_types_crate`, `with_wasm`, `nested_layout`, `autobins = false` - Zero references to `hero_livekit::livekit::*` - Zero references to `hero_livekit_sdk::openrpc::*` or `hero_livekit_sdk::livekit::*` - Final workspace members: `hero_livekit_server`, `hero_livekit_sdk`, `hero_livekit_backend`, `hero_livekit_ui`, `hero_livekit_examples`, `hero_livekit_rhai` - `hero_livekit_backend` source unchanged - `hero_livekit_rhai` source unchanged except for the SDK import path ### Deviations from the spec - **Build.rs writes `livekit/mod.rs` and `livekit/core/mod.rs`** after the OschemaBuilder run. The current generator emits a flat-layout `mod.rs` containing `pub use self as core;` and `pub use self as server;`, both of which are invalid Rust syntax. The build.rs now overwrites `mod.rs` with the canonical nested-bridge form (matching `hero_compute_server`) and creates `core/mod.rs` (the cfg-gated bridge to `types.rs` / `types_wasm_generated.rs`). This is the same shape `hero_compute_server` ships, but committed via build.rs rather than by hand. - **Build.rs patches `osis_server_generated.rs`** to fix an incorrect `include_str!("../core/openrpc.json")` emitted by the generator. The replacement points at `openrpc.json` next to the file, which is where the generator writes it. - **Workspace `herolib_core` dep** updated to `default-features = false, features = ["openrpc-transport"]` to match the SDK override (matches `hero_compute`'s workspace). - **Workspace `dotenvy = "0.15"`** added (used by the SDK). - **Removed dead `WhitelistState::new()` async ctor** in `hero_livekit_ui/src/whitelist.rs` (callers use `new_blocking()` only; `cargo clippy -- -D warnings` flagged it).
Author
Owner

Implementation Summary

Workspace shape — before vs after

Before (post PR #18):

  • 7 crates: hero_livekit (core w/ build.rs), hero_livekit_server, hero_livekit_sdk (with build.rs hack + livekit/ subtree), hero_livekit_ui (lib + bin), hero_livekit_examples, hero_livekit_rhai, hero_livekit_backend
  • Aspirational OschemaBuildConfig chain: single_bin, bin_companions, bin_ui, client_crate_dir, sdk_types_crate, with_wasm, nested_layout, sdk_dir
  • Auto-generated src/bin/hero_livekit.rs orchestrator that needed autobins = false to compile

After (this branch):

  • 6 crates: hero_livekit_server (owns build.rs + generated code), hero_livekit_sdk (one-file lib), hero_livekit_ui (bin-only), hero_livekit_examples, hero_livekit_rhai, hero_livekit_backend
  • Minimal OschemaBuildConfig chain: schemas_dir, docs_dir, domain, generate_server, debug — matches hero_compute_server
  • No orchestrator binary, no aspirational config flags

Changes by crate

crates/hero_livekit/ — deleted entirely.

  • The 962-line handwritten livekit/server/rpc.rs orchestrator (install / configure / start / stop / restart / status / room+participant ops / token signing / lifecycle triggers) was harvested into hero_livekit_server/src/livekit/rpc.rs.

crates/hero_livekit_server/ — refactored to own all server-side code.

  • New build.rs with compute-style minimal config, plus three local-only fixes for generator quirks: rewrites the broken generator-emitted livekit/mod.rs, creates the livekit/core/mod.rs bridge, and patches the bad include_str!("../core/openrpc.json") in the generated server file.
  • Cargo.toml: dropped hero_livekit dep, added reqwest, jsonwebtoken, flate2, tar, dirs, herolib_sid, plus [build-dependencies] hero_rpc_osis. Added [features] default = ["livekit"]; livekit = [] to mirror compute and silence unexpected_cfgs.
  • livekit/server/mod.rs is the compute bridge pattern (path-includes the flat generated files + handwritten rpc.rs).
  • openrpc.json lives at the crate root (synced from src/livekit/openrpc.json by build.rs).

crates/hero_livekit_sdk/ — collapsed to a single file.

  • src/lib.rs is now ~270 lines modeled on hero_compute_sdk/src/lib.rs: openrpc_client!("../hero_livekit_server/openrpc.json", name = "LiveKitClient"), plus env / socket / proxy / HTTP-RPC helpers.
  • build.rs, src/openrpc.rs, and the entire src/livekit/ subtree are gone.
  • Cargo.toml trimmed to compute's dep set (hero_rpc_derive, hero_rpc_openrpc, herolib_core, tokio, serde, serde_json, dotenvy); dropped hero_rpc_client, herolib_otoml, herolib_sid, the [features] block, and [lints.clippy].

crates/hero_livekit_ui/ — collapsed lib + bin into bin-only.

  • src/lib.rs deleted; contents moved to src/server.rs with a pub async fn run() -> anyhow::Result<()> entry point.
  • src/main.rs is now 9 lines: mod assets; mod server; mod whitelist; then server::run().await.
  • src/assets.rs extracted as a small rust_embed module (mirrors hero_compute_ui).
  • [lib] target removed from Cargo.toml. All deps preserved.
  • Removed dead WhitelistState::new() async ctor; only new_blocking() remains (the one used by the router).

crates/hero_livekit_examples/, crates/hero_livekit_rhai/ — single-line import fix: hero_livekit_sdk::openrpc::*hero_livekit_sdk::*. No behavior change.

crates/hero_livekit_backend/ — untouched.

Workspace Cargo.toml — removed crates/hero_livekit from members and the hero_livekit workspace dep. Added dotenvy = "0.15". Updated herolib_core to default-features = false, features = ["openrpc-transport"] to match compute and the SDK's override pattern.

File-level diff stat (main..HEAD)

Test results

  • cargo build --workspace: PASS
  • cargo clippy --workspace --all-targets -- -D warnings: PASS
  • cargo test --workspace: PASS — 31 tests passed, 0 failed, 2 doc-tests ignored

Notes / caveats

  • The SERVER_RPC_PATH constant and connect_server_socket() impl described in the spec were intentionally deferred — current callers use LiveKitClient::connect_socket(...) (the macro default), and adding them now would be speculative. They can be added in a follow-up if a real consumer needs a non-default path.
  • The build.rs workarounds for the OschemaBuilder generator (overwrite of mod.rs, manual core/mod.rs bridge, include_str! patch) are local, contained, and documented inline. If/when the generator is fixed upstream, those three blocks can be removed.
  • Branch: refactor/align-with-hero-compute. Commit not yet created — awaiting user approval.
## Implementation Summary ### Workspace shape — before vs after **Before** (post PR #18): - 7 crates: `hero_livekit` (core w/ build.rs), `hero_livekit_server`, `hero_livekit_sdk` (with build.rs hack + `livekit/` subtree), `hero_livekit_ui` (lib + bin), `hero_livekit_examples`, `hero_livekit_rhai`, `hero_livekit_backend` - Aspirational `OschemaBuildConfig` chain: `single_bin`, `bin_companions`, `bin_ui`, `client_crate_dir`, `sdk_types_crate`, `with_wasm`, `nested_layout`, `sdk_dir` - Auto-generated `src/bin/hero_livekit.rs` orchestrator that needed `autobins = false` to compile **After** (this branch): - 6 crates: `hero_livekit_server` (owns `build.rs` + generated code), `hero_livekit_sdk` (one-file lib), `hero_livekit_ui` (bin-only), `hero_livekit_examples`, `hero_livekit_rhai`, `hero_livekit_backend` - Minimal `OschemaBuildConfig` chain: `schemas_dir`, `docs_dir`, `domain`, `generate_server`, `debug` — matches `hero_compute_server` - No orchestrator binary, no aspirational config flags ### Changes by crate **`crates/hero_livekit/` — deleted entirely.** - The 962-line handwritten `livekit/server/rpc.rs` orchestrator (install / configure / start / stop / restart / status / room+participant ops / token signing / lifecycle triggers) was harvested into `hero_livekit_server/src/livekit/rpc.rs`. **`crates/hero_livekit_server/` — refactored to own all server-side code.** - New `build.rs` with compute-style minimal config, plus three local-only fixes for generator quirks: rewrites the broken generator-emitted `livekit/mod.rs`, creates the `livekit/core/mod.rs` bridge, and patches the bad `include_str!("../core/openrpc.json")` in the generated server file. - `Cargo.toml`: dropped `hero_livekit` dep, added `reqwest`, `jsonwebtoken`, `flate2`, `tar`, `dirs`, `herolib_sid`, plus `[build-dependencies] hero_rpc_osis`. Added `[features] default = ["livekit"]; livekit = []` to mirror compute and silence `unexpected_cfgs`. - `livekit/server/mod.rs` is the compute bridge pattern (path-includes the flat generated files + handwritten `rpc.rs`). - `openrpc.json` lives at the crate root (synced from `src/livekit/openrpc.json` by `build.rs`). **`crates/hero_livekit_sdk/` — collapsed to a single file.** - `src/lib.rs` is now ~270 lines modeled on `hero_compute_sdk/src/lib.rs`: `openrpc_client!("../hero_livekit_server/openrpc.json", name = "LiveKitClient")`, plus env / socket / proxy / HTTP-RPC helpers. - `build.rs`, `src/openrpc.rs`, and the entire `src/livekit/` subtree are gone. - `Cargo.toml` trimmed to compute's dep set (`hero_rpc_derive`, `hero_rpc_openrpc`, `herolib_core`, `tokio`, `serde`, `serde_json`, `dotenvy`); dropped `hero_rpc_client`, `herolib_otoml`, `herolib_sid`, the `[features]` block, and `[lints.clippy]`. **`crates/hero_livekit_ui/` — collapsed lib + bin into bin-only.** - `src/lib.rs` deleted; contents moved to `src/server.rs` with a `pub async fn run() -> anyhow::Result<()>` entry point. - `src/main.rs` is now 9 lines: `mod assets; mod server; mod whitelist;` then `server::run().await`. - `src/assets.rs` extracted as a small `rust_embed` module (mirrors `hero_compute_ui`). - `[lib]` target removed from `Cargo.toml`. All deps preserved. - Removed dead `WhitelistState::new()` async ctor; only `new_blocking()` remains (the one used by the router). **`crates/hero_livekit_examples/`, `crates/hero_livekit_rhai/`** — single-line import fix: `hero_livekit_sdk::openrpc::*` → `hero_livekit_sdk::*`. No behavior change. **`crates/hero_livekit_backend/`** — untouched. **Workspace `Cargo.toml`** — removed `crates/hero_livekit` from members and the `hero_livekit` workspace dep. Added `dotenvy = "0.15"`. Updated `herolib_core` to `default-features = false, features = ["openrpc-transport"]` to match compute and the SDK's override pattern. ### File-level diff stat (`main..HEAD`) ``` ``` ### Test results - `cargo build --workspace`: PASS - `cargo clippy --workspace --all-targets -- -D warnings`: PASS - `cargo test --workspace`: PASS — 31 tests passed, 0 failed, 2 doc-tests ignored ### Notes / caveats - The `SERVER_RPC_PATH` constant and `connect_server_socket()` impl described in the spec were intentionally **deferred** — current callers use `LiveKitClient::connect_socket(...)` (the macro default), and adding them now would be speculative. They can be added in a follow-up if a real consumer needs a non-default path. - The `build.rs` workarounds for the `OschemaBuilder` generator (overwrite of `mod.rs`, manual `core/mod.rs` bridge, `include_str!` patch) are local, contained, and documented inline. If/when the generator is fixed upstream, those three blocks can be removed. - Branch: `refactor/align-with-hero-compute`. Commit not yet created — awaiting user approval.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_livekit#29
No description provided.