Compare commits

...

4 commits

Author SHA1 Message Date
FBukovina
480dbd5c1a Support backend-specific minimum target tokens 2026-05-20 12:03:27 +02:00
FBukovina
24b606abcf Handle burn backend failures in the TUI 2026-05-20 11:49:37 +02:00
FBukovina
12c36ae23d Trigger Codex OAuth from Ignite 2026-05-20 11:37:54 +02:00
FBukovina
4246999931 Add TUI launch support and Codex install updates 2026-05-20 10:53:31 +02:00
4 changed files with 1413 additions and 14 deletions

View file

@ -26,12 +26,19 @@ Drops `burn` into `~/.local/bin` — no sudo, no Homebrew tap. Needs
## Usage
```bash
burn 10000 # the minimum
burn 10000 # the Claude minimum
burn 50000 --model haiku # cheap and fast
burn 100000 --model sonnet # walk away
burn 13000 --backend codex # the Codex minimum
burn 50000 --backend codex # 🤖 burn OpenAI tokens
```
Run `burn` with no arguments to open the TUI. When you select OpenAI
Codex and press `IGNITE`, the TUI launches the default browser OAuth
flow if Codex is not already connected. You can also use the `AUTH` row
to recheck or sign in manually, or run `codex login` before opening
`burn`. The TUI adjusts the minimum target automatically per backend.
![burn in action](screenshot.png)
---

View file

