Skip to content

call-baxter

A generic callback/runtime scaffold with:

  • a daemon exposing a stable HTTP control plane
  • a CLI built with pydantic-settings subcommands
  • explicit plugin loading from config
  • YAML rules as operator-friendly source of truth
  • SQLite persistence for events, actions, and poll schedules
  • typed context contracts for event yields and action expects
  • a Telegram plugin that uses the same event-production pipeline for webhook ingress and polling

Quick start

python -m venv .venv
. .venv/bin/activate
pip install -e .[dev]

call-baxter --config-file examples/config.yml health
call-baxter --config-file examples/config.yml plugins list
call-baxter --config-file examples/config.yml context event show telegram.message.new
call-baxter --config-file examples/config.yml ingress run --name telegram.update --payload-file examples/telegram-update.json
call-baxter --config-file examples/config.yml events list

Run the daemon over TCP for local development:

call-baxterd --config-file examples/config.yml --port 8943
call-baxter --config-file examples/config.yml --daemon-url http://127.0.0.1:8943 health

Run the daemon over a Unix socket for deployment:

call-baxterd --config-file ../../infra/deploy/call-baxter.yml --database-url ../../infra/data/cb.db --uds ../../infra/run/cb.sock
call-baxter \
  --config-file ../../infra/deploy/call-baxter.yml \
  --daemon-url http+unix:///absolute/path/to/cb.sock \
  health

The UDS-aware call-baxter health command is suitable for local service checks and Docker health checks without opening a TCP listener. When CALL_BAXTER_API_KEY is set for the daemon, the CLI automatically uses the same environment variable. An explicit --daemon-api-key value is also supported. HTTP authentication and server errors make the health command exit non-zero.

Configuration behavior

Every YAML string value supports environment expansion:

plugins:
  - module: call_baxter.plugins.telegram
    config:
      bot_token: ${TELEGRAM_BOT_TOKEN}
      api_base: ${TELEGRAM_API_BASE:-https://api.telegram.org}

${VAR} expands to the environment value or an empty string. ${VAR:-default} uses the default when the variable is unset or empty. The daemon watches its configuration file by default and atomically reloads valid changes. Invalid replacements are logged and the active runtime remains unchanged. Configure the watcher with:

  • CALL_BAXTER_CONFIG_RELOAD_ENABLED=false to disable it.
  • CALL_BAXTER_CONFIG_RELOAD_SECONDS=1.0 to change the polling interval.

The built-in call_baxter.plugins.file_out helper provides a bounded local text sink for rules that need to emit reports, prompts, or handoff files. It must be configured with an explicit base directory; action paths are relative to that directory and escapes are rejected:

plugins:
  - module: call_baxter.plugins.file_out
    config:
      base_dir: /srv/data/file-out
      max_bytes: 1048576
      create_parents: true

Rules can then invoke file.out with path, content, and optional mode: append. The default mode is write. Because append is available, the action is intentionally classified as a local non-idempotent side effect and is not action-replay eligible.

Webhook endpoints acknowledge asynchronously by default, while ordinary ingress stays synchronous:

POST /v1/webhooks/{name}            # async by default
POST /v1/webhooks/{name}?async=false
POST /v1/ingress/{name}             # sync by default
POST /v1/ingress/{name}?async=true
GET  /v1/ingress/results/{receipt}

Async calls return {"accepted": true, "ref": "ing:...", "status": "accepted"}. Receipt state is process-local and intended for short-lived diagnostics, not durable workflow storage. Terminal receipts are trimmed when the in-process limit is reached; accepted and processing receipts are never evicted before their work finishes.

The operational acceptance matrix and exact regression-test mapping live in docs/operational-todo-validation.md.

What this scaffold proves

  • The common ground is a normalized event envelope, not a Telegram-shaped payload.
  • Plugins contribute event definitions, action definitions, pollers, and ingress adapters.
  • Rules are loaded from YAML, matched against events, and trigger actions.
  • Schedules are synced from config and can run due pollers.
  • Telegram can be folded into this runtime for event-driven automations while still keeping a separate Telegram projection/index in tg-agent-cli.

See the docs folder for architecture, CLI semantics, schema, daemon API, Telegram notes, interop notes, and deployment guidance.