imrove hero_aibroker_services #48
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_aibroker#48
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
rename
/Volumes/T7/code0/hero_aibroker/crates/hero_broker_server -> /Volumes/T7/code0/hero_aibroker/crates/hero_aibroker_services
make more modular, each of the services needs to
Implementation Spec for Issue #48
Objective
Rename
crates/hero_broker_servertocrates/hero_aibroker_servicesand refactor it into a modular crate where every search/scrape service lives in its own submodule directory undersrc/services/<name>/, each carrying its ownREADME.md, OpenRPC spec, and integration tests. Each service must, at startup, fetch its required API key from the hero_proc secret store (instead of reading raw OS env vars), and fail fast with a clear error if the secret is missing — so all secrets come from the canonical hero_proc store.Requirements
crates/hero_broker_server→crates/hero_aibroker_services.hero_broker_server→hero_aibroker_services; binary renamed tohero_aibroker_services.src/services/<name>/{mod.rs, README.md, openrpc.json}.tests/<name>.rsat the crate root.hero_proc_sdk) before launching the listener; missing secret causes a logged error and the service is skipped (other services keep running).hero_proc_sdk::HeroProcRPCAPIClient::secret_getwith context"core", never barestd::env::varfor API keys.Cargo.toml,buildenv.sh,Makefile,crates/hero_aibroker/src/main.rs(orchestrator), andREADME.md.Files to Modify/Create
Rename and structural moves (treat as deletes-from-old + creates-at-new):
crates/hero_broker_server/(entire directory) → renamed tocrates/hero_aibroker_services/. Inside the renamed crate, files are reorganized as below.Inside the renamed crate:
crates/hero_aibroker_services/Cargo.toml— package namehero_aibroker_services, binary namehero_aibroker_services, addhero_proc_sdkworkspace dep, adddirsdep (used for~/hero/var/socketsfallback), keep existing deps.crates/hero_aibroker_services/README.md— overview of the crate, list of services, secret requirements, how to run, how to test.crates/hero_aibroker_services/src/main.rs— slim binary: parse args, init tracing, buildHeroProcRPCAPIClient, dispatch to each service module'sstart()async fn (each returnsResult<Option<JoinHandle<...>>>), join all.crates/hero_aibroker_services/src/lib.rs— declarespub mod common; pub mod services;so integration tests can import shared helpers.crates/hero_aibroker_services/src/common/mod.rs— keep currentRpcRequest,rpc_ok,rpc_err,require_str. Add new helpers:pub async fn hero_proc_client() -> anyhow::Result<HeroProcRPCAPIClient>— connect usinghero_proc_factory()(mirrorcrates/hero_aibroker/src/main.rs).pub async fn require_secret(client: &HeroProcRPCAPIClient, key: &str) -> anyhow::Result<String>— callssecret_getwith context"core", returns error if value empty/missing.pub async fn serve_socket(name: String, socket_path: PathBuf, app: axum::Router) -> anyhow::Result<()>— moved from currentmain.rs.pub fn hero_socket_dir() -> PathBuf— moved from currentmain.rs.crates/hero_aibroker_services/src/services/mod.rs—pub mod ping; pub mod serpapi; pub mod serper; pub mod exa; pub mod scraperapi; pub mod scrapfly;.Per service (six dirs — same structure for each):
crates/hero_aibroker_services/src/services/ping/mod.rscrates/hero_aibroker_services/src/services/ping/README.mdcrates/hero_aibroker_services/src/services/ping/openrpc.jsoncrates/hero_aibroker_services/src/services/serpapi/{mod.rs,README.md,openrpc.json}crates/hero_aibroker_services/src/services/serper/{mod.rs,README.md,openrpc.json}crates/hero_aibroker_services/src/services/exa/{mod.rs,README.md,openrpc.json}crates/hero_aibroker_services/src/services/scraperapi/{mod.rs,README.md,openrpc.json}crates/hero_aibroker_services/src/services/scrapfly/{mod.rs,README.md,openrpc.json}Each
mod.rsexposes:pub const SERVICE_NAME: &str = ...pub const SECRET_KEYS: &[&str](canonical singular first, legacy plural as fallback; empty for ping)pub const OPENRPC_SPEC: &str = include_str!("openrpc.json")pub fn router(api_key: Option<String>) -> axum::Routerpub async fn start(client: &HeroProcRPCAPIClient, socket_dir: &Path) -> anyhow::Result<Option<JoinHandle<anyhow::Result<()>>>>— fetches the secret ifSECRET_KEYSis non-empty, logs and returnsOk(None)if missing (so other services start), spawnsserve_socket(...)otherwise.Integration tests:
crates/hero_aibroker_services/tests/common/mod.rs— shared helpers (spawn the renamed binary as child; wait for socket; build a hyper-over-UDS JSON-RPC POST helper).crates/hero_aibroker_services/tests/{ping,serpapi,serper,exa,scraperapi,scrapfly}.rsWorkspace and tooling updates (rename references):
Cargo.toml(workspace root) — replace"crates/hero_broker_server"with"crates/hero_aibroker_services"inmembers.buildenv.sh— replacehero_broker_serverwithhero_aibroker_servicesinBINARIES.Makefile— line 70: replacehero_proc status hero_broker_serverwithhero_proc status hero_aibroker_services.crates/hero_aibroker/src/main.rs— lines 282–372: rename binary lookup, action name, and health-check action name. Remove thefor env_name in [...] { broker_builder.env(...) }block (per-service binary now reads secrets directly from hero_proc).README.md(top level) — replace thehero_broker_servermentions withhero_aibroker_services.Implementation Plan
Step 1: Move and rename the crate skeleton
git mv crates/hero_broker_server crates/hero_aibroker_servicescrates/hero_aibroker_services/Cargo.toml: setname = "hero_aibroker_services",[[bin]] name = "hero_aibroker_services", addhero_proc_sdkanddirsdeps.Dependencies: none.
Step 2: Update workspace members and tooling references
Cargo.tomlline 19:"crates/hero_broker_server"→"crates/hero_aibroker_services".buildenv.shline 5: rename token inBINARIES.Makefileline 70: renamehero_proc statusarg.Dependencies: Step 1.
Step 3: Update orchestrator (
hero_aibrokerCLI) — register renamed binary, drop secret env forwardingcrates/hero_aibroker/src/main.rslines 282–372:"hero_broker_server"with"hero_aibroker_services"(binary path, action name, health-check action name).for env_name in ["SERPAPI_API_KEYS", ...] { broker_builder.env(...) }loop (lines 296–305). Replace with a comment:// API keys are read directly from hero_proc secret store by hero_aibroker_services.Dependencies: Step 1 (independent of Step 2 → can run in parallel).
Step 4: Restructure
src/intoservices/submodule treesrc/lib.rswithpub mod common; pub mod services;.git mv src/common.rs src/common/mod.rs. Extend withhero_proc_client(),require_secret(),serve_socket(),hero_socket_dir()helpers.src/services/mod.rsdeclaring the six submodules.<name>in {ping, serpapi, serper, exa, scraperapi, scrapfly}:git mv src/<name>.rs src/services/<name>/mod.rsgit mv openrpc/<name>.json src/services/<name>/openrpc.jsoninclude_str!path to"openrpc.json".SECRET_KEYSandstart()per-service entry point.std::env::var("*_API_KEYS")reads withapi_keyparameter passed in fromstart().openrpc/directory.src/main.rs: build hero_proc client, call eachservices::<name>::start(&hp, &socket_dir).await?, join handles.Dependencies: Step 1 (independent of Steps 2/3 → parallelizable).
Step 5: Add per-service READMEs
README.mdin eachsrc/services/<name>/describing: required hero_proc secret key, RPC methods, socket path, and a curl-via-UDS example.Dependencies: Step 4.
Step 6: Add crate-level README
crates/hero_aibroker_services/README.md— overview, services table, secret resolution rules, "How to add a new service" recipe, "Running tests".Dependencies: Step 1 (parallelizable with Step 5).
Step 7: Add integration tests (one per service)
tests/common/mod.rs—start_services_binary()(spawn binary with tempHERO_SOCKET_DIR),wait_for_socket(),rpc_call()over UDS.tests/ping.rs— callsping.ping,rpc.discover,/health,/openrpc.json. Always succeeds.tests/{serpapi,serper,exa,scraperapi,scrapfly}.rs— start binary; if secret absent in hero_proc, assert socket absent; if present, assert socket exists andrpc.discover//healthsucceed. No live external API calls.Dependencies: Step 4.
Step 8: Update top-level
README.mdhero_broker_serverwithhero_aibroker_services.Dependencies: Step 1.
Acceptance Criteria
crates/hero_aibroker_services; oldcrates/hero_broker_serverno longer exists.Cargo.tomllistscrates/hero_aibroker_services.hero_aibroker_services.src/services/<name>/with its ownmod.rs,README.md, andopenrpc.json.tests/<name>.rs.hero_proc_sdk::secret_get(...)at startup; missing secret yieldstracing::warn!and the service is skipped.std::env::var("*_API_KEYS"|"*_API_KEY")reads insidecrates/hero_aibroker_services/.hero_broker_serveranywhere in the repo (excludingCargo.lock).crates/hero_aibroker/src/main.rsregistershero_aibroker_servicesand no longer copies*_API_KEYSenv vars into the action.buildenv.shBINARIESlist containshero_aibroker_services.Makefilestatustarget referenceshero_aibroker_services.README.mdno longer referenceshero_broker_server.Notes
Canonical secret keys. Current code reads plural keys (
SERPAPI_API_KEYS,SERPER_API_KEYS,EXA_API_KEYS,SCRAPERAPI_API_KEYS,SCRAPFLY_API_KEYS). The hero_proc canonical secret keys use the singular form (EXA_API_KEY). Each service module looks up secrets in this priority order: canonical singular hero_proc key first, then legacy plural key as fallback. Each service exposes:and
require_secretreturns the first non-empty value found in hero_proc.Socket-name compatibility. Current sockets are named
hero_broker_<name>/rpc.sock. Renaming them would break downstream clients. This change keeps the on-disk socket directory names as they are (hero_broker_serpapi, etc.) — only crate, binary, and process names change. Thekill_otherlist incrates/hero_aibroker/src/main.rskeeps the existing names. Renaming sockets is a follow-up issue.OpenRPC format. JSON files (matching the rest of the repo), one
openrpc.jsonper service inside its own directory.hero_proc connectivity at startup. If
hero_proc_factory()fails (hero_proc not running), the binary logs a clear error and exits non-zero. Per-servicestart()only logswarnand returnsOk(None)for missing secrets; a hard hero_proc connectivity failure is a top-level error inmain.No new branch / no test-running step. All changes are made on the current working branch and committed in a single commit at the end.
cargo build/cargo testare NOT part of the implementation steps — they run in a separate later test phase.Cargo.lockwill be regenerated automatically by the nextcargoinvocation.Test Results
Build (
cargo build --workspace)FAIL —
hero_aibroker_services(lib) failed to compile due to 1 type-mismatch error. Tests were not run (per instructions, do not run tests against a broken build).First lines of error output:
Tests (
cargo test -p hero_aibroker_services)Not run (build failed).
Tests (
cargo test --workspace --no-fail-fast)Not run (build failed).
Build broken in
hero_aibroker_services—hero_proc_factory()now returns aHeroProcFactorybuilder (not a connected client), socrates/hero_aibroker_services/src/common/mod.rs:39-43no longer matches the declared return typeanyhow::Result<HeroProcRPCAPIClient>. The factory likely needs a terminating call (e.g..connect().awaitor equivalent) to produce the client. Until this is fixed, tests cannot be run.Test Results
Build (
cargo build --workspace)PASS — clean compile after the rename, including the orchestrator (
hero_aibroker) which now references the renamed binary.Tests (
cargo test -p hero_aibroker_services)The 6 integration tests cover the always-on
pingservice plus the five secret-gated services (serpapi,serper,exa,scraperapi,scrapfly). Each secret-gated test asserts the service is either up (when the corresponding hero_proc secret is present) or correctly skipped (when not), without taking down the rest of the binary. The tests skip with a stderr note when no hero_proc daemon is reachable on the host, so they remain CI-friendly.Tests (
cargo test --workspace --no-fail-fast)All builds and tests pass. No live external API calls are made by the new test suite.
Implementation Summary
Renamed crate
crates/hero_broker_server->crates/hero_aibroker_services(history preserved viagit mv).hero_broker_server->hero_aibroker_services.Modular per-service layout
Each of the six services now lives in its own directory under
crates/hero_aibroker_services/src/services/<name>/with:mod.rs(Rust code:SERVICE_NAME,SECRET_KEYS,OPENRPC_SPEC,router(...),start(...))README.md(per-service docs: required hero_proc secret, RPC method list, socket path, curl example)openrpc.json(the OpenRPC spec for that service)Services:
ping,serpapi,serper,exa,scraperapi,scrapfly.Secrets sourced from hero_proc
*_API_KEY/*_API_KEYSlookups now go throughhero_proc_sdk(secret_get, contextcore). Nostd::env::var(...)reads for API keys remain inside the crate.EXA_API_KEY), then the legacy plural key (e.g.EXA_API_KEYS) as a fallback for migration compatibility.tracing::warn!("skipping {service}: ...")and the service is skipped; the rest of the binary keeps running.crates/hero_aibroker/src/main.rs) no longer forwards*_API_KEYSenv vars into the spawned action — that responsibility moved into the services binary.Integration tests (one per service)
crates/hero_aibroker_services/tests/common/mod.rs— shared helpers: spawn the binary against a tempHERO_SOCKET_DIR, hyper-over-UDS RPC client, ping-based liveness check.tests/{ping,serpapi,serper,exa,scraperapi,scrapfly}.rs— one file per service.pingmust always be up; the secret-gated tests assert "up if secret set, cleanly skipped otherwise". No live external API calls. Tests gracefully skip when no hero_proc daemon is reachable on the host.Documentation
crates/hero_aibroker_services/README.md(overview, services table, secret-resolution rules, "How to add a new service" recipe, "Running tests" section).README.mdfiles undersrc/services/<name>/.README.mdupdated: every reference tohero_broker_serverreplaced withhero_aibroker_services.Workspace + tooling updates
Cargo.tomlmembers:crates/hero_broker_server->crates/hero_aibroker_services.buildenv.shBINARIESupdated.Makefilestatustarget updated.Backwards-compatibility note
On-disk socket directory names keep the existing
hero_broker_<service>/rpc.sockshape so downstream clients written against the old binary keep working. Renaming socket paths is left as a follow-up.Test results
cargo build --workspace-> PASS.cargo test -p hero_aibroker_services-> 6/6 pass.cargo test --workspace --no-fail-fast-> 37/37 pass.