mirror of
https://github.com/FBukovina/burn-baby-burn.git
synced 2026-10-27 15:14:32 +00:00
first commit
This commit is contained in:
commit
7478aef719
5 changed files with 516 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.DS_Store
|
||||
*.swp
|
||||
*.swo
|
||||
19
Formula/burn.rb
Normal file
19
Formula/burn.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
class Burn < Formula
|
||||
desc "Token-burning harness around Claude Code"
|
||||
homepage "https://github.com/dtnewman/burn_baby_burn"
|
||||
url "https://github.com/dtnewman/burn_baby_burn/archive/refs/tags/v0.1.0.tar.gz"
|
||||
sha256 "REPLACE_WITH_RELEASE_TARBALL_SHA256"
|
||||
license "MIT"
|
||||
head "https://github.com/dtnewman/burn_baby_burn.git", branch: "main"
|
||||
|
||||
depends_on "jq"
|
||||
|
||||
def install
|
||||
bin.install "bin/burn"
|
||||
end
|
||||
|
||||
test do
|
||||
assert_match "burn", shell_output("#{bin}/burn --help")
|
||||
assert_match(/at least \d+/, shell_output("#{bin}/burn 500 2>&1", 2))
|
||||
end
|
||||
end
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Daniel Newman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
198
README.md
Normal file
198
README.md
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
# burn-baby-burn
|
||||
|
||||
> *"Burn, baby, burn — disco inferno."*
|
||||
> — The Trammps, 1976, on the subject of API quotas
|
||||
|
||||
A tiny bash CLI that wraps [Claude Code](https://docs.claude.com/claude-code)
|
||||
and sets your token budget on fire, intentionally, in controlled increments.
|
||||
|
||||
```bash
|
||||
burn 50000
|
||||
```
|
||||
|
||||
That's it. It spins up Claude Code in non-interactive mode, feeds it a
|
||||
garbage prompt sized to ~1000 input tokens, and keeps doing that until your
|
||||
target is met or exceeded. Real token counts come back from the API, so the
|
||||
total you see is the total that hit your bill.
|
||||
|
||||
No Python. No virtualenvs. One bash file. The way nature intended.
|
||||
|
||||
---
|
||||
|
||||
## Why would anyone want this?
|
||||
|
||||
Glad you asked. A non-exhaustive list of legitimate reasons to burn tokens
|
||||
on purpose:
|
||||
|
||||
- **Rate-limit and budget testing.** Verify your alerting fires before
|
||||
production traffic does.
|
||||
- **Spend-pacing experiments.** Watch your dashboards in real time while a
|
||||
known quantity of tokens lands on your account.
|
||||
- **Reproducible load.** Generate a deterministic amount of Claude Code
|
||||
usage for CI smoke tests, dashboards, or demos.
|
||||
- **Stress-testing wrappers.** If you've built tooling around Claude Code,
|
||||
`burn` is a cheap way to drive sustained-but-bounded traffic through it.
|
||||
- **Curiosity.** You wanted to know what happens. Now you'll know.
|
||||
|
||||
This tool exists because every other "load test" script eventually does
|
||||
something accidentally useful. `burn` does nothing useful. It is pure,
|
||||
honest waste — measured to the token.
|
||||
|
||||
---
|
||||
|
||||
## Install
|
||||
|
||||
### Homebrew (recommended)
|
||||
|
||||
```bash
|
||||
brew tap dtnewman/burn https://github.com/dtnewman/burn_baby_burn
|
||||
brew install burn
|
||||
```
|
||||
|
||||
This installs the `burn` script to `$(brew --prefix)/bin/burn` and pulls in
|
||||
`jq` as a dependency. The formula lives in this repo at `Formula/burn.rb`.
|
||||
|
||||
### One-liner (no brew)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/dtnewman/burn_baby_burn/main/bin/burn \
|
||||
-o /usr/local/bin/burn && chmod +x /usr/local/bin/burn
|
||||
```
|
||||
|
||||
You'll also need `jq`:
|
||||
|
||||
```bash
|
||||
brew install jq # macOS
|
||||
sudo apt install jq # Debian/Ubuntu
|
||||
```
|
||||
|
||||
### From a clone
|
||||
|
||||
```bash
|
||||
git clone https://github.com/dtnewman/burn_baby_burn
|
||||
cd burn_baby_burn
|
||||
ln -s "$PWD/bin/burn" /usr/local/bin/burn
|
||||
```
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Claude Code](https://docs.claude.com/claude-code) installed and
|
||||
authenticated on your `PATH`. If `claude --version` works, you're good.
|
||||
- [`jq`](https://jqlang.github.io/jq/) for parsing Claude Code's JSON
|
||||
output. `brew install jq` if you don't have it.
|
||||
- `bash` 3.2+ (i.e. anything from this millennium).
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
burn TOKENS [--model MODEL]
|
||||
```
|
||||
|
||||
| Arg / flag | Meaning |
|
||||
|------------------|----------------------------------------------------------------------|
|
||||
| `TOKENS` | Target number of tokens to burn. **Minimum: 10000.** |
|
||||
| `--model MODEL` | Optional. Pass through to `claude --model` (e.g. `haiku`, `sonnet`). |
|
||||
| `-h`, `--help` | Show help. |
|
||||
|
||||
### Examples
|
||||
|
||||
Burn ten thousand tokens, the absolute minimum:
|
||||
|
||||
```bash
|
||||
burn 10000
|
||||
```
|
||||
|
||||
Burn fifty thousand tokens on Haiku (cheap and fast — perfect for soak tests):
|
||||
|
||||
```bash
|
||||
burn 50000 --model haiku
|
||||
```
|
||||
|
||||
Burn a hundred thousand and walk away:
|
||||
|
||||
```bash
|
||||
burn 100000 --model sonnet
|
||||
```
|
||||
|
||||
### What you'll see
|
||||
|
||||
```
|
||||
$ burn 10000
|
||||
burn baby burn — going to try to burn approximately 10000 tokens.
|
||||
each iteration spins up `claude -p` and burns ~10000 tokens.
|
||||
|
||||
[1] burning… +11142 tokens $0.034230 (total 11142 / 10000, $0.034230)
|
||||
|
||||
done. burned 11142 tokens across 1 call(s) (target was 10000), spent $0.034230.
|
||||
```
|
||||
|
||||
A single `claude -p` call carries non-trivial system-prompt overhead, so
|
||||
the first iteration almost always overshoots. Subsequent iterations are
|
||||
cheaper (prompt caching kicks in). `burn` always reports the **actual**
|
||||
token counts and dollar cost returned by the API, summed across input,
|
||||
cache reads, cache writes, and output.
|
||||
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
1. Parse the target (must be ≥ 10000 — a single `claude -p` call already
|
||||
burns about that much, so anything smaller is just a typo).
|
||||
2. Build a prompt of roughly 1000 input tokens: a "reply with the word
|
||||
'burned'" instruction followed by lorem-ipsum-grade disco-inferno filler.
|
||||
3. Shell out to `claude -p --output-format json --no-session-persistence`
|
||||
with that prompt.
|
||||
4. `jq` extracts the `usage` block from Claude Code's JSON response.
|
||||
5. Tokens burned this call =
|
||||
`input_tokens + cache_creation_input_tokens + cache_read_input_tokens + output_tokens`.
|
||||
6. Repeat until the running total meets or exceeds your target.
|
||||
7. Print the receipts.
|
||||
|
||||
`Ctrl-C` at any time stops the burn and tells you how far you got.
|
||||
|
||||
The whole thing is ~140 lines of bash. You can read it before you run it.
|
||||
You probably should.
|
||||
|
||||
---
|
||||
|
||||
## What it does NOT do
|
||||
|
||||
- It does **not** mint tokens, generate value, or accomplish anything.
|
||||
- It does **not** modify files, run tools, edit your code, or commit
|
||||
anything. The prompt asks Claude for a one-word reply.
|
||||
- It does **not** bypass safety, billing, or rate limits. `burn` is just a
|
||||
shell wrapper; Claude Code does what Claude Code does.
|
||||
- It does **not** know about your wallet. **You are responsible for the
|
||||
bill.** Run `burn 100000000` and you'll find out exactly how responsible.
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Why is the minimum 10000?**
|
||||
A: A single `claude -p` call already burns roughly that much (system-prompt
|
||||
overhead plus the ~1000-token filler payload). Asking for less is asking
|
||||
for one call, which the harness can't honestly deliver.
|
||||
|
||||
**Q: Why did `burn 10000` burn 11,142 tokens?**
|
||||
A: Claude Code attaches a system prompt to every `-p` invocation. That
|
||||
overhead lands on the first call. `burn` reports honest totals; it does not
|
||||
fudge them down.
|
||||
|
||||
**Q: Can I make it burn slower?**
|
||||
A: Pick a smaller, cheaper model (`--model haiku`) and a bigger target.
|
||||
|
||||
**Q: Why bash instead of Python / Go / Rust?**
|
||||
A: Because it's 140 lines that shell out to another CLI. Anything heavier
|
||||
would be bringing a flamethrower to a candle.
|
||||
|
||||
**Q: Will Anthropic stop me?**
|
||||
A: They will charge you. That is how they will stop you.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
MIT. Burn responsibly.
|
||||
275
bin/burn
Executable file
275
bin/burn
Executable file
|
|
@ -0,0 +1,275 @@
|
|||
#!/usr/bin/env bash
|
||||
# burn — a token-burning harness around Claude Code.
|
||||
# Usage: burn TOKENS [--model MODEL]
|
||||
set -euo pipefail
|
||||
|
||||
FILLER_TOKENS=1000
|
||||
SYSTEM_OVERHEAD_TOKENS=9000 # measured: claude -p system prompt + tool defs
|
||||
TOKENS_PER_CALL=$(( SYSTEM_OVERHEAD_TOKENS + FILLER_TOKENS ))
|
||||
MIN_TOKENS=$TOKENS_PER_CALL # asking for less than one call can't be honored
|
||||
FILLER_LINE="Burn baby burn, this is a disco inferno, burning tokens is for great engineers. "
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
burn — spin up Claude Code in a loop to burn approximately N tokens.
|
||||
|
||||
Usage:
|
||||
burn TOKENS [--model MODEL]
|
||||
|
||||
Arguments:
|
||||
TOKENS Target tokens to burn (minimum ${MIN_TOKENS}).
|
||||
|
||||
Options:
|
||||
--model NAME Pass through to 'claude --model' (e.g. haiku, sonnet, opus).
|
||||
-h, --help Show this help and exit.
|
||||
EOF
|
||||
}
|
||||
|
||||
model=""
|
||||
tokens=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h|--help) usage; exit 0 ;;
|
||||
--model)
|
||||
if [[ $# -lt 2 ]]; then echo "error: --model requires a value" >&2; exit 2; fi
|
||||
model="$2"; shift 2 ;;
|
||||
--model=*) model="${1#*=}"; shift ;;
|
||||
--) shift; break ;;
|
||||
-*) echo "error: unknown flag: $1" >&2; usage >&2; exit 2 ;;
|
||||
*)
|
||||
if [[ -n "$tokens" ]]; then
|
||||
echo "error: unexpected extra argument: $1" >&2
|
||||
exit 2
|
||||
fi
|
||||
tokens="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$tokens" ]]; then
|
||||
usage >&2; exit 2
|
||||
fi
|
||||
if ! [[ "$tokens" =~ ^[0-9]+$ ]]; then
|
||||
echo "error: TOKENS must be a positive integer (got '$tokens')" >&2; exit 2
|
||||
fi
|
||||
if (( tokens < MIN_TOKENS )); then
|
||||
echo "error: tokens must be at least ${MIN_TOKENS} (got ${tokens})" >&2; exit 2
|
||||
fi
|
||||
|
||||
if ! command -v claude >/dev/null 2>&1; then
|
||||
echo "error: 'claude' CLI not found on PATH." >&2
|
||||
echo " install Claude Code first: https://docs.claude.com/claude-code" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "error: 'jq' not found on PATH (used to parse Claude Code's JSON output)." >&2
|
||||
echo " install with: brew install jq" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ~4 chars/token is the standard rule of thumb for English text.
|
||||
target_chars=$(( FILLER_TOKENS * 4 ))
|
||||
filler_len=${#FILLER_LINE}
|
||||
repeats=$(( target_chars / filler_len ))
|
||||
(( repeats < 1 )) && repeats=1
|
||||
filler=""
|
||||
for ((i=0; i<repeats; i++)); do
|
||||
filler+="$FILLER_LINE"
|
||||
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
|
||||
ORANGE=$'\033[1;33m'
|
||||
RED=$'\033[0;31m'
|
||||
DIM=$'\033[2m'
|
||||
RESET=$'\033[0m'
|
||||
HIDE_CURSOR=$'\033[?25l'
|
||||
SHOW_CURSOR=$'\033[?25h'
|
||||
else
|
||||
ORANGE=''
|
||||
RED=''
|
||||
DIM=''
|
||||
RESET=''
|
||||
HIDE_CURSOR=''
|
||||
SHOW_CURSOR=''
|
||||
fi
|
||||
|
||||
PAD=' '
|
||||
|
||||
# Three flame frames cycled to make the fire dance. Each row is exactly
|
||||
# 23 characters wide so it sits cleanly inside the fireplace opening.
|
||||
flame_frame() {
|
||||
case "$1_$2" in
|
||||
0_0) printf '%s' ' ) ) ) ' ;;
|
||||
0_1) printf '%s' ' )\ )\ )\ ' ;;
|
||||
0_2) printf '%s' ' ( ( ( ( ( ( ( ( ( ' ;;
|
||||
0_3) printf '%s' ' ) ) ) ) ) ) ) ) ) ' ;;
|
||||
0_4) printf '%s' ' \_/ \_/ \_/ ' ;;
|
||||
1_0) printf '%s' ' ( ( ( ' ;;
|
||||
1_1) printf '%s' ' /( /( /( ' ;;
|
||||
1_2) printf '%s' ' ( ( ( ( ( ( ( ( ( ' ;;
|
||||
1_3) printf '%s' ' ( ) ( ) ( ) ( ) ( ' ;;
|
||||
1_4) printf '%s' ' \_/ \_/ \_/ ' ;;
|
||||
2_0) printf '%s' ' ) ( ) ' ;;
|
||||
2_1) printf '%s' ' (( )( (( ' ;;
|
||||
2_2) printf '%s' ' ( ( ( ( ( ( ( ( ( ' ;;
|
||||
2_3) printf '%s' ' ) ( ) ( ) ( ) ( ) ' ;;
|
||||
2_4) printf '%s' ' \_/ \_/ \_/ ' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
flame_color_for_row() {
|
||||
case "$1" in
|
||||
0|1) printf '%s' "$ORANGE" ;;
|
||||
*) printf '%s' "$RED" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
print_flame_row() {
|
||||
local frame=$1 row=$2 content color
|
||||
content=$(flame_frame "$frame" "$row")
|
||||
color=$(flame_color_for_row "$row")
|
||||
printf '%s%s│░░░│%s%s%s%s%s│░░░│%s\n' \
|
||||
"$PAD" "$DIM" "$RESET" "$color" "$content" "$RESET" "$DIM" "$RESET"
|
||||
}
|
||||
|
||||
draw_fireplace() {
|
||||
local frame=${1:-0}
|
||||
printf '%s%s┌───────────────────────────────┐%s\n' "$PAD" "$DIM" "$RESET"
|
||||
printf '%s%s│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│%s\n' "$PAD" "$DIM" "$RESET"
|
||||
printf '%s%s│░░░┌───────────────────────┐░░░│%s\n' "$PAD" "$DIM" "$RESET"
|
||||
printf '%s%s│░░░│ │░░░│%s\n' "$PAD" "$DIM" "$RESET"
|
||||
printf '%s%s│░░░│ │░░░│%s\n' "$PAD" "$DIM" "$RESET"
|
||||
for row in 0 1 2 3 4; do
|
||||
print_flame_row "$frame" "$row"
|
||||
done
|
||||
printf '%s%s│░░░│ │░░░│%s\n' "$PAD" "$DIM" "$RESET"
|
||||
printf '%s%s│░░░│%s %s▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓%s %s│░░░│%s\n' \
|
||||
"$PAD" "$DIM" "$RESET" "$RED" "$RESET" "$DIM" "$RESET"
|
||||
printf '%s%s│░░░└───────────────────────┘░░░│%s\n' "$PAD" "$DIM" "$RESET"
|
||||
printf '%s%s└───────────────────────────────┘%s\n' "$PAD" "$DIM" "$RESET"
|
||||
}
|
||||
|
||||
# Distance (in lines) from the progress-bar cursor up to the top flame row:
|
||||
# blank line above bar + outer-bottom + inner-bottom + log + blank-below-flames
|
||||
# + 5 flame rows = 10.
|
||||
FLAME_OFFSET=10
|
||||
|
||||
redraw_flames() {
|
||||
local frame=$1
|
||||
printf '\033[s' # save cursor
|
||||
printf '\033[%dA\r' "$FLAME_OFFSET" # move up to top flame row
|
||||
for row in 0 1 2 3 4; do
|
||||
print_flame_row "$frame" "$row"
|
||||
done
|
||||
printf '\033[u' # restore cursor
|
||||
}
|
||||
|
||||
burned=0
|
||||
iteration=0
|
||||
total_cost=0.000000
|
||||
BAR_WIDTH=30
|
||||
claude_pid=""
|
||||
last_output=""
|
||||
|
||||
claude_cmd=(claude -p --output-format json --no-session-persistence --tools "")
|
||||
if [[ -n "$model" ]]; then
|
||||
claude_cmd+=(--model "$model")
|
||||
fi
|
||||
# `--tools` is variadic, so terminate flag parsing before the prompt.
|
||||
claude_cmd+=(--)
|
||||
|
||||
draw_bar() {
|
||||
local pct filled empty bar="" i suffix="${1:-}"
|
||||
pct=$(( burned * 100 / tokens ))
|
||||
(( pct > 100 )) && pct=100
|
||||
filled=$(( pct * BAR_WIDTH / 100 ))
|
||||
empty=$(( BAR_WIDTH - filled ))
|
||||
for ((i=0; i<filled; i++)); do bar+='█'; done
|
||||
for ((i=0; i<empty; i++)); do bar+='░'; done
|
||||
# \033[K clears any leftover characters from a longer previous draw.
|
||||
printf "\r[%s] %3d%% • %d/%d tokens • \$%s • %d call(s)%s\033[K" \
|
||||
"$bar" "$pct" "$burned" "$tokens" "$total_cost" "$iteration" "$suffix"
|
||||
}
|
||||
|
||||
restore_cursor() {
|
||||
printf '%s' "$SHOW_CURSOR"
|
||||
}
|
||||
trap restore_cursor EXIT
|
||||
|
||||
on_interrupt() {
|
||||
if [[ -n "$claude_pid" ]]; then
|
||||
kill "$claude_pid" 2>/dev/null || true
|
||||
wait "$claude_pid" 2>/dev/null || true
|
||||
fi
|
||||
echo
|
||||
printf "stopped. burned %s tokens across %s call(s), spent \$%s.\n" \
|
||||
"$burned" "$iteration" "$total_cost"
|
||||
exit 130
|
||||
}
|
||||
trap on_interrupt INT
|
||||
|
||||
# Run one claude call in the background while animating flames. On success
|
||||
# leaves the parsed stdout in $last_output. Returns the claude exit status.
|
||||
run_claude() {
|
||||
local output_file
|
||||
output_file=$(mktemp)
|
||||
"${claude_cmd[@]}" "$prompt" > "$output_file" 2>/dev/null &
|
||||
claude_pid=$!
|
||||
|
||||
local frame=0
|
||||
while kill -0 "$claude_pid" 2>/dev/null; do
|
||||
redraw_flames $(( frame % 3 ))
|
||||
frame=$(( frame + 1 ))
|
||||
sleep 0.25
|
||||
done
|
||||
|
||||
if wait "$claude_pid" 2>/dev/null; then
|
||||
last_output=$(<"$output_file")
|
||||
rm -f "$output_file"
|
||||
claude_pid=""
|
||||
return 0
|
||||
else
|
||||
rm -f "$output_file"
|
||||
claude_pid=""
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
printf '%s' "$HIDE_CURSOR"
|
||||
echo
|
||||
draw_fireplace 0
|
||||
echo
|
||||
draw_bar
|
||||
|
||||
while (( burned < tokens )); do
|
||||
iteration=$(( iteration + 1 ))
|
||||
|
||||
if ! run_claude; then
|
||||
draw_bar " (retrying)"
|
||||
if ! run_claude; then
|
||||
printf "\nerror: two consecutive claude calls failed; aborting.\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
read -r this_call this_cost < <(jq -r '
|
||||
[ ((.usage.input_tokens // 0) +
|
||||
(.usage.cache_creation_input_tokens // 0) +
|
||||
(.usage.cache_read_input_tokens // 0) +
|
||||
(.usage.output_tokens // 0)),
|
||||
(.total_cost_usd // 0)
|
||||
] | @tsv' <<<"$last_output")
|
||||
|
||||
if ! [[ "$this_call" =~ ^[0-9]+$ ]]; then
|
||||
printf "\nerror: could not parse token usage from claude output\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
burned=$(( burned + this_call ))
|
||||
total_cost=$(awk -v a="$total_cost" -v b="$this_cost" 'BEGIN{printf "%.6f", a+b}')
|
||||
draw_bar
|
||||
done
|
||||
|
||||
echo
|
||||
printf "done. burned %s tokens across %s call(s) (target was %s), spent \$%s.\n" \
|
||||
"$burned" "$iteration" "$tokens" "$total_cost"
|
||||
Loading…
Add table
Add a link
Reference in a new issue