Verified against aomi-sdk@main on 2026-06-04.
cli feature builds aomi-build and aomi-git; the dev-runtime feature builds aomi-run. Install with --features cli alone and you get only aomi-build and aomi-git, not aomi-run. These binaries have no --version flag; run aomi-git --help (or --help on any of them) to confirm the install.
These are not standalone crates or npm packages, so there is no
aomi-run to find on crates.io or npm. They are binaries built from the aomi-sdk crate, which is why the install command points at that crate and uses feature flags to choose which binaries you get.aomi-build
Scaffold from an OpenAPI spec, compile the plugin, and run the end to end test. This is where an App is born.
aomi-run
Chat with your compiled plugin against a real LLM, locally, with no backend. This is how you sanity check tool behavior.
aomi-git
Deploy your source into a platform repo, watch CI, and activate the release on a backend. This is how an App ships.
You can run any of these without installing. Prefix with
cargo run -p aomi-sdk --features cli --bin aomi-build -- (or aomi-git), or --features dev-runtime --bin aomi-run --. The install is just a convenience.Which tool, when
Build
Use aomi-build to turn an OpenAPI spec into a compiled plugin (a
cdylib). It scaffolds the app crate, generates a typed client, stubs one tool per endpoint, and compiles. You then curate the tools by hand. End with a passing test.json.Run
Use aomi-run to chat with that compiled plugin locally. It loads the
.dylib, reads its manifest, and opens a REPL wired to Anthropic, OpenAI, or OpenRouter. No backend, no deploy. You watch which tools the model picks and how they respond.Ship
Use aomi-git to publish.
deploy stages your source into a platform repo (such as community-apps) and pushes to the publish branch, where CI builds and cuts a release. activate then tells a backend to load that release. You run activate yourself, using the per-app activation code ops issued you on your first request.aomi-build
aomi-build scaffolds, compiles, and end to end tests an App. The full path from “external API docs” to “tested plugin” is a six stage pipeline. Every stage runs on its own, and new-app is the one shot orchestrator for the first stages plus the compile.
test.json authoring in stage 6 are done with the authoring skills, not the binary.
Subcommands
| Subcommand | What it does |
|---|---|
compile | Build every app plugin into plugins/. The everyday build command. |
init <name> | Scaffold a bare app skeleton. Use when you are not driving from an OpenAPI spec. |
new-app <p> | Orchestrator: gen-specs then gen-client then gen-tool then cargo build. |
gen-specs <p> | Discover or fetch an OpenAPI spec and write the YAML. |
gen-client <p> | Turn the OpenAPI YAML into a typed Rust client via progenitor. |
gen-tool <p> | Scaffold the app crate and write one stub tool per operationId. |
tighten-spec <p> | Sharpen loose additionalProperties: true schemas from real captured samples. |
test-schema <p> | Validate the spec against the live API with schemathesis. |
<p> is the platform slug, for example petstore or khalani.
Flags
| Flag | Applies to | Meaning |
|---|---|---|
--app <name> | compile | Build a single app instead of all of them. |
--release | compile | Build in release mode. |
--target <triple> | compile | Cross compile for a target triple, for example aarch64-apple-darwin. |
--from-url <URL> | gen-specs, new-app | Direct spec URL when discovery does not find one. |
--shared | stages 1 through 3, new-app | Treat artifacts as shared under ext/ instead of app local under apps/. |
--no-tool | new-app | Stop after gen-client; skip tool scaffolding. |
--force | gen-client | Regenerate even when output already exists. |
--base-url <URL> | test-schema | Live API base URL to validate against. |
Spec generation stages default to app local: every artifact lives under
apps/<p>/. Pass --shared only when several Apps wrap the same upstream (say, multiple Apps over one exchange) and should reuse one client under ext/.Scaffold and compile a new App
- From an OpenAPI spec
- Bare skeleton
- Compile existing apps
new-app finishes, the App compiles but its tools are mechanical, one per endpoint, with machine names. You make it useful by curating the tool layer (stage 4) with the authoring skills, then rebuilding.
aomi-build compile builds the apps inside an aomi-sdk style workspace and writes them into plugins/. If you are building a single standalone App crate, the kind you publish to community-apps, you do not need aomi-build. Build it with cargo build --release and find the plugin in target/release/.Sharpen and validate the spec
The end to end test
Each App carries one canonical e2e spec atapps/<platform>/test.json. It describes a real LLM run: an optional wallet seed, a list of user prompts, the tools expected per turn, optional wallet callbacks, and a final state assertion. The runner lives in the backend repo, not here. You point it at your compiled plugin with an env var:
| Env var | Required | Purpose |
|---|---|---|
AOMI_E2E_APP_PATH | yes | Absolute path to the compiled dylib (or a manifest bundle directory). |
ANTHROPIC_API_KEY | yes | Provider key for the real LLM call. |
AOMI_E2E_SPEC | no | Override test.json discovery and run one explicit spec file. |
test.json shape (abridged)
test.json shape (abridged)
The spec runs turn by turn. Two limits worth knowing. Host tools (
expected_tools checks must_call (all listed) or any_of (at least one). final_assertion checks the user state, tool responses, and turn cap.stage_tx, simulate_batch, commit_txs) carry a model set topic arg, so listing them in must_call will not match; the runtime fires them internally during routed enforcement. And a terminal wallet:tx_complete callback consumes pending_txs, so assert max_count: 0 after a callback rather than min_count: 1.aomi-run
aomi-run loads a compiled plugin and opens an interactive REPL against a real LLM, locally, with no backend required. It is how you feel out whether the model reaches for the right tools before you ever ship.
There are no subcommands. You pass the plugin path as the one positional argument, then a handful of flags.
aomi-run prints a summary of what it loaded, stubs any host namespaces the plugin asked for, and opens the REPL:
tools line lists your App’s own tools. The namespaces line and the ⚙ stubbed line show the host capabilities the dev runtime stands in for, since the real backend is not present. Inside the REPL, /help lists commands and /quit exits.
Flags
| Flag | Default | Meaning |
|---|---|---|
<plugin> (positional) | required | Path to the built plugin (.dylib, .so, or .dll). |
--provider <P> | anthropic | LLM provider: anthropic, openai, or openrouter. |
--model <ID> | per provider | Model id. Defaults to a sane choice for the chosen provider. |
--max-turns <N> | 20 | Tool call rounds allowed inside one user turn before the model must answer in text. 0 means no tool round trips. |
--max-tokens <N> | 4096 | Cap on output tokens per LLM response. |
--env-file <FILE> | none | dotenv file loaded before any env var is read (API key and plugin secrets). |
--session-id <ID> | fresh UUID | Override the session id baked into every tool call context. |
--verbose, -v | off | More log detail. Sets a debug RUST_LOG if one is not already set. |
claude-sonnet-4-6 for Anthropic, gpt-5 for OpenAI, and anthropic/claude-sonnet-4 for OpenRouter. The provider’s API key must be present in the environment (or in --env-file); aomi-run checks for it before it even loads the plugin.
aomi-git
aomi-git publishes your App source through a platform’s Git policy, then activates the resulting release on a backend. It never edits your source. It copies a snapshot into a transit clone of the platform repo and pushes from there.
Run it from your source repo, the crate that holds aomi.toml and src/lib.rs.
| Subcommand | What it does | Who runs it |
|---|---|---|
request | Posts a request to the Aomi apps Discord so ops can invite your GitHub account to the platform repo and email you a per-app activation code. Run once, before your first deploy. | The app author |
deploy | Snapshots your source, stages it under apps/<slug>/ in the platform repo, commits, and pushes to the publish branch. CI then builds the cdylib and cuts a release. | The app author |
status | Reads .aomi/deployment.json, polls GitHub Actions and release state, and reports whether activation is ready. | The app author |
activate | Tells a backend to fetch a published release, validate it, and load it. | The app author, with their per-app activation code |
config | Edits a live App’s registry metadata (visibility and display label) without re-deploying or re-fetching the release. | The app author |
request
The first step for a new contributor. You cannot deploy until ops invite your GitHub account to the platform repo, and you cannot activate until ops issue you a per-app activation code.request posts that ask, carrying your GitHub account, email, and App, to the Aomi apps Discord. Run it once, before your first deploy.
| Flag | Mirrors aomi.toml | Meaning |
|---|---|---|
--email <EMAIL> | n/a | Where ops send your activation code. Required. |
--git-account <USER> | n/a | GitHub account to invite as a platform repo collaborator. Required. |
--app <NAME> | [app].name | App slug. Defaults to the value in aomi.toml. |
--platform <NAME> | [app].platform | Platform tag. Falls back to aomi.toml, then community. |
--path <DIR> | n/a | Source repo for the aomi.toml lookup. Default: . |
--dry-run | n/a | Print the Discord message; post nothing. |
request is community tier only. B2B partners deploy through a server-side proxy and do not get direct repo access, so they do not use it.deploy
| Flag | Mirrors aomi.toml | Meaning |
|---|---|---|
[PATH] (--path) | n/a | App source directory. Default: . |
--platform <NAME> | [app].platform | Platform tag. Default: the toml value, then community. |
--source-repo <URL|owner/repo> | [app].git | Platform repo. Resolved from the backend record when omitted. |
--platform-dir <DIR> | n/a | Hand managed local clone to stage and push from. Skips the transit cache. |
--backend <URL> | AOMI_BACKEND_URL | Backend base URL for online checks. |
--dry-run | n/a | Plan plus best effort backend reads. No staging, push, or activation. |
--allow-dirty | n/a | Permit a dirty working tree in the plan and during staging. |
--json | n/a | Print the plan or outcome as JSON. |
Auto activate on deploy. If
AOMI_APP_ACTIVATION_TOKEN is set and the push lands, deploy tries to activate right away. This usually returns a 502 on the first push because the release tarball does not exist yet, since CI is still building. That is expected. The normal flow is to run activate yourself once CI is green.status
| Flag | Mirrors aomi.toml | Meaning |
|---|---|---|
[APP_RELEASE_TAG] | n/a | Release to check. Falls back to deployment.json’s target.app_release_tag. |
--source-repo <URL|owner/repo> | [app].git | Platform repo. Falls back to deployment.json. |
--backend <URL> | AOMI_BACKEND_URL | Backend base URL. Pass --backend '' to skip. |
--access-token <$ENV|VAL> | [app].access_token | GitHub PAT for private repo reads. Omit for public repos. |
--path <DIR> | n/a | Source repo for the deployment.json fallback. Default: . |
--json | n/a | Print the status report as JSON. |
activate
Run by the app author, with the per-app activation code ops issued you. It tells the backend to fetch a release by tag, validate it, and load it. Run it from your source repo and it reads.aomi/deployment.json for most values, so usually you set only AOMI_APP_ACTIVATION_TOKEN and AOMI_BACKEND_URL and run aomi-git activate. The full form, for a release whose deployment.json is not on hand:
| Flag | Mirrors aomi.toml | Meaning |
|---|---|---|
[APP_RELEASE_TAG] | n/a | Release to activate (e.g. apps-my-bot-abc1234). Falls back to deployment.json’s target.app_release_tag. |
--platform <NAME> | [app].platform | Platform tag. Falls back to deployment.json, then community. |
--source-repo <URL|owner/repo> | [app].git | source_repo on the app row. Falls back to deployment.json, then a backend lookup. |
--backend <URL> | AOMI_BACKEND_URL | Backend base URL. Required. |
--activation-token <T> | AOMI_APP_ACTIVATION_TOKEN | Your per-app activation code. Required. |
--access-token <$ENV|VAL> | [app].access_token | GitHub PAT for the backend’s one shot release fetch. Only for private platform repos. |
--target-tag <TAG> | n/a | Required backend server tag. Repeatable. |
--visibility <V> | [app].public | private (default) or public. |
--display-name <STR> | [app].display_name | Registry label. Falls back to deployment.json. |
--source-commit <SHA> | n/a | Provenance. Falls back to deployment.json. |
--source-tree <SHA> | n/a | Provenance. Falls back to deployment.json. |
--source-digest <SHA> | n/a | Provenance. Falls back to deployment.json. |
--path <DIR> | n/a | Source repo for the deployment.json fallback. Default: . |
--dry-run | n/a | Print the activation request that would be sent. No HTTP. |
--json | n/a | Print the backend response as JSON. |
Flags resolve through a defaults pyramid: a CLI flag wins, then
.aomi/deployment.json at --path, then a backend lookup, then a hardcoded default. Each step is best effort, so a missing deployment.json or an unreachable backend never aborts the whole plan. Only the operation that genuinely needs the unresolved value will error. Two env vars feed the chain: AOMI_BACKEND_URL (used by every subcommand that talks to a backend) and AOMI_APP_ACTIVATION_TOKEN (used by activate, config, and deploy auto activate).config
config edits a live App’s registry metadata without re-deploying or re-fetching the release. Use it to flip visibility or rename the display label. It needs the same per-app activation code as activate. You must pass at least one of --public or --display-name; with neither, there is nothing to change and it errors.
| Flag | Mirrors aomi.toml | Meaning |
|---|---|---|
--app <NAME> | [app].name | App slug. Falls back to deployment.json’s app.name. |
--platform <NAME> | [app].platform | Platform tag. Falls back to deployment.json, then community. |
--backend <URL> | AOMI_BACKEND_URL | Backend base URL. |
--activation-token <T> | AOMI_APP_ACTIVATION_TOKEN | Your per-app activation code. |
--public <BOOL> | [app].public | Flip visibility live. Omit to leave it untouched. |
--display-name <STR> | [app].display_name | New registry label. Omit to leave it untouched. |
--path <DIR> | n/a | Source repo for the deployment.json fallback. Default: . |
--dry-run | n/a | Print the planned config request. No HTTP. |
--json | n/a | Print the backend response as JSON. |
The validation pipeline
Everydeploy, including --dry-run, runs a validation pipeline and records the result in .aomi/deployment.json. It runs in four ordered stages. Each stage is a precondition for the next, so a failing gate short circuits the rest and downstream stages are recorded as skipped.
Stages 1 and 2 are offline, computed from local git and aomi.toml. Stages 3 and 4 are online: they only run when a backend URL is available, and otherwise stay skipped.
| Stage | Question it answers |
|---|---|
workspace | Is the local tree shippable? (git_clean) |
manifest | Does aomi.toml declare what we need? (platform_declared, git_declared) |
platform | Can we resolve the platform repo and deploy branch? (backend_reachable, platform_resolved, branch_matches_contract, git_url_matches_platform) |
backend | Will the backend actually accept this release? (server_tags_subset) |
error (a gate that fails the stage and should block the deploy) or warn (advisory; downgrades the stage to warning but does not block). The two warn checks are git_declared and git_url_matches_platform, since a backend lookup can supply the repo and forks are tolerated. The big one to watch is branch_matches_contract: if your target branch is not the platform’s contractual deployment_branch, the push will not auto deploy.
What a passing preflight looks like
What a passing preflight looks like
The human summary prints one line per stage:A stage rolls up to
passed (all checks passed), failed (an error check failed, blocked here), warning (only warn checks failed), or skipped (an upstream gate failed or inputs were absent, such as no backend URL).The deployment.json artifact
.aomi/deployment.json is the plan artifact written next to your aomi.toml. It is always rewritten in full on each operation, via temp file plus rename, so a partial write is never observable. Beyond the stages, it carries the resolved plan (app, source, platform, target), a flat errors log, and three independent state flags:
pushed: the push to the platform repo succeeded.deployed: the push landed on the contractual deploy branch (a strict subset ofpushed; a--dry-runor--platform-dirrun that did not push staysfalse).activated: the backend wrote the app row withis_active = true.
activate reads this file for its defaults, so running it from the same directory as a prior deploy lets you drop most flags.
Related
Building an App
The full authoring walkthrough, from spec to curated tools to test.
SDK reference
The Rust plugin SDK that your App compiles against.
Client CLI
The npm
aomi command for chatting with and driving a deployed App from your terminal.