#!/bin/bash
# ahel-asset-agent.sh — register the M1 as an Arkenstone compute asset.
#
# PARKED TEMPLATE — NOT INSTALLED. The 2026-07-29 fresh redeploy of the
# orchestrator starts with an empty compute-asset registry, and machines are
# onboarded through the in-app "Add machine" wizard (which mints a single-use
# enrollment token and generates its own install one-liner — see
# ahel-gui/lib/arkenstone-assets.js). Do not install this by hand; it exists as the
# reference shape of the interim shell heartbeat until the Go `ahel-agent`
# runner covers M1. (Formerly `brain-asset-agent.sh`, retired with the rest of
# the pre-rename identifiers.)
#
# When (and only when) the wizard installs it, it runs on the M1 as the gui/501
# LaunchAgent com.ahel.ahel-asset (KeepAlive). Every 60s it:
#   1. asks the POOL UPSTREAM (Bifrost's ahel-pool provider base_url, see
#      infra/m1/bifrost-config.json) for its model list and prefixes the ids
#      with "ahel-pool/" — GOTCHA: Bifrost's own /v1/models aggregates ONLY
#      openrouter/*; custom providers (ahel-pool, claude-code) never appear
#      there (same gap the GUI papers over by force-including
#      DEFAULT_CHAT_MODEL), so filtering Bifrost /models with jq
#      would always yield []. Bifrost is still probed for liveness — the
#      registered base_url routes THROUGH it;
#   2. POSTs {key, label, base_url, models, meta} to the orchestrator's
#      /arkenstone/v1/assets/register on the box — registration IS the heartbeat
#      (Arkenstone marks an asset offline after ~120s of silence; it never
#      dials into the home network, only this outbound call exists).
#
# base_url is Bifrost's TAILNET address — Arkenstone pods on the box reach the M1
# over the tailnet, not the LAN.
#
# Auth: bearer read from a 600-mode token file; the token is never logged.
# Install target: /Users/admin/.ahel/ahel-asset-agent.sh (canonical copy in
# ahel-gui repo infra/m1/ — edit there; see infra/README.md).
set -u

# launchd starts with a bare PATH; jq/curl live under homebrew on the M1.
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"

BIFROST="${BIFROST_URL:-http://192.168.8.168:8081/v1}"
# Bifrost's ahel-pool provider upstream (M4 pool head; bifrost-config.json).
POOL="${POOL_URL:-http://192.168.8.178:8080/v1}"
ARKENSTONE="${ARKENSTONE_URL:-http://100.108.202.49:30040}"
TOKEN_FILE="${ARKENSTONE_TOKEN_FILE:-/Users/admin/.config/ahel/arkenstone-token}"
INTERVAL="${AHEL_ASSET_INTERVAL:-60}"

ASSET_KEY="m1-pool"
ASSET_LABEL="M1 + M4 pool"
ASSET_BASE_URL="http://100.117.228.68:8081/v1"

log() { printf '%s %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$*"; }

beat() {
  models=$(curl -fsS --max-time 10 "$POOL/models" 2>/dev/null \
    | jq -c '[.data[].id | {id: ("ahel-pool/" + .)}]' 2>/dev/null)
  if [ -z "${models:-}" ] || [ "$models" = "[]" ]; then
    log "skip: pool upstream $POOL serves no models (pool asleep?)"
    return 0
  fi
  if ! curl -fsS -o /dev/null --max-time 10 "$BIFROST/models" 2>/dev/null; then
    log "skip: Bifrost at $BIFROST not answering (the asset routes through it)"
    return 0
  fi
  if [ ! -r "$TOKEN_FILE" ]; then
    log "skip: token file $TOKEN_FILE missing/unreadable"
    return 0
  fi
  payload=$(jq -nc --argjson models "$models" \
    --arg key "$ASSET_KEY" --arg label "$ASSET_LABEL" --arg base_url "$ASSET_BASE_URL" \
    '{key: $key, label: $label, base_url: $base_url, models: $models,
      meta: {host: "ahel-m1", agent: "ahel-asset-agent"}}')
  code=$(curl -sS -o /tmp/ahel-asset-agent.last.json -w '%{http_code}' \
    --max-time 15 -X POST \
    -H "Authorization: Bearer $(cat "$TOKEN_FILE")" \
    -H 'Content-Type: application/json' \
    -d "$payload" "$ARKENSTONE/arkenstone/v1/assets/register" 2>/dev/null)
  if [ "${code:-000}" = "200" ]; then
    log "registered $ASSET_KEY ($(printf %s "$models" | jq 'length') models) -> $ARKENSTONE"
  else
    log "register failed: http ${code:-000} $(head -c 200 /tmp/ahel-asset-agent.last.json 2>/dev/null)"
  fi
}

log "ahel-asset-agent starting (bifrost=$BIFROST orchestrator=$ARKENSTONE interval=${INTERVAL}s)"
while :; do
  beat || true
  sleep "$INTERVAL"
done
