hyperhive/hive-sh4re/src/assets.rs
iris 73684fb00a rust+nix: load static assets at runtime, drop build.rs (#555)
Cuts every `include_bytes!`/`include_str!` of a non-rust path in
the workspace over to runtime file loads from `$HIVE_ASSETS_DIR`
(the `hyperhive-assets` derivation introduced in the previous
commit). After this commit the rust derivation has no compile-time
dependency on `branding/*` or `hive-ag3nt/prompts/*` anymore.

Call-site flips:

- `hive-c0re/src/forge.rs::CORE_AVATAR_PNG` /
  `CONFIG_ORG_AVATAR_PNG`: were `include_bytes!` of
  `branding/hyperhive.png` and `$OUT_DIR/agent-configs.png`. Now
  `ensure_core_avatar` / `ensure_config_org_avatar` `tokio::fs::read`
  via `hive_sh4re::assets::{core_avatar_png, config_org_avatar_png}`
  at startup. The `agent-configs.png` is now rendered by the
  `hyperhive-assets` derivation's rsvg-convert step (was
  `hive-c0re/build.rs` + librsvg on the rust derivation's
  nativeBuildInputs — both gone in the next commit).
- `hive-ag3nt/src/prompt.rs::TEMPLATE`: `render` now takes the
  template as an argument; `write_system_prompt` reads it once from
  `$HIVE_ASSETS_DIR/prompts/system.md` before calling render. The
  test module still `include_str!`s the production template so
  `cargo test --workspace` doesn't need `HIVE_ASSETS_DIR` set —
  this is the only remaining compile-time reference to the file
  from the rust workspace, gated to `#[cfg(test)]`.
- `hive-ag3nt/src/turn.rs::CLAUDE_SETTINGS`: was `include_str!`'d
  and written via `tokio::fs::write`; now `tokio::fs::copy` from
  `$HIVE_ASSETS_DIR/prompts/claude-settings.json` into the
  per-agent socket dir.
- `hive-ag3nt/src/web_ui.rs::DEFAULT_ICON`: was `include_str!`'d;
  now read on-demand from `$HIVE_ASSETS_DIR/branding/hyperhive.svg`
  inside `serve_icon`. Falls back to an empty body if missing so
  the endpoint never panics on a misconfigured container (matches
  the existing "per-agent icon.svg override" fallthrough).

`HIVE_ASSETS_DIR` wiring:

- Inside containers: `nix/templates/harness-base.nix`
  `environment.variables` sets it to
  `${pkgs.hyperhive-assets}/share/hyperhive` (resolved through
  the default overlay applied in `mkContainer`). Verified by
  building `agent-base-toplevel` and grepping the resulting
  `/etc/set-environment`.
- Host-side: `nix/modules/hive-c0re.nix` adds an `assets` option
  defaulting to `hyperhive.packages.${system}.assets`, threaded
  in from the flake's nixosModules wiring, and sets the same env
  var on the `hive-c0re` systemd unit so the daemon's
  `forge::ensure_*_avatar` startup hooks find the PNGs.

`hive-c0re/build.rs` deleted entirely; `[package].build` removed
from `hive-c0re/Cargo.toml`; rsvg-convert dependency lives in the
assets derivation only.

Validated: `nix build .#default .#checks.x86_64-linux.clippy
.#agent-base-toplevel .#manager-toplevel --fallback` all succeed.
`/etc/set-environment` in the toplevel shows
`HIVE_ASSETS_DIR="/nix/store/.../hyperhive-assets-0.1.0/share/hyperhive"`.
2026-05-29 12:59:48 +02:00

99 lines
3.7 KiB
Rust

