Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,31 @@ Internal macOS menu bar app for secure multi-account switching.
./.build/arm64-apple-macosx/release/jnslayer2-helper doctor --json
./.build/arm64-apple-macosx/release/jnslayer2-helper relogin --stale --json
```

## Remote-Safe Worker Mode

Keep the desktop Codex App and ChatGPT mobile remote-control pinned to the main
`~/.codex/auth.json` account. For quota-heavy work on another stored account,
run Codex through an isolated worker home instead of switching the shared auth:

```bash
codex-worker --email worker@example.com -- login status
codex-worker --email worker@example.com -- exec -C "$PWD" "summarize this repo"
```

`codex-worker` materializes `~/.jnslayer2/codex-homes/<email>/auth.json` from
the stored account file and sets `CODEX_HOME` only for that process. It does not
modify `~/.codex/auth.json`, restart Codex.app, or affect ChatGPT mobile
remote-control presence.

To protect the mobile remote-control account from accidental shared-auth
switches, run the guard with the ChatGPT account used on the phone:

```bash
CODEX_REMOTE_MAIN_EMAIL=main@example.com ./Scripts/codex-remote-main-guard
```

If `~/.codex/auth.json` no longer matches that account, the guard restores the
stored auth from `~/.jnslayer2/accounts/<email>/auth.json` and re-enables the
Codex app-server remote-control daemon. Run it from launchd or another scheduler
if you want continuous protection.
72 changes: 72 additions & 0 deletions Scripts/codex-remote-main-guard
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -euo pipefail

PINNED_EMAIL="${CODEX_REMOTE_MAIN_EMAIL:-}"
HOME_DIR="${HOME:?HOME is required}"
ACTIVE_AUTH="$HOME_DIR/.codex/auth.json"
PINNED_AUTH="$HOME_DIR/.jnslayer2/accounts/$PINNED_EMAIL/auth.json"
LOCK_DIR="/tmp/codex-remote-main-guard.lock"
CODEX_BIN="/Applications/Codex.app/Contents/Resources/codex"

mkdir -p "$HOME_DIR/.codex"

if [[ -z "$PINNED_EMAIL" ]]; then
echo "codex-remote-main-guard: set CODEX_REMOTE_MAIN_EMAIL to the ChatGPT account used by mobile remote-control" >&2
exit 2
fi

if [[ ! -f "$PINNED_AUTH" ]]; then
echo "codex-remote-main-guard: pinned auth missing: $PINNED_AUTH" >&2
exit 1
fi

active_account_id() {
/usr/bin/python3 - "$1" <<'PY'
import json
import sys
try:
with open(sys.argv[1], "r", encoding="utf-8") as handle:
auth = json.load(handle)
print((auth.get("tokens") or {}).get("account_id") or "")
except Exception:
print("")
PY
}

if [[ -e "$LOCK_DIR" && ! -d "$LOCK_DIR" ]]; then
rm -f "$LOCK_DIR"
elif [[ -d "$LOCK_DIR" ]]; then
rmdir "$LOCK_DIR" 2>/dev/null || true
fi

if ! mkdir "$LOCK_DIR" 2>/dev/null; then
exit 0
fi
trap 'rmdir "$LOCK_DIR" 2>/dev/null || true' EXIT

pinned_id="$(active_account_id "$PINNED_AUTH")"
current_id=""
if [[ -f "$ACTIVE_AUTH" ]]; then
current_id="$(active_account_id "$ACTIVE_AUTH")"
fi

if [[ -z "$pinned_id" ]]; then
echo "codex-remote-main-guard: pinned auth has no account_id" >&2
exit 1
fi

if [[ "$current_id" != "$pinned_id" ]]; then
install -m 600 "$PINNED_AUTH" "$ACTIVE_AUTH"
echo "codex-remote-main-guard: restored Codex remote account to $PINNED_EMAIL"
"$CODEX_BIN" app-server daemon restart \
--enable remote_control \
--enable hooks \
--enable goals \
--enable prevent_idle_sleep >/dev/null
fi

"$CODEX_BIN" app-server daemon enable-remote-control \
--enable remote_control \
--enable hooks \
--enable goals \
--enable prevent_idle_sleep >/dev/null
90 changes: 90 additions & 0 deletions Scripts/codex-worker
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env bash
set -euo pipefail

usage() {
cat <<'EOF'
Usage:
codex-worker --email <account-email> -- <codex arguments...>

Examples:
codex-worker --email worker@example.com -- login status
codex-worker --email worker@example.com -- exec -C "$PWD" "summarize this repo"

This runs Codex with an isolated CODEX_HOME under ~/.jnslayer2/codex-homes.
It does not modify ~/.codex/auth.json, so Codex App remote-control keeps using
the main desktop account.
EOF
}

email=""
while [[ $# -gt 0 ]]; do
case "$1" in
--email)
if [[ $# -lt 2 ]]; then
echo "codex-worker: --email requires a value" >&2
exit 2
fi
email="$2"
shift 2
;;
--help|-h)
usage
exit 0
;;
--)
shift
break
;;
*)
echo "codex-worker: unknown argument before --: $1" >&2
usage >&2
exit 2
;;
esac
done

if [[ -z "$email" ]]; then
echo "codex-worker: missing --email" >&2
usage >&2
exit 2
fi

if [[ $# -eq 0 ]]; then
echo "codex-worker: missing codex arguments after --" >&2
usage >&2
exit 2
fi

account_auth="$HOME/.jnslayer2/accounts/$email/auth.json"
if [[ ! -f "$account_auth" ]]; then
echo "codex-worker: stored auth not found for $email" >&2
echo "expected: $account_auth" >&2
exit 1
fi

safe_name="$(printf '%s' "$email" | tr -cs 'A-Za-z0-9._@-' '_' | sed 's/^_//; s/_$//')"
worker_home="$HOME/.jnslayer2/codex-homes/$safe_name"
mkdir -p "$worker_home"
chmod 700 "$worker_home"

install -m 600 "$account_auth" "$worker_home/auth.json"

if [[ -f "$HOME/.codex/config.toml" && ! -f "$worker_home/config.toml" ]]; then
install -m 600 "$HOME/.codex/config.toml" "$worker_home/config.toml"
fi

codex_bin="${JNSLAYER2_CODEX:-$HOME/.local/bin/codex}"
if [[ ! -x "$codex_bin" ]]; then
codex_bin="/Applications/Codex.app/Contents/Resources/codex"
fi
if [[ ! -x "$codex_bin" ]]; then
codex_bin="/opt/homebrew/bin/codex"
fi
if [[ ! -x "$codex_bin" ]]; then
echo "codex-worker: no executable codex binary found" >&2
exit 1
fi

export CODEX_HOME="$worker_home"
export PATH="$HOME/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
exec "$codex_bin" "$@"