call-baxter¶
A generic callback/runtime scaffold with:
- a daemon exposing a stable HTTP control plane
- a CLI built with
pydantic-settingssubcommands - 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=falseto disable it.CALL_BAXTER_CONFIG_RELOAD_SECONDS=1.0to 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.