diff --git a/crates/executor-core/src/sdk/mod.rs b/crates/executor-core/src/sdk/mod.rs
index 74a319c..5a17fb2 100644
--- a/crates/executor-core/src/sdk/mod.rs
+++ b/crates/executor-core/src/sdk/mod.rs
@@ -19,6 +19,7 @@ pub mod files;
pub mod http;
pub mod kv;
pub mod pubsub;
+pub mod secrets;
pub mod stdlib;
pub use bridge::{dynamic_to_json, json_to_dynamic};
@@ -41,5 +42,6 @@ pub fn register_all(engine: &mut RhaiEngine, services: &Services, cx: Arc) {
+ let svc = services.secrets.clone();
+ let mut module = Module::new();
+
+ // secrets::set(name, value) — overwrites if present.
+ {
+ let svc = svc.clone();
+ let cx = cx.clone();
+ module.set_native_fn(
+ "set",
+ move |name: &str, value: Dynamic| -> Result<(), Box> {
+ let json = dynamic_to_json(&value);
+ let svc = svc.clone();
+ let cx = cx.clone();
+ block_on(async move { svc.set(&cx, name, json).await })
+ },
+ );
+ }
+
+ // secrets::get(name) — decoded value, or () if missing.
+ {
+ let svc = svc.clone();
+ let cx = cx.clone();
+ module.set_native_fn(
+ "get",
+ move |name: &str| -> Result> {
+ let svc = svc.clone();
+ let cx = cx.clone();
+ let opt = block_on(async move { svc.get(&cx, name).await })?;
+ Ok(opt.map_or(Dynamic::UNIT, json_to_dynamic))
+ },
+ );
+ }
+
+ // secrets::delete(name) — bool was-present.
+ {
+ let svc = svc.clone();
+ let cx = cx.clone();
+ module.set_native_fn(
+ "delete",
+ move |name: &str| -> Result> {
+ let svc = svc.clone();
+ let cx = cx.clone();
+ block_on(async move { svc.delete(&cx, name).await })
+ },
+ );
+ }
+
+ // secrets::list(#{ cursor, limit }) — names only, cursor-paginated.
+ {
+ let svc = svc.clone();
+ let cx = cx.clone();
+ module.set_native_fn(
+ "list",
+ move |opts: Map| -> Result
{/if}
+
+ {#if revealConfirm}
+ (revealConfirm = false)}
+ >
+
+ The value you type will be shown in plain text on screen. Make sure no one
+ is looking over your shoulder and that screen-sharing is off.
+
+
+ {/if}
+
+ {#if secretToRemove}
+ (secretToRemove = null)}
+ >
+
+ Deleting {secretToRemove.name} is permanent. Any script calling
+ secrets::get("{secretToRemove.name}") will get ()
+ until you set it again.
+
+
+ {/if}
{/if}