Cheela Labs
GUIDES

Deploy a runtime

`cheela deploy` compiles your capability set into a manifest, writes four artifacts alongside it, and pushes the result to the control plane. It stops at the first validation failure, before anything is written or sent.

The pipeline

  1. Load configuration. cheela.config.ts is evaluated through tsx, after .env is read, so your config sees the same environment your app does.
  2. Validate configuration. Against a schema. A bad endpoint or a missing apiKey stops here.
  3. Discover the runtime. The module at .cheela/runtime.ts must default-export a Runtime.
  4. Discover capabilities. Every registration, with its schemas serialized to JSON Schema.
  5. Run generators. Every enabled generator is validated first, then each runs and has its output checked immediately.
  6. Compile the manifest. Capabilities, your website block, your namespace, the CLI version.
  7. Authenticate and push. POST /v1/deployments with the deploy key.

Nothing is written to disk or sent over the network past the first thrown error.

Dry runs

--dry-run runs everything except the push. Generators still write their files, so this is also how you regenerate artifacts without creating a deployment.

Terminal
npx cheela deploy --dry-run
Output
Cheela Deploy

✓ Config loaded
✓ Runtime loaded
✓ Found 4 capabilities
✓ Found 4 actions
✓ Deployment manifest valid

Generators
  capability-manifest   .cheela/generated/capability-manifest/capabilities.json (unchanged)
  runtime-manifest      .cheela/generated/runtime-manifest/runtime.json (unchanged)
  openapi               .cheela/generated/openapi/openapi.json (updated)
  adp                   .cheela/generated/adp/agent-discovery.json (updated)

Dry run complete.
No deployment was created.
Watch for the schema warning

After a real deploy, the CLI names any capability published without an input schema. The model is told those take no parameters. That is correct for something genuinely nullary and a bug for everything else — usually a schema that was not exported, or one that could not be serialized.

Generators

Four run by default. Each turns your capability set into a different artifact.

GENERATOROUTPUTWHAT IT IS FOR
capability-manifestcapability-manifest/capabilities.jsonThe canonical list of what this runtime exposes.
runtime-manifestruntime-manifest/runtime.jsonRuntime-level metadata, including SDK and CLI versions.
openapiopenapi/openapi.jsonAn OpenAPI document, so existing API tooling can read your capabilities.
adpadp/agent-discovery.jsonAn Agent Discovery Specification manifest, for publishing.

Disable any of them by name:

cheela.config.ts
export default defineConfig({
  apiKey: process.env.CHEELA_API_KEY!,
  generators: {
    disabled: ["openapi"],
  },
});

Runs are incremental. A generator whose inputs have not changed is skipped and reported as skipped (cached); the cache lives in .cheela/generate.cache.json, which is gitignored.

What gets written

Project
.cheela/
├── generated/              # commit this
│   ├── capability-manifest/capabilities.json
│   ├── runtime-manifest/runtime.json
│   ├── openapi/openapi.json
│   └── adp/agent-discovery.json
└── generate.cache.json     # gitignored

Commit generated/. The files are diffable, which means a pull request shows exactly how a published schema changed — the review you want before a stranger’s agent starts depending on it. Only the cache is local and derived.

Capability drift

Before pushing, deploy compares your local capability names against what is currently live and prints the difference. Same check, on demand:

Terminal
npx cheela status

If they disagree, status shows the diff and tells you to deploy. A failed status call never blocks a deploy — the diff is information, not a gate.

Removing a capability is a breaking change

If you have published a manifest, other people’s agents may have cached the capability’s address. The discovery spec has deprecation fields for exactly this — mark it deprecated and leave it serving before you delete it.

Deploying from CI

Deploy needs the deploy key and nothing else. Your endpoint should already be live at the URL in your config — deploying does not start anything.

.github/workflows/deploy.yml
- name: Deploy Cheela runtime
  run: npx cheela deploy
  env:
    CHEELA_API_KEY: ${{ secrets.CHEELA_API_KEY }}

Add a dry run to pull requests to catch a broken capability set before it merges:

.github/workflows/pr.yml
- name: Validate Cheela capabilities
  run: npx cheela deploy --dry-run
  env:
    CHEELA_API_KEY: ${{ secrets.CHEELA_API_KEY }}

Sequence matters: deploy after your application, so the endpoint serving the new capability set exists before the control plane starts routing to it.

Custom generators

A generator is an object with a name, an inputs() function used for cache invalidation, and a generate() function returning files to write. Register yours in config — they are appended to the built-ins, never a replacement.

cheela.config.ts
export default defineConfig({
  apiKey: process.env.CHEELA_API_KEY!,
  generators: {
    custom: [
      {
        name: "typed-client",
        inputs: (context) => context.capabilities.map((c) => c.capability.name),
        generate: (context) => [
          {
            path: "typed-client/client.ts",
            contents: renderClient(context.capabilities),
          },
        ],
      },
    ],
  },
});

Names must be unique across built-ins and custom generators; a collision is an error rather than a silent override. Anything that is not generator-shaped is rejected at load time with a message saying what was missing.

Full command syntax is in the CLI reference, and every config field in Configuration.