Adopts the three SDK families convention documented in fai/platform/docs/architecture/sdks.md: each base SDK (module, plugin, client) gets a language suffix on dir + repo so polyglot variants can be added later without renaming. fai_studio_plugin_sdk → fai_plugin_sdk_rust fai-studio-plugin-sdk → fai-plugin-sdk fai/studio-plugin-sdk → fai/plugin-sdk-rust Signed-off-by: flemming-it <sf@flemming.it>
3.7 KiB
fai-plugin-sdk (Rust)
Rust SDK for writing F∆I plugins (Studio extensions). One of
three SDK families documented in
fai/platform/docs/architecture/sdks.md:
fai-module-sdk(Rust) — building flow modulesfai-plugin-sdk(Rust) — building plugins ← this cratefai-client-sdk(Dart) — building hub-talking clients
Each gets a language-suffix on its dir/repo so polyglot
variants can be added later without renaming the existing
ones (e.g. a future fai-plugin-sdk-go).
Status
v0.1 builds. Studio plugins targeting the studio-plugin
world declared in wit/studio-plugin.wit can be compiled to
wasm32-wasip2 against this crate today. The proof-of-
contract plugin is studio-theme-solarized in the
fai-plugins namespace.
What's NOT yet implemented:
- Hub side: a Component-Model loader path for
studio-plugin-world components + theInvokePlugingRPC RPC that Studio talks to. Tracked infai/platform/docs/advanced/plugin-sdk.md. - Studio side:
plugin_host.dartthat discovers installedstudio.*capabilities and dispatches. - Per-hook worlds: today's v0.1 ships a single world
(
studio-plugin) that exports all three hooks (theme,translate,output-view). A plugin only caring about one hook still has to declare empty / declined impls for the other two. v0.2 splits into one world per hook. #[plugin(...)]proc-macro: v0.1 ships a plainexport_plugin!macro that wraps the wit-bindgen-emitted__export_studio_plugin_impl!. The proc-macro that generates the WIT bindings + theGuestimpls in one pass arrives in v0.2.
Usage
# Cargo.toml of your plugin
[package]
name = "my_studio_plugin"
version = "0.1.0"
edition = "2021" # match SDK edition
[lib]
crate-type = ["cdylib"]
[dependencies]
fai-plugin-sdk = { git = "https://git.flemming.ai/fai/plugin-sdk-rust", branch = "main" }
// src/lib.rs of your plugin
use fai_plugin_sdk::{bindings, export_plugin};
type ColorScheme = bindings::fai::studio_plugin::plugin_types::ColorScheme;
type PluginError = bindings::fai::studio_plugin::plugin_types::PluginError;
type RenderedOutput = bindings::fai::studio_plugin::plugin_types::RenderedOutput;
struct MyPlugin;
impl bindings::exports::fai::studio_plugin::theme::Guest for MyPlugin {
fn theme_for(brightness: String) -> Result<ColorScheme, PluginError> {
// Return a ColorScheme or PluginError::Declined.
}
}
// v0.1: every plugin must declare all three hooks. Plugins
// that don't actually serve a hook return Declined.
impl bindings::exports::fai::studio_plugin::translate::Guest for MyPlugin {
fn translate(_: String, _: String, _: String) -> Result<String, PluginError> {
Err(PluginError::Declined("not a translate plugin".to_string()))
}
}
impl bindings::exports::fai::studio_plugin::output_view::Guest for MyPlugin {
fn render(_: String, _: Vec<u8>) -> Result<RenderedOutput, PluginError> {
Err(PluginError::Declined("not a view plugin".to_string()))
}
}
export_plugin!(MyPlugin);
Then:
cargo build --release --target wasm32-wasip2
fai pack . -o my-plugin-0.1.0.fai
WIT layout
wit/
├── studio-plugin.wit # main world, fai:studio-plugin@0.1.0
└── deps/
└── platform/
└── world.wit # fai:platform@1.0.0 (host imports)
Both files are verbatim copies of the authoritative versions
in fai/platform/wit/. When the contract changes upstream,
re-copy here in lock-step.
Why edition 2021
wit-bindgen 0.36 emits #[link_section] / unsafe-attribute
syntax that the edition-2024 compiler rejects. Bump when
moving to a wit-bindgen version with edition-2024-compatible
codegen.