v0.6.4: cygpath -w wrap on every --rawfile/--slurpfile argv path

Bryan hit this on MobaXterm immediately after v0.6.3 shipped:

  C:\Users\...\bin\jq.exe: Bad JSON in --rawfile c /tmp/tmp.AlsIcdX9aO:
  Could not open /tmp/tmp.AlsIcdX9aO: No such file or directory
  error: OAuth token unavailable; run 'larry-auth.sh login' to re-authenticate
  error: empty response from API (timeout or network?)

Root cause is the same one v0.5.4 fixed for the OAuth file reads, but now
biting the new tempfile pipeline introduced for argv-overflow avoidance.
The Windows-native jq.exe shipped to MobaXterm via install-larry.sh
cannot resolve Cygwin paths like /tmp/tmp.X or /home/mobaxterm/.larry/...
when they come in as argv arguments to --rawfile / --slurpfile — it
interprets them as Windows paths and the open() fails.

v0.5.4 fixed this for stdin-read cases by piping via bash redirection
(`< "$file"`), since bash handles the cygwin→windows path open before
jq sees a file descriptor. But --rawfile / --slurpfile DO want a path
argument, so the stdin trick doesn't apply — we must give jq a path it
can actually open.

Fix: new jqpath() helper translates a cygwin path to its real Windows
equivalent via `cygpath -w` when cygpath exists (Cygwin / MobaXterm / MSYS).
On Linux and macOS cygpath is absent and the helper echoes the path
unchanged — true cross-platform no-op outside Cygwin.

Wrapped every --rawfile / --slurpfile argv path in larry.sh:
  add_user_text         --rawfile c ←
  add_assistant_blocks  --slurpfile b ←
  add_user_tool_results --slurpfile b ←
  agent_turn payload    --rawfile system, --slurpfile messages,
                        --slurpfile tools ←
  agent_turn tool-result aggregation  --rawfile c ←

Verified on macOS: jqpath echoes paths unchanged, the 40KB prompt
smoke test from v0.6.3 still works end-to-end.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Bryan Johnson 2026-05-27 14:48:52 -07:00
parent 38d1eeede4
commit 47452d3910
2 changed files with 24 additions and 9 deletions

View File

@ -1 +1 @@
0.6.3 0.6.4

View File