//! Resolve the on-disk path to hyperhive's static assets (branding +
//! claude prompts). Single source of truth for both the host daemon
//! (`hive-c0re`) and the in-container harness (`hive-ag3nt` /
//! `hive-m1nd`) so they agree on the lookup contract.
//!
//! At runtime, the path is read from `$HIVE_ASSETS_DIR`. In nix
//! builds that env var is set by the `hive-c0re` / `harness-base` modules
//! to `${pkgs.hyperhive-assets}/share/hyperhive` (see `nix/assets.nix`).
//! For `cargo run` outside nix, set it yourself:
//!
//! ```sh
//! HIVE_ASSETS_DIR=$(pwd)/dev-assets cargo run ...
//! ```
//!
//! where `dev-assets/` is laid out the same as the nix output:
//!
//! ```text
//! dev-assets/
//! branding/
//! hyperhive.{svg,png}
//! agent-configs.{svg,png}
//! prompts/
//! system.md
//! claude-settings.json
//! ```
//!
//! A convenience `cargo xtask seed-dev-assets` could materialise this
//! by copying `branding/` + `hive-ag3nt/prompts/` + rendering
//! `agent-configs.png` via rsvg-convert, but it's intentionally not
//! shipped yet — dev setups vary too much for one helper to fit.
//!
//! Each crate that wants a specific asset goes through one of the
//! typed helpers (`branding_svg()`, `prompt_template()`, …) so the
//! lookup contract is centralised. Missing files panic at first
//! call with a clear "set `HIVE_ASSETS_DIR` + put the file at …"
//! message — same failure shape as the old `include_*!` macros
//! (which would have failed at compile time, not runtime, but the
//! diagnostic value is identical).
use std::path::PathBuf;
/// Read `$HIVE_ASSETS_DIR`. Panics with a clear remediation message
/// when unset — every code path that calls into this module is one
/// the binary cannot run without, so a hard early failure is the
/// right shape.
fn dir() -> PathBuf {
match std::env::var("HIVE_ASSETS_DIR") {
Ok(v) if !v.is_empty() => PathBuf::from(v),
_ => panic!(
"HIVE_ASSETS_DIR is not set. Inside the nix-built systemd \
units this is wired automatically from \
`pkgs.hyperhive-assets`; for `cargo run` outside nix, \
set it to a directory laid out like \
`nix/assets.nix`'s output (branding/ + prompts/ \
subdirs). See hive-sh4re/src/assets.rs for the contract.",
),
}
}
/// `$HIVE_ASSETS_DIR/branding/hyperhive.svg` — the project's primary
/// mark. Loaded by the per-agent web UI as its default icon
/// (`/agents/<n>/icon.svg` falls through here when the agent didn't
/// override `hyperhive.icon` in its `agent.nix`).
#[must_use]
pub fn branding_svg() -> PathBuf {
dir().join("branding/hyperhive.svg")
}
/// `$HIVE_ASSETS_DIR/branding/hyperhive.png` — the same mark
/// rasterised, used by `hive-c0re::forge::push_avatar` to upload
/// the forge org avatar.
#[must_use]
pub fn core_avatar_png() -> PathBuf {
dir().join("branding/hyperhive.png")
}
/// `$HIVE_ASSETS_DIR/branding/agent-configs.png` — secondary org
/// mark for the `agent-configs/` mirror org. Rendered from
/// `agent-configs.svg` at asset-build time (was rendered in
/// `hive-c0re/build.rs` before #555).
#[must_use]
pub fn config_org_avatar_png() -> PathBuf {
dir().join("branding/agent-configs.png")
}
/// `$HIVE_ASSETS_DIR/prompts/system.md` — the claude system prompt
/// template. `hive-ag3nt::prompt::render` filters the role markers
/// inside it per agent / manager flavor.
#[must_use]
pub fn prompt_template() -> PathBuf {
dir().join("prompts/system.md")
}
/// `$HIVE_ASSETS_DIR/prompts/claude-settings.json` — the static
/// `--settings` JSON every claude invocation reads.
#[must_use]
pub fn claude_settings() -> PathBuf {
dir().join("prompts/claude-settings.json")
}