feat(vars): VarsService + vars:: SDK + config capabilities
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>
This commit is contained in:
@@ -26,6 +26,7 @@ pub mod retry;
|
||||
pub mod secrets;
|
||||
pub mod stdlib;
|
||||
pub mod users;
|
||||
pub mod vars;
|
||||
|
||||
pub use bridge::{dynamic_to_json, json_to_dynamic};
|
||||
pub use cx::SdkCallCx;
|
||||
@@ -62,6 +63,7 @@ pub fn register_all(
|
||||
queue::register(engine, services, cx.clone());
|
||||
retry::register(engine, services, cx.clone());
|
||||
secrets::register(engine, services, cx.clone());
|
||||
vars::register(engine, services, cx.clone());
|
||||
email::register(engine, services, cx.clone());
|
||||
users::register(engine, services, cx.clone());
|
||||
invoke::register(engine, services, cx, limits, self_engine);
|
||||
|
||||
57
crates/executor-core/src/sdk/vars.rs
Normal file
57
crates/executor-core/src/sdk/vars.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
//! `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());
|
||||
}
|
||||
Reference in New Issue
Block a user