fix what the review found: signals, identity, the profile the wrapper nearly used
Ten findings from a review of the first commit. The ones that bite in normal use:
An unchecked mktemp left EPHEMERAL empty, which becomes `--user-data-dir=` — and Chrome reads that as
"use the default profile", i.e. the browser the user actually signs into. `set -u` does not catch it,
because the variable is set, just empty. Now checked and refused. That branch also never exec'd and
had no way to reach the browser with a signal; it now backgrounds, records the pid, and traps.
Signal handlers registered AFTER a build that takes minutes, so a pm2 restart during a first start
killed this process and left cargo compiling as an orphan — then started a second one on the same
target directory. Handlers now register first and shutdown kills the build.
`which('iris')` treated presence as identity. A truncated binary from an interrupted install is
present, reports ready, and never rebuilds. It now runs `--version` and checks — the same lesson
os-deps.ts already learned as `identify`.
Cookies were copied without Cookies-wal. These are WAL databases, so the newest session may exist
only in the log — copy the .db alone and the capture is signed out for exactly the account that just
signed in. PLUGIN.md had this backwards, citing WAL as the reason it was safe; corrected there too.
Also: --password-store=basic was missing from the branch that actually runs, working only because
chromiumoxide's DEFAULT_ARGS happens to supply it; seeding failures were silent, so the skill's
"sign in again" advice was offered for an unreadable profile it cannot fix; only --user-data-dir=X was
detected and not the space-separated form; installWrapper was unguarded at top level and crash-looped
on an unwritable ~/.local/bin; and it truncated the wrapper in place rather than renaming over it.
The searchPath copy is gone — the platform now exports locateBinary and this asks it. That copy is
what made the manifest probe refuse a rustup-only machine the sidecar could build on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+62
-11
@@ -36,34 +36,85 @@ fi
|
||||
|
||||
# Cookies plus the key that decrypts them. Login Data rides along so "remembered" form logins survive.
|
||||
# Everything else in a profile is cache and preference noise a capture does not need.
|
||||
#
|
||||
# ── The -wal files are not optional ──
|
||||
#
|
||||
# These are SQLite databases in WAL mode, and a recent write may live ONLY in the write-ahead log: the
|
||||
# browser has not checkpointed it into the .db yet. Copying `Cookies` without `Cookies-wal` therefore
|
||||
# produces a profile that is missing exactly the newest sessions — sign in, capture immediately, and the
|
||||
# capture is signed out, which reads as "sessions do not work" rather than as a missing file.
|
||||
#
|
||||
# `-shm` is deliberately NOT copied: it is shared memory for live coordination between processes, it is
|
||||
# rebuilt from the WAL on open, and a stale one is worse than none.
|
||||
#
|
||||
# Failures are REPORTED, not swallowed. The skill tells the user to sign in again when a capture comes
|
||||
# back logged out; that advice is useless if the real cause was an unreadable profile, and silence is
|
||||
# what made the two indistinguishable.
|
||||
seed_one() {
|
||||
[ -f "$1" ] || return 0
|
||||
cp "$1" "$2" 2>/dev/null || echo "iris-browser: could not copy $(basename "$1") — captures may be signed out" >&2
|
||||
}
|
||||
|
||||
seed_sessions() {
|
||||
dest="$1"
|
||||
[ -d "$PROFILE/Default" ] || return 0
|
||||
mkdir -p "$dest/Default"
|
||||
cp "$PROFILE/Local State" "$dest/Local State" 2>/dev/null || true
|
||||
cp "$PROFILE/Default/Cookies" "$dest/Default/Cookies" 2>/dev/null || true
|
||||
cp "$PROFILE/Default/Login Data" "$dest/Default/Login Data" 2>/dev/null || true
|
||||
[ -d "$PROFILE/Default" ] || {
|
||||
echo "iris-browser: no profile at $PROFILE — captures will be signed out until you sign in there" >&2
|
||||
return 0
|
||||
}
|
||||
mkdir -p "$dest/Default" || return 0
|
||||
seed_one "$PROFILE/Local State" "$dest/Local State"
|
||||
for db in "Cookies" "Login Data"; do
|
||||
seed_one "$PROFILE/Default/$db" "$dest/Default/$db"
|
||||
seed_one "$PROFILE/Default/$db-wal" "$dest/Default/$db-wal"
|
||||
done
|
||||
}
|
||||
|
||||
# Did the launcher already choose a profile directory?
|
||||
#
|
||||
# Both spellings. Chrome accepts `--user-data-dir=X` and `--user-data-dir X`, and matching only the first
|
||||
# would send us down the ephemeral path while the launcher used its own directory — two profiles, and the
|
||||
# sessions seeded into the one nobody opened.
|
||||
CALLER_DIR=""
|
||||
take_next=0
|
||||
for arg in "$@"; do
|
||||
if [ "$take_next" -eq 1 ]; then
|
||||
CALLER_DIR="$arg"
|
||||
take_next=0
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
--user-data-dir=*) CALLER_DIR="${arg#--user-data-dir=}" ;;
|
||||
--user-data-dir) take_next=1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --password-store=basic keeps cookie encryption portable between the profile and the copy: without it a
|
||||
# keyring-backed browser writes cookies that the copied `Local State` key cannot decrypt, and the capture
|
||||
# is signed out for a reason nothing reports. Passed on BOTH paths — chromiumoxide's own DEFAULT_ARGS
|
||||
# happen to include it today, which is not something this script should depend on.
|
||||
if [ -n "$CALLER_DIR" ]; then
|
||||
seed_sessions "$CALLER_DIR"
|
||||
exec "$BROWSER" "$@"
|
||||
exec "$BROWSER" "$@" --password-store=basic
|
||||
fi
|
||||
|
||||
EPHEMERAL="$(mktemp -d "${TMPDIR:-/tmp}/iris-profile.XXXXXX")"
|
||||
# CHECKED. `set -u` does not catch this: mktemp failing leaves EPHEMERAL an empty string, which becomes
|
||||
# `--user-data-dir=` — and Chrome reads an empty value as "use the default profile", i.e. the user's real
|
||||
# one. A capture would then run in, and write to, the browser they actually use.
|
||||
if ! EPHEMERAL="$(mktemp -d "${TMPDIR:-/tmp}/iris-profile.XXXXXX")" || [ -z "$EPHEMERAL" ]; then
|
||||
echo "iris-browser: could not create a temporary profile directory — refusing to launch" >&2
|
||||
exit 1
|
||||
fi
|
||||
trap 'rm -rf "$EPHEMERAL"' EXIT INT TERM
|
||||
seed_sessions "$EPHEMERAL"
|
||||
|
||||
# --password-store=basic keeps cookie encryption portable between the profile and the copy — without it a
|
||||
# keyring-backed browser writes cookies the copy cannot decrypt. Appended AFTER the launcher's args, and
|
||||
# so is our --user-data-dir, because for a repeated switch Chrome honours the last occurrence.
|
||||
"$BROWSER" "$@" --user-data-dir="$EPHEMERAL" --password-store=basic
|
||||
# NOT exec'd, unlike the branch above, because the trap has to outlive the browser to remove the profile —
|
||||
# and a profile holding copies of the user's cookies is not something to leave in /tmp. The cost is that
|
||||
# this shell stays alive as the parent; `wait` is what makes a signal to it reach the browser rather than
|
||||
# killing the wrapper and orphaning the child.
|
||||
#
|
||||
# Our --user-data-dir goes AFTER the launcher's args: for a repeated switch Chrome honours the last one.
|
||||
"$BROWSER" "$@" --user-data-dir="$EPHEMERAL" --password-store=basic &
|
||||
BROWSER_PID=$!
|
||||
trap 'kill "$BROWSER_PID" 2>/dev/null; rm -rf "$EPHEMERAL"' EXIT INT TERM
|
||||
wait "$BROWSER_PID"
|
||||
exit $?
|
||||
|
||||
Reference in New Issue
Block a user