From 381ff6624b30377a32a61b7fa37c6e002d3c6a5f Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Wed, 24 Jun 2026 10:42:43 -0700 Subject: [PATCH] ac-native: flash-time preset greeting city (ac-inscribe --city) Boot greeting (splash subtitle + TTS) already geolocates via /mnt/last-city.txt after the first wifi connect. Add a config.json "city" field, baked by `ac-inscribe --city `, so a freshly-flashed device greets from wherever it's shipped (e.g. Ridgewood) on first boot, before any IP lookup. Falls back to that preset, then "Los Angeles", when the live cache is absent. --- fedac/native/ac-inscribe | 19 +++++++++++++++++-- fedac/native/scripts/inscribe-lib.sh | 5 +++-- fedac/native/src/ac-native.c | 22 +++++++++++++++++++--- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/fedac/native/ac-inscribe b/fedac/native/ac-inscribe index c645c381c0..c8b8288dab 100755 --- a/fedac/native/ac-inscribe +++ b/fedac/native/ac-inscribe @@ -14,6 +14,8 @@ # ac-inscribe --no-claude # skip baking local Claude session # ac-inscribe --inspect # print summary of an existing inscription # ac-inscribe --for-handle # admin: inscribe for another user +# ac-inscribe --mood # override boot splash subtitle (else uses the handle's AC mood) +# ac-inscribe --city # preset first-boot greeting city until the device geolocates (e.g. "Ridgewood") # # Exit codes: # 0 inscription written and valid @@ -49,6 +51,9 @@ INSPECT="" FOR_HANDLE="" MOOD_OVERRIDE="" MOOD_OVERRIDE_SET=0 +CITY_OVERRIDE="" +CITY_OVERRIDE_SET=0 +ACI_CITY="" # config "city"; only set via --city (no account fetch), so default empty under `set -u` while [ $# -gt 0 ]; do case "$1" in @@ -59,6 +64,7 @@ while [ $# -gt 0 ]; do --inspect) INSPECT="$2"; shift ;; --for-handle) FOR_HANDLE="${2#@}"; shift ;; --mood) MOOD_OVERRIDE="$2"; MOOD_OVERRIDE_SET=1; shift ;; + --city) CITY_OVERRIDE="$2"; CITY_OVERRIDE_SET=1; shift ;; --help|-h) sed -n '2,28p' "$0" | sed 's/^# *//' exit 0 @@ -157,14 +163,19 @@ if [ -n "${FOR_HANDLE}" ]; then ACI_MOOD="${MOOD_OVERRIDE}" aci_ok "mood override: ${ACI_C_BOLD}${ACI_MOOD:-(cleared)}${ACI_C_RESET}" fi + if [ "${CITY_OVERRIDE_SET}" -eq 1 ]; then + ACI_CITY="${CITY_OVERRIDE}" + aci_ok "city override: ${ACI_C_BOLD}${ACI_CITY:-(cleared)}${ACI_C_RESET}" + fi aci_step 3 4 "Assembling inscription bundle for @${ACI_HANDLE}" BUNDLE=$(aci_build_inscription_json) || aci_die "bundle assembly failed" BUNDLE_BYTES=$(printf '%s' "${BUNDLE}" | wc -c | tr -d ' ') aci_ok "version 1 envelope, ${BUNDLE_BYTES} bytes" - aci_ok "fields: handle, sub, email(empty), token(empty)$([ -n "${ACI_CLAUDE_TOKEN}" ] && printf ', claudeToken')$([ -n "${ACI_GITHUB_PAT}" ] && printf ', githubPat')$([ -n "${ACI_HANDLE_COLORS_JSON}" ] && printf ', colors')$([ -n "${ACI_MOOD}" ] && printf ', mood')" + aci_ok "fields: handle, sub, email(empty), token(empty)$([ -n "${ACI_CLAUDE_TOKEN}" ] && printf ', claudeToken')$([ -n "${ACI_GITHUB_PAT}" ] && printf ', githubPat')$([ -n "${ACI_HANDLE_COLORS_JSON}" ] && printf ', colors')$([ -n "${ACI_MOOD}" ] && printf ', mood')$([ -n "${ACI_CITY}" ] && printf ', city')" [ -n "${ACI_HANDLE_COLORS_JSON}" ] && aci_ok "handle-colors: ${ACI_C_BOLD}per-character palette baked${ACI_C_RESET}" || aci_info "handle-colors: none stored — boot uses theme color" [ -n "${ACI_MOOD}" ] && aci_ok "mood: ${ACI_C_BOLD}${ACI_MOOD}${ACI_C_RESET}" || aci_info "mood: none yet — boot subtitle uses default" + [ -n "${ACI_CITY}" ] && aci_ok "city: ${ACI_C_BOLD}${ACI_CITY}${ACI_C_RESET} (greeting until geolocated)" || aci_info "city: none — first-boot greeting uses default" aci_warn "access_token is empty — recipient runs ac-login on device once for tape uploads" if [ "${YES}" -eq 0 ] && [ -t 0 ]; then @@ -255,13 +266,17 @@ if [ "${MOOD_OVERRIDE_SET}" -eq 1 ]; then ACI_MOOD="${MOOD_OVERRIDE}" aci_ok "mood override: ${ACI_C_BOLD}${ACI_MOOD:-(cleared)}${ACI_C_RESET}" fi +if [ "${CITY_OVERRIDE_SET}" -eq 1 ]; then + ACI_CITY="${CITY_OVERRIDE}" + aci_ok "city override: ${ACI_C_BOLD}${ACI_CITY:-(cleared)}${ACI_C_RESET}" +fi # Step 4 — assemble bundle aci_step 4 5 "Assembling inscription bundle" BUNDLE=$(aci_build_inscription_json) || aci_die "bundle assembly failed" BUNDLE_BYTES=$(printf '%s' "${BUNDLE}" | wc -c | tr -d ' ') aci_ok "version 1 envelope, ${BUNDLE_BYTES} bytes" -aci_ok "fields: handle, sub, email, token$([ -n "${ACI_CLAUDE_TOKEN}" ] && printf ', claudeToken')$([ -n "${ACI_GITHUB_PAT}" ] && printf ', githubPat')$([ -n "${ACI_CLAUDE_CREDS}" ] && printf ', claudeCreds')$([ -n "${ACI_CLAUDE_STATE}" ] && printf ', claudeState')$([ -n "${ACI_HANDLE_COLORS_JSON}" ] && printf ', colors')$([ -n "${ACI_MOOD}" ] && printf ', mood')" +aci_ok "fields: handle, sub, email, token$([ -n "${ACI_CLAUDE_TOKEN}" ] && printf ', claudeToken')$([ -n "${ACI_GITHUB_PAT}" ] && printf ', githubPat')$([ -n "${ACI_CLAUDE_CREDS}" ] && printf ', claudeCreds')$([ -n "${ACI_CLAUDE_STATE}" ] && printf ', claudeState')$([ -n "${ACI_HANDLE_COLORS_JSON}" ] && printf ', colors')$([ -n "${ACI_MOOD}" ] && printf ', mood')$([ -n "${ACI_CITY}" ] && printf ', city')" [ -n "${ACI_HANDLE_COLORS_JSON}" ] && aci_ok "handle-colors: ${ACI_C_BOLD}per-character palette baked${ACI_C_RESET}" || aci_info "handle-colors: none stored — boot uses theme color" [ -n "${ACI_MOOD}" ] && aci_ok "mood: ${ACI_C_BOLD}${ACI_MOOD}${ACI_C_RESET}" || aci_info "mood: none yet — boot subtitle uses default" diff --git a/fedac/native/scripts/inscribe-lib.sh b/fedac/native/scripts/inscribe-lib.sh index 05af6f9da7..42e7322e54 100644 --- a/fedac/native/scripts/inscribe-lib.sh +++ b/fedac/native/scripts/inscribe-lib.sh @@ -256,7 +256,7 @@ aci_collect_local_claude() { # and ACI_MOOD (plain string). aci_build_usb_config_json() { node -e " - const [handle, sub, email, token, claudeToken, githubPat, claudeCreds, claudeState, colorsJson, mood] = process.argv.slice(1); + const [handle, sub, email, token, claudeToken, githubPat, claudeCreds, claudeState, colorsJson, mood, city] = process.argv.slice(1); const cfg = { handle, sub, email, token }; if (claudeToken) cfg.claudeToken = claudeToken; if (githubPat) cfg.githubPat = githubPat; @@ -269,11 +269,12 @@ aci_build_usb_config_json() { } } catch (e) {} if (mood) cfg.mood = mood; + if (city) cfg.city = city; process.stdout.write(JSON.stringify(cfg)); " "${ACI_HANDLE:-}" "${ACI_SUB:-}" "${ACI_EMAIL:-}" "${ACI_ACCESS_TOKEN:-}" \ "${ACI_CLAUDE_TOKEN:-}" "${ACI_GITHUB_PAT:-}" \ "${ACI_CLAUDE_CREDS:-}" "${ACI_CLAUDE_STATE:-}" \ - "${ACI_HANDLE_COLORS_JSON:-}" "${ACI_MOOD:-}" + "${ACI_HANDLE_COLORS_JSON:-}" "${ACI_MOOD:-}" "${ACI_CITY:-}" } # aci_build_inscription_json diff --git a/fedac/native/src/ac-native.c b/fedac/native/src/ac-native.c index 7669f2c2b0..dcbd8023ff 100644 --- a/fedac/native/src/ac-native.c +++ b/fedac/native/src/ac-native.c @@ -740,6 +740,13 @@ static int boot_title_colors_len = 0; // and falls back to the original "enjoy !" rendering. static char boot_mood[256] = ""; +// Flash-time preset city — baked into config.json "city" by +// `ac-inscribe --city` so a device greets from wherever it's being shipped +// (e.g. "Ridgewood") before it has ever geolocated. read_cached_city() uses +// this as the fallback when /mnt/last-city.txt (the live IP-lookup cache) +// doesn't exist yet; once the device geolocates, that cache wins. +static char preset_city[96] = ""; + // (Hardware device identity globals are defined further up — before the // compute_device_fingerprint() helper that needs them in file order.) @@ -878,6 +885,10 @@ static void load_boot_visual_config(void) { parse_config_string(json, "\"mood\"", boot_mood, sizeof(boot_mood)); } if (boot_mood[0]) ac_log("[ac-native] Boot mood: %s\n", boot_mood); + + // Flash-time preset greeting city (used until the device geolocates). + parse_config_string(json, "\"city\"", preset_city, sizeof(preset_city)); + if (preset_city[0]) ac_log("[ac-native] Preset city: %s\n", preset_city); } // Read wifi flag (default: enabled) @@ -2283,8 +2294,10 @@ static int get_la_hour(void) { // Fill buf with the city name for the boot greeting. The geo piece writes // /mnt/last-city.txt after a successful IP lookup, so this reads whatever -// was cached on a previous boot. Falls back to "Los Angeles" on first boot -// or when the cache is missing/empty — matches the pre-cache greeting. +// was cached on a previous boot. When that cache is missing/empty (first +// boot, before any IP lookup) it falls back to the flash-time preset city +// (config.json "city", baked by `ac-inscribe --city`) so a device greets +// from wherever it's shipped, then to "Los Angeles" as a last resort. static void read_cached_city(char *buf, size_t len) { if (!buf || len == 0) return; buf[0] = 0; @@ -2300,7 +2313,10 @@ static void read_cached_city(char *buf, size_t len) { fclose(f); } if (buf[0] == 0) { - strncpy(buf, "Los Angeles", len - 1); + // No live geolocation cache yet — fall back to the flash-time preset + // city (where the device was shipped), then to "Los Angeles". + const char *fallback = preset_city[0] ? preset_city : "Los Angeles"; + strncpy(buf, fallback, len - 1); buf[len - 1] = 0; } } -- 2.51.2