mirror of
https://github.com/kellymichels/zscripts-token-savers
synced 2026-10-07 07:18:18 +00:00
feat(bash): add token-usage tracking to the bash port (#6)
Adds z_track_start/z_track_stop + z_record to zhelpers.sh and wires z_track_start into every command script. Each run now captures its own output volume (FIFO+tee, ANSI stripped), prints the '--- N lines / N chars / ~N tokens est. (Claude Code) ---' footer, and appends one JSONL row per top-level run in the same shape as the PowerShell tokens.jsonl. A nested-run guard keeps zrestart from double-counting its zkill/zstart children. Data dir precedence: $ZTOKENS_DATA, config ztokens.dataDir, sibling ../../ztokens/data, else ~/.ztokens/data. Docs + example config updated. Verified on WSL (isolated data dir): single run records correctly; nested zrestart produces one combined record, not three; est = round(chars/3.5).
This commit is contained in:
parent
cdeec77609
commit
d945430807
@ -54,6 +54,30 @@ command argument:
|
|||||||
Flags use GNU style (`--port 3000`, `--detached`) instead of PowerShell style
|
Flags use GNU style (`--port 3000`, `--detached`) instead of PowerShell style
|
||||||
(`-Port`, `-Detached`). Detached dev servers log to `/tmp/zstart-<project>.log`.
|
(`-Port`, `-Detached`). Detached dev servers log to `/tmp/zstart-<project>.log`.
|
||||||
|
|
||||||
|
## Usage tracking
|
||||||
|
|
||||||
|
Every run prints a footer with its own output volume:
|
||||||
|
|
||||||
|
```
|
||||||
|
--- 1,048 lines / 69,009 chars / ~19,717 tokens est. (Claude Code) ---
|
||||||
|
```
|
||||||
|
|
||||||
|
and (when `jq` is available) appends one JSON line per run to `tokens.jsonl`,
|
||||||
|
so you can see how much infrastructure output you keep out of an AI agent's
|
||||||
|
context over time. Fields: `ts, script, projects, lines, chars, est, model`
|
||||||
|
(`est` ≈ `chars / 3.5`). Nested runs (the `zkill`/`zstart` inside `zrestart`)
|
||||||
|
are counted once, not double.
|
||||||
|
|
||||||
|
Where it's written, first match wins:
|
||||||
|
|
||||||
|
1. `$ZTOKENS_DATA`
|
||||||
|
2. `ztokens.dataDir` in `zconfig.json`
|
||||||
|
3. the sibling `../ztokens/data` directory, if present
|
||||||
|
4. `~/.ztokens/data` (created on first run)
|
||||||
|
|
||||||
|
Set `ZTOKENS_MODEL` to tag records with a specific model; it defaults to
|
||||||
|
`est. chars/3.5`.
|
||||||
|
|
||||||
## Differences from the PowerShell versions
|
## Differences from the PowerShell versions
|
||||||
|
|
||||||
- `zsync <viteproject>` mirrors `dist/` with `rsync -a --delete` (robocopy /MIR equivalent).
|
- `zsync <viteproject>` mirrors `dist/` with `rsync -a --delete` (robocopy /MIR equivalent).
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$_HERE/zhelpers.sh"
|
source "$_HERE/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
|
|
||||||
install=0; at_time="02:00"
|
install=0; at_time="02:00"
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
z_require zip
|
z_require zip
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$_HERE/zhelpers.sh"
|
source "$_HERE/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
|
|
||||||
printf '\n'
|
printf '\n'
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
z_require ssh scp
|
z_require ssh scp
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,11 @@
|
|||||||
"oneDriveBackups": ""
|
"oneDriveBackups": ""
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ztokens": {
|
||||||
|
"_comment": "Optional. Where each run's token-usage stats (tokens.jsonl) are recorded. Omit to use ~/.ztokens/data.",
|
||||||
|
"dataDir": "/home/youruser/.ztokens/data"
|
||||||
|
},
|
||||||
|
|
||||||
"projects": {
|
"projects": {
|
||||||
"_comment": "Rename these keys to your own project names — the key IS the command argument: zstart pyapp, zdeploy viteapp, zbackup nextapp. Add as many projects as you like. Keys starting with _ are ignored.",
|
"_comment": "Rename these keys to your own project names — the key IS the command argument: zstart pyapp, zdeploy viteapp, zbackup nextapp. Add as many projects as you like. Keys starting with _ are ignored.",
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
z_require ssh scp zip curl jq
|
z_require ssh scp zip curl jq
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
z_require curl
|
z_require curl
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
z_require curl ssh
|
z_require curl ssh
|
||||||
|
|
||||||
|
|||||||
@ -237,3 +237,77 @@ kill_listeners() {
|
|||||||
done
|
done
|
||||||
printf '%s' "$n"
|
printf '%s' "$n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ---- output tracking (ztokens) ----------------------------------------------
|
||||||
|
# Mirrors the PowerShell Start-/Stop-ZTracking + tokens.jsonl feed: capture this
|
||||||
|
# script's own output volume, print the "--- N lines / N chars / ~N tokens ---"
|
||||||
|
# footer, and append one JSONL record per top-level run. Nested z-scripts (e.g.
|
||||||
|
# zkill/zstart under zrestart) inherit the capture and do NOT double-count.
|
||||||
|
# Call `z_track_start "$@"` once near the top; an EXIT trap covers every exit.
|
||||||
|
z_track_start() {
|
||||||
|
[ "${Z_TRACK_ACTIVE:-0}" = "1" ] && return # nested under another z-script
|
||||||
|
command -v mktemp >/dev/null 2>&1 || return
|
||||||
|
export Z_TRACK_ACTIVE=1
|
||||||
|
Z_TRACK_SCRIPT="$(basename "$0")"
|
||||||
|
Z_TRACK_ARGS="$*"
|
||||||
|
Z_TRACK_FILE="$(mktemp "${TMPDIR:-/tmp}/ztrack.XXXXXX")" || { Z_TRACK_FILE=""; return; }
|
||||||
|
Z_TRACK_FIFO="${Z_TRACK_FILE}.fifo"
|
||||||
|
if ! mkfifo "$Z_TRACK_FIFO" 2>/dev/null; then rm -f "$Z_TRACK_FILE"; Z_TRACK_FILE=""; return; fi
|
||||||
|
# tee copies the FIFO to the log file while still showing output on the terminal.
|
||||||
|
tee "$Z_TRACK_FILE" < "$Z_TRACK_FIFO" &
|
||||||
|
Z_TRACK_TEE=$!
|
||||||
|
exec 3>&1 4>&2 >"$Z_TRACK_FIFO" 2>&1
|
||||||
|
trap z_track_stop EXIT
|
||||||
|
}
|
||||||
|
|
||||||
|
z_track_stop() {
|
||||||
|
set +e # never let cleanup abort mid-way
|
||||||
|
[ -n "${Z_TRACK_FILE:-}" ] || return
|
||||||
|
exec 1>&3 2>&4 3>&- 4>&- # restore stdout/stderr, close the pipe
|
||||||
|
[ -n "${Z_TRACK_TEE:-}" ] && wait "$Z_TRACK_TEE" 2>/dev/null
|
||||||
|
rm -f "${Z_TRACK_FIFO:-}" 2>/dev/null
|
||||||
|
|
||||||
|
local esc plain lines chars tok
|
||||||
|
esc=$'\033'
|
||||||
|
plain="${Z_TRACK_FILE}.plain"
|
||||||
|
sed "s/${esc}\[[0-9;]*m//g" "$Z_TRACK_FILE" > "$plain" 2>/dev/null || cp "$Z_TRACK_FILE" "$plain" 2>/dev/null
|
||||||
|
lines="$(grep -cve '^[[:space:]]*$' "$plain" 2>/dev/null)"; [ -n "$lines" ] || lines=0
|
||||||
|
chars="$(wc -m < "$plain" 2>/dev/null | tr -d ' ')"; [ -n "$chars" ] || chars=0
|
||||||
|
tok="$(awk -v c="$chars" 'BEGIN{printf "%d", int(c/3.5 + 0.5)}')"
|
||||||
|
|
||||||
|
printf '\n%s--- %s lines / %s chars / ~%s tokens est. (Claude Code) ---%s\n' \
|
||||||
|
"${C_GRAY:-}" "$(_z_commafy "$lines")" "$(_z_commafy "$chars")" "$(_z_commafy "$tok")" "${C_RESET:-}"
|
||||||
|
|
||||||
|
z_record "$lines" "$chars" "$tok"
|
||||||
|
rm -f "$Z_TRACK_FILE" "$plain" 2>/dev/null
|
||||||
|
Z_TRACK_FILE=""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Portable thousands separators (avoids locale-dependent printf %'d).
|
||||||
|
_z_commafy() { printf '%s' "$1" | sed -E ':a;s/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/;ta'; }
|
||||||
|
|
||||||
|
# Append a run to the ztokens JSONL store. Data dir precedence: $ZTOKENS_DATA,
|
||||||
|
# then config "ztokens.dataDir", then the sibling ../../ztokens/data if present
|
||||||
|
# (dev layout), else ~/.ztokens/data. No-op without jq. Model tag is
|
||||||
|
# $ZTOKENS_MODEL or "est. chars/3.5".
|
||||||
|
z_record() { # <lines> <chars> <est>
|
||||||
|
command -v jq >/dev/null 2>&1 || return
|
||||||
|
local lines="$1" chars="$2" est="$3" dir model projects
|
||||||
|
dir="${ZTOKENS_DATA:-}"
|
||||||
|
[ -z "$dir" ] && [ -f "$ZCONFIG" ] && dir="$(jq -r '.ztokens.dataDir // empty' "$ZCONFIG" 2>/dev/null)"
|
||||||
|
if [ -z "$dir" ]; then
|
||||||
|
if [ -d "$_ZDIR/../../ztokens/data" ]; then dir="$_ZDIR/../../ztokens/data"; else dir="$HOME/.ztokens/data"; fi
|
||||||
|
fi
|
||||||
|
mkdir -p "$dir" 2>/dev/null || return
|
||||||
|
model="${ZTOKENS_MODEL:-est. chars/3.5}"
|
||||||
|
projects=""
|
||||||
|
[ -n "${Z_TRACK_ARGS:-}" ] && projects="$(printf '%s\n' $Z_TRACK_ARGS | grep -v '^-' | paste -sd',' - 2>/dev/null)"
|
||||||
|
jq -cn \
|
||||||
|
--arg ts "$(date +%Y-%m-%dT%H:%M:%S%z)" \
|
||||||
|
--arg script "${Z_TRACK_SCRIPT%.*}" \
|
||||||
|
--arg projects "$projects" \
|
||||||
|
--argjson lines "${lines:-0}" --argjson chars "${chars:-0}" --argjson est "${est:-0}" \
|
||||||
|
--arg model "$model" \
|
||||||
|
'{ts:$ts,script:$script,projects:$projects,lines:$lines,chars:$chars,est:$est,model:$model}' \
|
||||||
|
>> "$dir/tokens.jsonl" 2>/dev/null
|
||||||
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
z_require lsof
|
z_require lsof
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
z_require ssh curl
|
z_require ssh curl
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$_HERE/zhelpers.sh"
|
source "$_HERE/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
|
|
||||||
projects=(); port_override=0; kill_all=0; no_restart=0; detached=0; bind_host="127.0.0.1"
|
projects=(); port_override=0; kill_all=0; no_restart=0; detached=0; bind_host="127.0.0.1"
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
z_require ssh
|
z_require ssh
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
|
|
||||||
projects=(); port_override=0; bind_host="127.0.0.1"; detached=0
|
projects=(); port_override=0; bind_host="127.0.0.1"; detached=0
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$_HERE/zhelpers.sh"
|
source "$_HERE/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_require docker
|
z_require docker
|
||||||
|
|
||||||
build=0; attached=0; solo=0
|
build=0; attached=0; solo=0
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
z_require ssh
|
z_require ssh
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/zhelpers.sh"
|
||||||
|
z_track_start "$@"
|
||||||
z_need_config
|
z_need_config
|
||||||
|
|
||||||
projects=(); dest="${ZSYNC_DEST:-}"
|
projects=(); dest="${ZSYNC_DEST:-}"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user