Something went wrong. Try again.
a tiny pet with a GPT-2 brain, living in your browser
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118#!/bin/bash# Build the static site: the page, benchmark, app modules, service worker, the# manifest and icons, and a _headers file for hosts that read one. The model# and runtime directories are published separately (train/publish.sh) into# the same tree, under models/ and runtime/.## Usage: site/build.sh OUT_DIR## Optional env:# RELEASE release description (default: site/release.json): modelId, size,# trained, news, features, older, runtime, source; optional analyticsToken# belongs in a deployment override (see docs/deployment.md)# BASE URL path the site is served under, with leading and trailing# slash (default: /)# MODELS_URL URL prefix of the model directories (default: ${BASE}models/)# RUNTIME_URL URL of the runtime directory (default: ${BASE}runtime/<release runtime>)# SOURCE URL for the page's source link (default: the release's "source", else #)# CF_ANALYTICS_TOKEN optional Cloudflare Web Analytics site token (32 hex# characters); overrides release analyticsToken. Empty disables it.# REMOTE_ENDPOINT optional default remote-host URL (else release remoteEndpoint,# else empty). Local remains the default execution mode.# MODEL_ID, SIZE override the release's model (dev-site.sh uses these)# OUT_DIR is replaced except for models/ and runtime/, which are left alone.# Copying, rendering and validation finish in a sibling staging directory before# existing output is changed; installing the staged entries is not atomic.set -euo pipefail[ $# -eq 1 ] || { sed -n '2,/^set -e/p' "$0" | sed '$d;s/^# \{0,1\}//' >&2; exit 2; }HERE=$(cd "$(dirname "$0")" && pwd); REPO=$(dirname "$HERE")OUT=$(realpath -m "$1")RELEASE=${RELEASE:-$HERE/release.json} BASE=${BASE:-/}case "$BASE" in /*/|/) ;; *) echo "BASE must start and end with /" >&2; exit 2;; esac[ -f "$RELEASE" ] || { echo "no release file: $RELEASE" >&2; exit 2; }
# Build the shared rules for the browser, then type-check and bundle before# replacing output. Hash emitted bytes, including the bundled Preact# dependency and the rules module, so a browser never mixes app/worker/rules versions.[ -x "$REPO/node_modules/.bin/tsc" ] || { echo "Run npm ci before building the site" >&2; exit 2; }"$REPO/core/wasm/build.sh"(cd "$REPO" && npm run typecheck)BUNDLE=$(mktemp -d)STAGE=trap 'rm -rf "$BUNDLE"; [ -z "$STAGE" ] || rm -rf "$STAGE"' EXITtrap 'exit 1' HUP INT TERMnode "$HERE/bundle.mjs" "$BUNDLE"VER=$(cat "$BUNDLE"/*.js "$BUNDLE"/*.map "$BUNDLE"/*.wasm | sha256sum | cut -c1-12)mkdir -p "$(dirname "$OUT")"STAGE=$(mktemp -d "$(dirname "$OUT")/.$(basename "$OUT").partial.XXXXXX")mkdir -p "$STAGE/app/$VER" "$STAGE/assets"cp "$BUNDLE"/* "$STAGE/app/$VER/"cp "$HERE"/assets/* "$STAGE/assets/"cp "$HERE/sw.js" "$STAGE/sw.js"
python3 - "$HERE/index.html" "$STAGE" "$RELEASE" "$BASE" "$VER" "$REPO/test/bench.html" <<'PY'import json, os, re, syspage, out, release, base, ver, bench = sys.argv[1:]r = json.load(open(release))mid = os.environ.get("MODEL_ID") or r["modelId"]size = int(os.environ.get("SIZE") or r["size"])models = os.environ.get("MODELS_URL") or base + "models/"runtime = os.environ.get("RUNTIME_URL") or f"{base}runtime/{r['runtime']}"source = os.environ.get("SOURCE") or r.get("source") or "#"analytics_token = os.environ.get("CF_ANALYTICS_TOKEN", r.get("analyticsToken", ""))if not isinstance(analytics_token, str) or (analytics_token and not re.fullmatch(r"[0-9a-fA-F]{32}", analytics_token)): sys.exit("CF_ANALYTICS_TOKEN / analyticsToken must be empty or a 32-character hexadecimal site token")analytics = notice = ""if analytics_token: beacon = json.dumps({"token": analytics_token}) analytics = ("<!-- Cloudflare Web Analytics --><script type='module' " "src='https://static.cloudflareinsights.com/beacon.min.js' " f"data-cf-beacon='{beacon}'></script><!-- End Cloudflare Web Analytics -->") notice = ('<p>This site uses <a href="https://www.cloudflare.com/web-analytics/">Cloudflare Web Analytics</a> ' 'for page visits and performance, without analytics cookies or fingerprinting. ' 'Pet names, conversations, diaries and saves are not included.</p>')values = { "RUNTIME": runtime, "MODELS": models, "MODEL_ID": mid, "SIZE": str(size), "SIZE_MB": str(round(size / 1e6)), "APP": f"{base}app/{ver}", "ASSETS": f"{base}assets", "TRAINED": r.get("trained", ""), "SOURCE": source, "ANALYTICS": analytics, "ANALYTICS_NOTICE": notice, # JSON values for the config block "NEWS": json.dumps(r.get("news", "")), "FEATURES": json.dumps(r.get("features", [])), "OLDER": json.dumps([o for o in r.get("older", []) if o["id"] != mid]), "REMOTE_ENDPOINT": json.dumps(os.environ.get("REMOTE_ENDPOINT", r.get("remoteEndpoint", ""))).replace("<", "\\u003c"),}def render(path): s = re.sub(r"@([A-Z_]+)@", lambda m: values.get(m.group(1), m.group(0)), open(path).read()) left = re.findall(r"@[A-Z_]+@", s) if left: sys.exit(f"unfilled placeholders in {path}: {sorted(set(left))}") return ss = render(page)json.loads(re.search(r'id="pet-config">(.*?)</script>', s, re.S).group(1)) # the config must parseopen(f"{out}/index.html", "w").write(s)open(f"{out}/bench.html", "w").write(render(bench))# Headers for static hosts that read a _headers file; other servers need the# same rules in their own configuration (docs/deployment.md).open(f"{out}/_headers", "w").write(f"""{base}* Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp{base}app/* Cache-Control: public, max-age=31536000, immutable{base}models/* Cache-Control: public, max-age=31536000, immutable{base}runtime/* Cache-Control: public, max-age=31536000, immutable{base}sw.js Cache-Control: no-cache{base}bench.html Cache-Control: no-cache""")PY
# Keep published model/runtime directories, replacing the other entries only# after every staged file has been copied and both pages have rendered.mkdir -p "$OUT"find "$OUT" -mindepth 1 -maxdepth 1 ! -name models ! -name runtime -exec rm -rf {} +mv "$STAGE"/* "$OUT/"echo "built $OUT (app $VER)"