From 47452d3910d8d65b6b9a5390146a01e56d635f3f Mon Sep 17 00:00:00 2001 From: Bryan Johnson Date: Wed, 27 May 2026 14:48:52 -0700 Subject: [PATCH] v0.6.4: cygpath -w wrap on every --rawfile/--slurpfile argv path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- VERSION | 2 +- larry.sh | 31 +++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/VERSION b/VERSION index 844f6a9..d2b13eb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.3 +0.6.4 diff --git a/larry.sh b/larry.sh index edb41c4..c9ebc6b 100755 --- a/larry.sh +++ b/larry.sh @@ -36,7 +36,7 @@ set -o pipefail # ───────────────────────────────────────────────────────────────────────────── # Config # ───────────────────────────────────────────────────────────────────────────── -LARRY_VERSION="0.6.3" +LARRY_VERSION="0.6.4" LARRY_HOME="${LARRY_HOME:-$HOME/.larry}" 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}" @@ -95,6 +95,21 @@ if ! command -v jq >/dev/null 2>&1; then 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 # ───────────────────────────────────────────────────────────────────────────── @@ -467,7 +482,7 @@ add_user_text() { local cfile tmp cfile=$(mktemp); tmp=$(mktemp) 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" rm -f "$cfile" } @@ -476,7 +491,7 @@ add_assistant_blocks() { local bfile tmp bfile=$(mktemp); tmp=$(mktemp) 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" rm -f "$bfile" } @@ -485,7 +500,7 @@ add_user_tool_results() { local bfile tmp bfile=$(mktemp); tmp=$(mktemp) 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" rm -f "$bfile" } @@ -1066,9 +1081,9 @@ agent_turn() { jq -n \ --arg model "$LARRY_MODEL" \ --argjson max_tokens "$LARRY_MAX_TOKENS" \ - --rawfile system "$system_file" \ - --slurpfile messages "$MESSAGES_FILE" \ - --slurpfile tools "$tools_file" \ + --rawfile system "$(jqpath "$system_file")" \ + --slurpfile messages "$(jqpath "$MESSAGES_FILE")" \ + --slurpfile tools "$(jqpath "$tools_file")" \ '{model:$model, max_tokens:$max_tokens, system:$system, messages:$messages[0], tools:$tools[0]}' \ > "$payload_file" @@ -1122,7 +1137,7 @@ agent_turn() { local result_file; result_file=$(mktemp) printf '%s' "$result" > "$result_file" 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}]') rm -f "$result_file" done < <(printf '%s' "$resp" | jq -c '.content[] | select(.type=="tool_use")')