@ -36,7 +36,7 @@ set -o pipefail
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
# Config # Config
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
LARRY_VERSION="0.6.3" LARRY_VERSION="0.6.4"
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}" LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
LARRY_BASE_URL="${LARRY_BASE_URL:-https://raw.githubusercontent.com/bojj27/cloverleaf-larry/main}" LARRY_BASE_URL="${LARRY_BASE_URL:-https://raw.githubusercontent.com/bojj27/cloverleaf-larry/main}"
LARRY_UPDATE_URL="${LARRY_UPDATE_URL:-${LARRY_BASE_URL}/larry.sh}" LARRY_UPDATE_URL="${LARRY_UPDATE_URL:-${LARRY_BASE_URL}/larry.sh}"
@ -95,6 +95,21 @@ if ! command -v jq >/dev/null 2>&1; then
fi fi
fi fi
# jqpath PATH — translate a path for jq's argv consumption.
# On MobaXterm/Cygwin/MSYS the bundled jq is a Windows-native jq.exe that
# rejects Cygwin paths like /tmp/tmp.X or /home/mobaxterm/.larry/... when
# they come in as argv arguments (it tries to open them as Windows paths
# and fails). cygpath -w translates Cygwin → Windows; jq.exe can then open
# the file. On Linux/macOS cygpath does not exist and we echo the path
# unchanged. Wrap EVERY --rawfile / --slurpfile path with $(jqpath "$p").
jqpath() {
if command -v cygpath >/dev/null 2>&1; then
cygpath -w "$1"
else
printf '%s' "$1"
fi
}
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
# Bootstrap LARRY_HOME and API key # Bootstrap LARRY_HOME and API key
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
@ -467,7 +482,7 @@ add_user_text() {
local cfile tmp local cfile tmp
cfile=$(mktemp); tmp=$(mktemp) cfile=$(mktemp); tmp=$(mktemp)
printf '%s' "$content" > "$cfile" printf '%s' "$content" > "$cfile"
jq --rawfile c "$cfile" '. + [{"role":"user","content":[{"type":"text","text":$c}]}]' < "$MESSAGES_FILE" > "$tmp" \ jq --rawfile c "$(jqpath "$cfile")" '. + [{"role":"user","content":[{"type":"text","text":$c}]}]' < "$MESSAGES_FILE" > "$tmp" \
&& mv "$tmp" "$MESSAGES_FILE" && mv "$tmp" "$MESSAGES_FILE"
rm -f "$cfile" rm -f "$cfile"
} }
@ -476,7 +491,7 @@ add_assistant_blocks() {
local bfile tmp local bfile tmp
bfile=$(mktemp); tmp=$(mktemp) bfile=$(mktemp); tmp=$(mktemp)
printf '%s' "$blocks" > "$bfile" printf '%s' "$blocks" > "$bfile"
jq --slurpfile b "$bfile" '. + [{"role":"assistant","content":$b[0]}]' < "$MESSAGES_FILE" > "$tmp" \ jq --slurpfile b "$(jqpath "$bfile")" '. + [{"role":"assistant","content":$b[0]}]' < "$MESSAGES_FILE" > "$tmp" \
&& mv "$tmp" "$MESSAGES_FILE" && mv "$tmp" "$MESSAGES_FILE"
rm -f "$bfile" rm -f "$bfile"
} }
@ -485,7 +500,7 @@ add_user_tool_results() {
local bfile tmp local bfile tmp
bfile=$(mktemp); tmp=$(mktemp) bfile=$(mktemp); tmp=$(mktemp)
printf '%s' "$blocks" > "$bfile" printf '%s' "$blocks" > "$bfile"
jq --slurpfile b "$bfile" '. + [{"role":"user","content":$b[0]}]' < "$MESSAGES_FILE" > "$tmp" \ jq --slurpfile b "$(jqpath "$bfile")" '. + [{"role":"user","content":$b[0]}]' < "$MESSAGES_FILE" > "$tmp" \
&& mv "$tmp" "$MESSAGES_FILE" && mv "$tmp" "$MESSAGES_FILE"
rm -f "$bfile" rm -f "$bfile"
} }
@ -1066,9 +1081,9 @@ agent_turn() {
jq -n \ jq -n \
--arg model "$LARRY_MODEL" \ --arg model "$LARRY_MODEL" \
--argjson max_tokens "$LARRY_MAX_TOKENS" \ --argjson max_tokens "$LARRY_MAX_TOKENS" \
--rawfile system "$system_file" \ --rawfile system "$(jqpath "$system_file")" \
--slurpfile messages "$MESSAGES_FILE" \ --slurpfile messages "$(jqpath "$MESSAGES_FILE")" \
--slurpfile tools "$tools_file" \ --slurpfile tools "$(jqpath "$tools_file")" \
'{model:$model, max_tokens:$max_tokens, system:$system, messages:$messages[0], tools:$tools[0]}' \ '{model:$model, max_tokens:$max_tokens, system:$system, messages:$messages[0], tools:$tools[0]}' \
> "$payload_file" > "$payload_file"
@ -1122,7 +1137,7 @@ agent_turn() {
local result_file; result_file=$(mktemp) local result_file; result_file=$(mktemp)
printf '%s' "$result" > "$result_file" printf '%s' "$result" > "$result_file"
results=$(printf '%s' "$results" | jq \ results=$(printf '%s' "$results" | jq \
--arg id "$tu_id" --rawfile c "$result_file" \ --arg id "$tu_id" --rawfile c "$(jqpath "$result_file")" \
'. + [{"type":"tool_result","tool_use_id":$id,"content":$c}]') '. + [{"type":"tool_result","tool_use_id":$id,"content":$c}]')
rm -f "$result_file" rm -f "$result_file"
done < <(printf '%s' "$resp" | jq -c '.content[] | select(.type=="tool_use")') done < <(printf '%s' "$resp" | jq -c '.content[] | select(.type=="tool_use")')