The owner needs exact new-user simulation as a one-command launch (repeatable first-run BYOK testing). Option A, owner-approved: env-hiding switch + launcher script. Fix 1 — LH_IGNORE_ENV_KEYS=1 in the keys seam: when set, every env key is invisible app-wide; only auth.json counts. The mechanism is boot-time DELETION, not seam-local filtering: pi-ai resolves provider keys from process.env NATIVELY at every request (verified — auth/context.js's defaultProviderAuthContext reads live env, no boot snapshot), so deleting the managed vars from process.env in initAuthEnv is the ONE mechanism that hides keys from every consumer — model engines included, plus the three direct process.env readers (visual-maker, pdf-reader, openRouterApiKey). realEnv stays EMPTY under the switch (nothing counts as env), and the fill-gap still reflects auth.json keys so a fresh user can add keys mid-session and engines see them with no restart. keyValue reads file-only under the switch as belt-and-suspenders (a post-boot env reintroduction stays invisible to the seam). Unset switch = today's behavior, byte-for-byte (server-keys.test.ts pins it). Fix 2 — scripts/dev-fresh.sh (npm run dev:fresh): fresh throwaway LH_DATA_DIR (.fresh-data/, wiped each launch, --keep preserves), and — the subtle bit — LH_AUTH_FILE redirected INTO the fresh dir: dev's AUTH_FILE default is the repo-root auth.json, which may symlink the real keys; the fresh instance must never see it. Sets the switch, dedicated port (default 3005, --port overrideable, standing assignments 3000-3004 refused), prints URL + data dir, safe to rerun. .fresh-data/ gitignored. Test: tests/fresh-env-keys.test.ts (own process — the switch is boot-time): the engine-path deletion asserted directly, the env/file collision resolved to the file, status rows report file sources only, mid-session saveKey/deleteKey round-trip, idempotent re-init, and the unset-switch env-wins read seam live-checked. Live-verified on 3005 via the launcher: zero federated models, all providers keyPresent false, no present keys, no auth.json/settings.json in the fresh dir; then mid-session ZAI save → 7 zai models federate instantly, source 'file' — while .env.local's real keys stayed invisible throughout. Gates: typecheck, lint, 863/863, build.
100 lines
3.1 KiB
Bash
Executable File
100 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# THE EXACT NEW-USER SIMULATOR (owner, interlude 2026-09-22): one command,
|
|
# a fresh instance with zero keys and zero data — setup stage 1, every
|
|
# launch. The owner's repeatable first-run BYOK test bed.
|
|
#
|
|
# bash scripts/dev-fresh.sh # http://localhost:3005
|
|
# npm run dev:fresh # the same, via package.json
|
|
# scripts/dev-fresh.sh --port 3006 # any port OUTSIDE 3000-3004
|
|
# scripts/dev-fresh.sh --keep # don't wipe between runs
|
|
# (multi-session testing)
|
|
#
|
|
# WHY A SWITCH, NOT AN ENV SCRUB: a script cannot merely unset shell
|
|
# *_API_KEY vars — Next injects .env.local (the dev secrets) into the
|
|
# server env at boot, so a "fresh" instance would silently federate the
|
|
# real keys. LH_IGNORE_ENV_KEYS=1 (the keys seam, src/server/keys.ts)
|
|
# deletes every managed key var from the process after Next's env load:
|
|
# pi-ai reads provider keys from process.env NATIVELY at every request,
|
|
# so deletion hides keys from the whole app, model engines included.
|
|
#
|
|
# LH_DATA_DIR → .fresh-data/ (wiped each launch unless --keep)
|
|
# LH_AUTH_FILE → .fresh-data/auth.json (NEVER the repo-root auth.json
|
|
# — that file may symlink the real keys)
|
|
# LH_IGNORE_ENV_KEYS=1
|
|
#
|
|
# Safe to rerun. The data dir is disposable scratch — never copy it back.
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
PORT="3005"
|
|
DATA_DIR="$ROOT/.fresh-data"
|
|
KEEP=false
|
|
|
|
usage() {
|
|
sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'
|
|
}
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--port)
|
|
[ $# -ge 2 ] || { echo "error: --port needs a value" >&2; exit 1; }
|
|
PORT="$2"
|
|
shift 2
|
|
;;
|
|
--port=*)
|
|
PORT="${1#--port=}"
|
|
shift
|
|
;;
|
|
--keep)
|
|
KEEP=true
|
|
shift
|
|
;;
|
|
-h | --help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "error: unknown flag: $1" >&2
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if ! [[ "$PORT" =~ ^[0-9]+$ ]]; then
|
|
echo "error: --port must be a number, got \"$PORT\"" >&2
|
|
exit 1
|
|
fi
|
|
case "$PORT" in
|
|
3000 | 3001 | 3002 | 3003 | 3004)
|
|
echo "error: port $PORT is a standing assignment (3000 master, 3001 UI, 3002 graph, 3003 teacher, 3004 platform)" >&2
|
|
echo " — the fresh instance runs on a dedicated port (default 3005)" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [ "$KEEP" != "true" ]; then
|
|
rm -rf "$DATA_DIR"
|
|
else
|
|
# --keep preserves prior sessions (multi-session testing); the dir is
|
|
# still created if this is the first run.
|
|
:
|
|
fi
|
|
mkdir -p "$DATA_DIR"
|
|
|
|
export LH_DATA_DIR="$DATA_DIR"
|
|
export LH_AUTH_FILE="$DATA_DIR/auth.json"
|
|
export LH_IGNORE_ENV_KEYS=1
|
|
export PORT
|
|
|
|
echo "── fresh-user instance ─────────────────────────────────"
|
|
echo " URL : http://localhost:$PORT"
|
|
echo " data dir : $DATA_DIR ($( [ "$KEEP" = "true" ] && echo 'kept across runs' || echo 'wiped next launch unless --keep' ))"
|
|
echo " keys : env keys hidden (LH_IGNORE_ENV_KEYS=1), auth.json empty"
|
|
echo "─────────────────────────────────────────────────────────"
|
|
|
|
cd "$ROOT"
|
|
exec npm run dev
|