Scripts can now read group-inherited config via vars::get(key) / vars::all(). - shared: VarsService trait (read-only get/all) + NoopVarsService; added as the 14th field on the Services bundle. - manager-core: VarsServiceImpl resolves the calling app's config via config_resolver (derives app_id from cx.app_id; AppVarsRead-gated for authed principals, script-as-gate for anon). - executor-core: vars:: Rhai bridge (get/all), registered in the SDK. - authz: AppVarsRead/Write, GroupVarsRead/Write, GroupSecretsRead/Write capabilities (group caps resolve via the Phase-2 ancestor walk; the GroupSecretsRead human-read gate is group_admin-only, distinct from an app's runtime injection). - wired VarsServiceImpl into the picloud binary; updated the executor-core test Services::new call sites. 386 manager-core lib tests green; clippy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
2.0 KiB
Rust
58 lines
2.0 KiB
Rust
//! `vars::` Rhai bridge — read-only access to the app's resolved,
|
|
//! group-inherited config (Phase 3).
|
|
//!
|
|
//! ```rhai
|
|
//! let region = vars::get("region"); // value or ()
|
|
//! let all = vars::all(); // #{ key: value, ... }
|
|
//! ```
|
|
//!
|
|
//! Values are inherited down the group tree and env-filtered (§3); the
|
|
//! resolution happens server-side in manager-core. Writes go through the
|
|
//! admin API, not the SDK. `app_id` is derived from `cx.app_id` in the
|
|
//! service — never a script argument — preserving cross-app isolation.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use picloud_shared::{SdkCallCx, Services};
|
|
use rhai::{Dynamic, Engine as RhaiEngine, EvalAltResult, Map, Module};
|
|
|
|
use super::bridge::{block_on, json_to_dynamic};
|
|
|
|
pub(super) fn register(engine: &mut RhaiEngine, services: &Services, cx: Arc<SdkCallCx>) {
|
|
let svc = services.vars.clone();
|
|
let mut module = Module::new();
|
|
|
|
// vars::get(key) — resolved value, or () if no level defines it.
|
|
{
|
|
let svc = svc.clone();
|
|
let cx = cx.clone();
|
|
module.set_native_fn(
|
|
"get",
|
|
move |key: &str| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
let svc = svc.clone();
|
|
let cx = cx.clone();
|
|
let opt = block_on("vars", async move { svc.get(&cx, key).await })?;
|
|
Ok(opt.map_or(Dynamic::UNIT, json_to_dynamic))
|
|
},
|
|
);
|
|
}
|
|
|
|
// vars::all() — the fully-resolved config map.
|
|
{
|
|
let svc = svc.clone();
|
|
let cx = cx.clone();
|
|
module.set_native_fn("all", move || -> Result<Map, Box<EvalAltResult>> {
|
|
let svc = svc.clone();
|
|
let cx = cx.clone();
|
|
let resolved = block_on("vars", async move { svc.all(&cx).await })?;
|
|
let mut m = Map::new();
|
|
for (k, v) in resolved {
|
|
m.insert(k.into(), json_to_dynamic(v));
|
|
}
|
|
Ok(m)
|
|
});
|
|
}
|
|
|
|
engine.register_static_module("vars", module.into());
|
|
}
|