Get started
Quick start
Install the oino CLI, run your first prompt, and embed the runtime in a Rust app.
Install the CLI#
macOS and Linux:
curl -fsSL https://oino.dev/install.sh | shWindows (PowerShell):
irm https://oino.dev/install.ps1 | iexThe installer downloads the release binaries for your platform, verifies their checksums, and installs oino into ~/.local/bin (override the prefix with OINO_PREFIX). The same scripts run from a source checkout.
From Rust, cargo install oino-cli also works. Caveat: a cargo-built binary ships no extension packages, and the bundled providers live in packages, so you would have no models until you install provider packages yourself (oino install <source>). Use the installer for a complete setup.
First prompt#
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY, OPENROUTER_API_KEY, ...
oino -p "Summarize this repository"Without -p you get the interactive terminal UI. /model picks a model, /settings opens settings, ctrl+d quits. oino --list-models shows every model you have credentials for.
Credentials resolve in this order: --api-key on the command line, ~/.oino/auth.json, then provider environment variables. See providers.
Other ways in:
oino --mode json "List the crates" # JSON event stream (CI, scripts)
oino --mode rpc # JSONL protocol on stdio
oino --mode acp # Agent Client Protocol, e.g. for ZedEmbed it in 40 lines#
Add the crates:
[dependencies]
oino-core = "1"
oino-client = "1"
indexmap = "2"
anyhow = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
futures = "0.3"Then create the services, a session runtime, and a backend that sends prompts and yields the event stream:
use std::{path::PathBuf, sync::Arc};
use futures::StreamExt;
use indexmap::IndexMap;
use oino_client::{AgentCommand, InProcessBackend, RpcCommand, SessionBackend};
use oino_core::extensions::api::ExtensionMode;
use oino_core::runtime::{AgentSessionRuntime, SessionOptions};
use oino_core::services::{ResourceFlags, Services, ServicesOptions, StaticLoader};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let services = Services::create(ServicesOptions {
cwd: std::env::current_dir()?,
agent_dir: PathBuf::from(
std::env::var("OINO_AGENT_DIR").unwrap_or_else(|_| {
format!("{}/.oino", std::env::var("HOME").unwrap())
}),
),
docs_root: PathBuf::new(),
mode: ExtensionMode::Print,
has_ui: false,
flags: ResourceFlags::default(),
extension_flag_values: IndexMap::new(),
loader: Arc::new(StaticLoader(Vec::new())), // no extensions here
approve: None,
models: None,
settings: None,
})
.await?;
let runtime = Arc::new(
AgentSessionRuntime::create(services, SessionOptions::default()).await?,
);
let backend = InProcessBackend::new(runtime);
backend
.send(RpcCommand::Agent(AgentCommand::Prompt {
message: "Summarize this repository".into(),
images: Vec::new(),
streaming_behavior: None,
}))
.await?;
let mut events = backend.events();
while let Some(event) = events.next().await {
println!("{event:?}");
}
backend.shutdown().await;
Ok(())
}SessionBackend (one command path in, one ordered event stream out, dialogs answered through ui_respond) is the same contract behind the TUI, the RPC server and the ACP adapter, so this program and the shipping clients are peers. The embedding page covers loading real extension packages, spawning the agent out of process, and the rest of the API.
Write an extension#
One file of Rust plus a manifest:
use oino_extension::prelude::*;
#[derive(Default)]
struct Hello;
#[derive(Deserialize, JsonSchema)]
struct HelloArgs { name: String }
impl Extension for Hello {
async fn init(&'static self, cx: &mut Init<Self>) -> Result<()> {
cx.tool(
Tool::new("hello")
.description("Greets a person by name")
.args::<HelloArgs>()
.run(|_ext, args: HelloArgs, _cx| async move {
Ok(ToolResult::text(format!("Hello, {}!", args.name)))
}),
);
Ok(())
}
}
oino_extension::export_extension!(Hello);Build for wasm32-wasip2, point the CLI at the package, and the hello tool is live:
rustup target add wasm32-wasip2
cargo build --target wasm32-wasip2
oino -e my-package-dirThe full walkthrough, including the manifest and permissions, is in extensions.