use std::io::Write;
use crate::canonical::ExitClass;
use crate::config::{dump_config, EnvSnapshot, PartialConfig};
pub(super) fn dump(
stdout: &mut dyn Write,
stderr: &mut dyn Write,
flags: PartialConfig,
env: &EnvSnapshot,
file: PartialConfig,
) -> u8 {
match dump_config(flags, env, file) {
Ok(toml) => match stdout
.write_all(toml.as_bytes())
.and_then(|()| stdout.flush())
{
Ok(()) => ExitClass::Ok.code(),
Err(io) => ExitClass::from_io(&io).code(),
},
Err(e) => super::fail_early(stderr, e.into()),
}
}
pub(crate) fn emit(stdout: &mut dyn Write, doc: &str) -> u8 {
match stdout
.write_all(doc.as_bytes())
.and_then(|()| stdout.flush())
{
Ok(()) => ExitClass::Ok.code(),
Err(io) => ExitClass::from_io(&io).code(),
}
}
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub(crate) const VERSION_LINE: &str = concat!("bz ", env!("CARGO_PKG_VERSION"), "\n");
pub(crate) const SKILL: &str = include_str!("../../SKILL.md");
pub(crate) const HELP: &str = concat!(
"bz ",
env!("CARGO_PKG_VERSION"),
" — a stateless LLM adapter: one request, one round-trip, one POSIX exit.\n",
"\n",
"USAGE:\n",
" bz [FLAGS] \"PROMPT\" one-shot: the positional prompt is the request\n",
" echo '{…}' | bz [FLAGS] pipe a canonical request (JSON) on stdin instead\n",
" bz --login --provider <id> [--browser] | bz --list-models [--provider <id>]\n",
"\n",
"The request arrives exactly one way: a positional PROMPT (argv) XOR a canonical\n",
"request on stdin. A prompt wins and stdin is not read. A leading bare word is\n",
"ALWAYS a prompt — control operations are flags, never verbs. Output is a\n",
"projection chosen by flag; the default is plain text.\n",
"\n",
"CONTROL (each replaces the data-plane run with a control action, then exits):\n",
" --login obtain and store an OAuth/SSO credential for --provider\n",
" (the one interactive surface; never entered by the data\n",
" plane). Default: the headless device flow (shows a code to\n",
" enter on another device). --browser: the loopback browser\n",
" flow (opens a URL, captures the redirect).\n",
" --list-models one GET: list the resolved provider's models\n",
" --list-providers no round-trip: the EFFECTIVE provider table (name,\n",
" protocol, auth, credential) in routing-priority order —\n",
" the built-in rows included, which --dump-config omits\n",
" --count-tokens one round-trip: provider-accurate input-token count of the\n",
" request (read as for a run). Providers with no count endpoint\n",
" decline (78); output is {\"input_tokens\":N} (--json) else N.\n",
" --serve serve the [ingress] masquerade over HTTP (the route path\n",
" picks the codec: OpenAI/Anthropic clients reach any provider);\n",
" runs until SIGINT/SIGTERM\n",
" --dump-config print the merged config as TOML, exit 0\n",
" --skill print the fuller skill doc (worked examples), exit 0\n",
" --help, -h print this help, exit 0\n",
" --version, -V print the version, exit 0\n",
"\n",
"FLAGS:\n",
" --provider <id> provider row id (else routed from the model)\n",
" -m, --model <id> model id; a partial/absent id resolves against the cache\n",
" --api-key <key> inline credential (else the credential store / env)\n",
" --system <text> leading system prompt\n",
" --max-tokens <n> generation cap\n",
" --temperature <f> sampling temperature\n",
" --top-p <f> nucleus sampling\n",
" --stream/--no-stream stream the response (default) or fold one JSON body\n",
" --thinking include reasoning/thinking output (text mode)\n",
" --text human-readable text (default)\n",
" --json the full NDJSON canonical event stream\n",
" --raw pass bytes through verbatim, provider-native both ways\n",
" --in <dialect> read ONE client-dialect request (openai_chat) from stdin\n",
" and write the dialect response to stdout (SSE if it asks\n",
" stream:true) — the one-shot ingress filter\n",
" -f, --file <path> attach a file's text as context (repeatable; before the prompt)\n",
" --input <file> read the request from a file instead of stdin\n",
" --config <file> use this config file (else the default search path)\n",
" --timeout <s> abort on N seconds of upstream silence (connect/headers/between chunks)\n",
"\n",
"EXIT CODES (sysexits):\n",
" 0 success (incl. a provider refusal — a 200)\n",
" 64 usage: bad/unknown flag, malformed stdin request\n",
" 66 --input file missing or unreadable\n",
" 69 transport error, upstream 4xx (incl. 429), premature EOF\n",
" 70 upstream 5xx (retryable)\n",
" 77 auth: 401/403, missing credentials, login/refresh failure\n",
" 78 config: no/unknown/ambiguous provider or model, bad config\n",
" 130/141/143 interrupted by signal (SIGINT/SIGPIPE/SIGTERM)\n",
);