Chapter 5: Hello, Pack!
TL;DR
- Create the pack:
worka init --tenant <your-github-username> --name my-first-pack --template hello-world
cd my-first-pack - Run the dev loop:
worka dev - Open Worka: Your pack appears under Development.
With your environment set up, you're ready for the most exciting part: creating and running your very first pack. This is the "Hello, World!" of Worka development.
Step 1: Scaffold Your Pack
Run:
worka init --tenant <your-github-username> --name my-first-pack --template hello-world
The CLI scaffolds a full working pack. Below is a complete example you can paste into your pack to see it running immediately.
Step 2: Complete Example (All Files)
aip.json
{
"tenant": "<your-github-username>",
"name": "my-first-pack",
"version": "0.1.0",
"display_name": "My First Pack",
"description": "A tiny pack with a view and a tool.",
"views": {
"home": { "title": "Hello Pack" }
},
"capabilities": [],
"dependencies": []
}
Cargo.toml
[package]
name = "worka_hello_world"
version = "0.1.0"
edition = "2024"
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
prost = "0.13"
serde_json = "1"
rmcp = { path = "/path/to/worka/sdks/rust/crates/rmcp" }
runtime-proto = { path = "/path/to/worka/crates/runtime-proto" }
src/lib.rs
use prost::Message;
use rmcp::ui::*;
use runtime_proto::runtime::{AbiEnvelope, AbiRequest, AbiResponse};
use serde_json::json;
#[unsafe(no_mangle)]
pub extern "C" fn worka_init() {}
#[unsafe(no_mangle)]
pub extern "C" fn worka_handle_request(ptr: u32, len: u32) -> u64 {
let req_bytes = unsafe { slice_from_raw(ptr, len) };
let envelope = AbiEnvelope::decode(req_bytes).unwrap();
let req = AbiRequest::decode(envelope.payload.as_slice()).unwrap();
let mut response = AbiResponse {
request_id: req.request_id,
payload: Vec::new(),
error: String::new(),
outbound_calls: Vec::new(),
};
if req.method == "init_views" {
response.payload = serde_json::to_vec(&build_views_payload()).unwrap();
} else {
response.error = format!("unsupported method: {}", req.method);
}
let mut buf = Vec::new();
response.encode(&mut buf).unwrap();
let out_ptr = worka_alloc(buf.len() as u32);
unsafe {
let out = slice_from_raw_mut(out_ptr, buf.len() as u32);
out.copy_from_slice(&buf);
}
pack_ptr_len(out_ptr, buf.len() as u32)
}
fn build_views_payload() -> serde_json::Value {
let view = build_hello_world_view();
json!({
"views": [
{ "id": "home", "title": "Hello Pack", "a2ui": view }
]
})
}
fn build_hello_world_view() -> serde_json::Value {
let header = Widget::new(WidgetKind::Text(Text {
text: literal("Hello from Worka"),
usage_hint: Some("h2".to_string()),
}))
.with_id("header");
let button_label = Widget::new(WidgetKind::Text(Text {
text: literal("Open dialog"),
usage_hint: None,
}))
.with_id("button-label");
let button = Widget::new(WidgetKind::Button(Button {
child: Box::new(button_label),
action: UiAction {
name: "open_dialog".to_string(),
context: vec![],
},
primary: Some(true),
}))
.with_id("button");
Widget::new(WidgetKind::Column(Column {
children: UiChildren::Items(vec![header, button]),
distribution: Some("start".to_string()),
alignment: Some("start".to_string()),
gap: Some(12.0),
}))
.to_json()
}
#[unsafe(no_mangle)]
pub extern "C" fn worka_alloc(len: u32) -> u32 {
let mut buf = Vec::<u8>::with_capacity(len as usize);
let ptr = buf.as_mut_ptr();
core::mem::forget(buf);
ptr as u32
}
fn pack_ptr_len(ptr: u32, len: u32) -> u64 {
((ptr as u64) << 32) | len as u64
}
unsafe fn slice_from_raw(ptr: u32, len: u32) -> &'static [u8] {
core::slice::from_raw_parts(ptr as *const u8, len as usize)
}
unsafe fn slice_from_raw_mut(ptr: u32, len: u32) -> &'static mut [u8] {
core::slice::from_raw_parts_mut(ptr as *mut u8, len as usize)
}
Replace
/path/to/workainCargo.tomlwith your local Worka repo path.
Step 3: Run the Dev Loop
worka dev
This single command kicks off a powerful development process:
- It immediately does an initial bundle of your pack.
- It starts a file watcher that monitors all your pack's source files for changes.
- It starts a local server to communicate with the main Worka Host application.
Any time you save a file, the CLI will rebuild the WASM and notify the Worka app to reload.
Step 4: See Your Pack in Worka
With the worka dev command still running in your terminal, open (or switch to) the main Worka desktop application.
In the sidebar on the left, you should see a new section labeled Development, and inside it, an item for "My First Pack". Click on it.
The main content area of Worka will now display the simple user interface from your pack’s init_views output.
Congratulations! You have successfully created, run, and viewed your first AI Pack.