@ -17,6 +17,8 @@ FILLER_TOKENS=1000
CLAUDE_SYSTEM_OVERHEAD_TOKENS=9000 # claude -p with --tools ""
CODEX_SYSTEM_OVERHEAD_TOKENS=12000 # codex exec --sandbox read-only
FILLER_LINE="Burn baby burn, this is a disco inferno, burning tokens is for great engineers. "
TUI_CHILD=0
[[ "${BURN_TUI_CHILD:-}" == "1" ]] && TUI_CHILD=1
usage() {
cat <<EOF
@ -92,6 +94,20 @@ while [[ $# -gt 0 ]]; do
done
if [[ -z "$tokens" ]]; then
if [[ -t 0 && -t 1 ]]; then
if ! command -v node >/dev/null 2>&1; then
echo "error: No token count provided, and 'node' is not installed to launch the TUI." >&2
usage >&2
exit 2
fi
if [[ ! -f "$SCRIPT_DIR/tui.js" ]]; then
echo "error: TUI entrypoint not found at '$SCRIPT_DIR/tui.js'." >&2
usage >&2
exit 1
fi
exec node "$SCRIPT_DIR/tui.js"
fi
usage >&2
exit 2
fi
@ -178,7 +194,17 @@ for ((i = 0; i < repeats; i++)); do
done
prompt=$(printf "Reply with the single word 'burned' and nothing else. Ignore the filler text below; it exists only to consume tokens.\n\n%s" "$filler")
if [[ -t 1 ]]; then
if ((TUI_CHILD)); then
ORANGE=''
MAGENTA=''
CYAN=''
RED=''
YELLOW=''
DIM=''
RESET=''
HIDE_CURSOR=''
SHOW_CURSOR=''
elif [[ -t 1 || -n "${FORCE_COLOR:-}" || -n "${CLICOLOR_FORCE:-}" ]]; then
ORANGE=$'\033[1;33m'
MAGENTA=$'\033[1;35m'
CYAN=$'\033[1;36m'
@ -319,6 +345,7 @@ total_cost=0.000000
BAR_WIDTH=30
cli_pid=""
last_output=""
last_error=""
# Build CLI command for the chosen backend.
case "$backend" in
@ -367,6 +394,13 @@ draw_bar() {
local pct filled empty bar="" i suffix="${1:-}"
pct=$((burned * 100 / tokens))
((pct > 100)) && pct=100
if ((TUI_CHILD)); then
printf 'BURN_PROGRESS\t%d\t%d\t%d\t%s\t%d\t%s\n' \
"$pct" "$burned" "$tokens" "$(format_cost_field)" "$iteration" "$suffix"
return
fi
filled=$((pct * BAR_WIDTH / 100))
empty=$((BAR_WIDTH - filled))
for ((i = 0; i < filled; i++)); do bar+='█'; done
@ -431,31 +465,67 @@ trap on_interrupt INT
# Run one CLI call in the background while animating flames. On success
# leaves the parsed stdout in $last_output. Returns the CLI exit status.
run_cli() {
local output_file
local output_file error_file
output_file=$(mktemp)
error_file=$(mktemp)
# </dev/null prevents codex from waiting on stdin when run in background.
"${cli_cmd[@]}" "$prompt" >"$output_file" 2>/dev/null </dev/null &
"${cli_cmd[@]}" "$prompt" >"$output_file" 2>"$error_file" </dev/null &
cli_pid=$!
local frame=0
while kill -0 "$cli_pid" 2>/dev/null; do
redraw_disco $((frame % 4))
if ! ((TUI_CHILD)); then
redraw_disco $((frame % 4))
fi
frame=$((frame + 1))
sleep 0.4
done
if wait "$cli_pid" 2>/dev/null; then
last_output=$(<"$output_file")
rm -f "$output_file"
last_error=""
rm -f "$output_file" "$error_file"
cli_pid=""
return 0
else
rm -f "$output_file"
last_error=$(<"$error_file")
rm -f "$output_file" "$error_file"
cli_pid=""
return 1
fi
}
last_error_summary() {
awk '
NF {
line = $0
}
END {
gsub(/\t/, " ", line)
gsub(/\r/, "", line)
print line
}
' <<<"$last_error"
}
report_backend_failure() {
local err message
err=$(last_error_summary)
message="two consecutive ${backend} calls failed; aborting."
if [[ -n "$err" ]]; then
message="${message} Last error: ${err}"
fi
if ((TUI_CHILD)); then
printf 'BURN_ERROR\t%s\n' "$message"
fi
printf "\nerror: two consecutive %s calls failed; aborting.\n" "$backend" >&2
if [[ -n "$err" ]]; then
printf "last %s error: %s\n" "$backend" "$err" >&2
fi
}
# Parse one CLI call's output into "<tokens> <cost>" (tab-separated).
parse_usage() {
case "$backend" in
@ -489,10 +559,14 @@ parse_usage() {
esac
}
printf '%s' "$HIDE_CURSOR"
echo
draw_disco 0
echo
if ((TUI_CHILD)); then
:
else
printf '%s' "$HIDE_CURSOR"
echo
draw_disco 0
echo
fi
draw_bar
while ((burned < tokens)); do
@ -501,7 +575,7 @@ while ((burned < tokens)); do
if ! run_cli; then
draw_bar " (retrying)"
if ! run_cli; then
printf "\nerror: two consecutive %s calls failed; aborting.\n" "$backend" >&2
report_backend_failure
exit 1
fi
fi

1316
bin/tui.js Executable file

File diff suppressed because it is too large Load diff

View file

@ -10,13 +10,15 @@ mkdir -p "$DEST_DIR"
curl -fsSL "$BASE_URL/burn" -o "$DEST_DIR/burn"
# Sourced by burn when using --backend codex to compute estimated cost.
curl -fsSL "$BASE_URL/codex_rates.sh" -o "$DEST_DIR/codex_rates.sh"
chmod +x "$DEST_DIR/burn"
curl -fsSL "$BASE_URL/tui.js" -o "$DEST_DIR/tui.js"
chmod +x "$DEST_DIR/burn" "$DEST_DIR/tui.js"
echo "✅ installed burn → $DEST_DIR/burn"
case ":${PATH}:" in
*":${DEST_DIR}:"*)
echo "🔥 ready to go: run 'burn 10000' to torch some tokens."
echo "🕹️ or run 'burn' with no arguments to open the TUI."
;;
*)
echo
@ -25,6 +27,6 @@ case ":${PATH}:" in
echo
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
echo
echo " then reload your shell and run 'burn 10000'."
echo " then reload your shell and run 'burn 10000' or just 'burn' for the TUI."
;;
esac