mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-15 20:05:16 +02:00
- x-code-mode-smoke.yml: on apps/x PRs, package the app on mac/linux/windows and run acp-smoke.mjs, which asserts (1) adapters staged + native engines stripped, (2) each staged adapter boots from the packaged app via the packaged Electron binary and answers ACP initialize, (3) a fake engine that launches but never responds is converted into a clear startup-timeout error instead of hanging forever (the silent-hang class) - diagnose-code-mode.sh: colleagues run one command and send one blob (engine versions/paths/types, login-shell vs GUI PATH, auth presence, stream-json probe, newest SDK debug log) — one round trip instead of five - forge.config.cjs: only sign/notarize when APPLE_ID is set, so unsigned local mac builds and the CI smoke matrix can package deterministically - client.ts: startup timeout overridable via ROWBOAT_ACP_STARTUP_TIMEOUT_MS (CI uses 10s; also an escape hatch for slow MCP-heavy setups) Verified on Windows: all smoke checks pass, including the end-to-end fake-hanging-engine timeout (fails in 10.0s with the stderr-enriched error).
88 lines
3 KiB
Bash
88 lines
3 KiB
Bash
#!/usr/bin/env bash
|
|
# One-shot code-mode diagnostics (macOS / Linux).
|
|
#
|
|
# When code mode misbehaves on your machine (stuck runs, "CLI not found",
|
|
# startup timeouts), run this and send the FULL output back — it collects
|
|
# everything needed to diagnose in one round trip:
|
|
#
|
|
# bash apps/x/scripts/diagnose-code-mode.sh
|
|
#
|
|
# Read-only except for one tiny `claude -p` probe (a single short API call).
|
|
|
|
section() { printf '\n=== %s ===\n' "$1"; }
|
|
|
|
# Portable timeout: mac has no `timeout` binary by default.
|
|
run_with_timeout() {
|
|
local secs="$1"; shift
|
|
"$@" & local pid=$!
|
|
( sleep "$secs" && kill "$pid" 2>/dev/null ) & local killer=$!
|
|
wait "$pid" 2>/dev/null; local rc=$?
|
|
kill "$killer" 2>/dev/null
|
|
return $rc
|
|
}
|
|
|
|
describe_binary() { # $1 = name
|
|
local p
|
|
p="$(/bin/sh -lc "command -v $1" 2>/dev/null)"
|
|
if [ -z "$p" ]; then
|
|
echo "$1: NOT on login-shell PATH"
|
|
return
|
|
fi
|
|
echo "$1: $p"
|
|
[ -L "$p" ] && echo " symlink -> $(readlink "$p")"
|
|
# Node-shebang script vs native binary — the distinction that matters for
|
|
# GUI launches (shebang scripts need `node` on the SPAWNING process's PATH).
|
|
local head1
|
|
head1="$(head -c 64 "$p" 2>/dev/null | head -n 1 | tr -d '\0')"
|
|
case "$head1" in
|
|
'#!'*) echo " type: script ($head1)" ;;
|
|
*) echo " type: native binary" ;;
|
|
esac
|
|
echo " version: $(run_with_timeout 15 "$1" --version 2>&1 | head -n 1)"
|
|
}
|
|
|
|
section "system"
|
|
echo "os: $(uname -sr) ($(uname -m))"
|
|
echo "shell: ${SHELL:-unset}"
|
|
echo "date: $(date)"
|
|
|
|
section "engines"
|
|
describe_binary claude
|
|
describe_binary codex
|
|
describe_binary node
|
|
|
|
section "PATH: login shell vs GUI"
|
|
echo "login-shell PATH:"
|
|
/bin/sh -lc 'echo " $PATH"'
|
|
if [ "$(uname -s)" = "Darwin" ]; then
|
|
echo "launchd (GUI) PATH:"
|
|
echo " $(launchctl getenv PATH 2>/dev/null || echo '(unset — GUI apps get the system default)')"
|
|
fi
|
|
|
|
section "auth presence (no secrets printed)"
|
|
if [ "$(uname -s)" = "Darwin" ]; then
|
|
if security find-generic-password -s "Claude Code-credentials" >/dev/null 2>&1; then
|
|
echo "claude: keychain credential present"
|
|
else
|
|
echo "claude: NO keychain credential (signed in?)"
|
|
fi
|
|
fi
|
|
[ -f "$HOME/.claude/.credentials.json" ] && echo "claude: ~/.claude/.credentials.json present"
|
|
[ -f "$HOME/.codex/auth.json" ] && echo "codex: ~/.codex/auth.json present" || echo "codex: NO ~/.codex/auth.json"
|
|
|
|
section "claude stream-json probe (what the app does under the hood)"
|
|
# A healthy claude prints a `system`/`init` JSON line within seconds. A hang or
|
|
# error here reproduces the in-app failure WITHOUT the app.
|
|
run_with_timeout 45 claude -p "reply with exactly: ok" --output-format stream-json --verbose 2>&1 | head -n 3
|
|
echo "(probe exit: $? — 143 means it hung and was killed after 45s)"
|
|
|
|
section "newest SDK debug log (~/.claude/debug)"
|
|
latest="$(ls -t "$HOME/.claude/debug"/sdk-*.txt 2>/dev/null | head -n 1)"
|
|
if [ -n "$latest" ]; then
|
|
echo "$latest:"
|
|
tail -n 40 "$latest"
|
|
else
|
|
echo "(none found)"
|
|
fi
|
|
|
|
printf '\ndone — send everything above.\n'
|