Something went wrong. Try again.
fork of prime agent with changes i want but are not upstreamable probqbly (and some fixes)
Something went wrong. Try again.
prime-agent install.sh
80 kB · 2357 lines
Shell
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358#!/bin/sh# prime-agent-native-recovery-v1
set -eu
# Keep these sentinels split so release publishing only rewrites the configured# values below; local or unpublished copies still need unreplaced values to compare.prime_agent_unconfigured_base_url="__PRIME_AGENT_DOWNLOAD_BASE""_URL__"prime_agent_unconfigured_default_release_channel="__PRIME_AGENT_DEFAULT_RELEASE_""CHANNEL__"prime_agent_base_url="${PRIME_AGENT_DOWNLOAD_BASE_URL:-__PRIME_AGENT_DOWNLOAD_BASE_URL__}"prime_agent_base_url="${prime_agent_base_url%/}"prime_agent_default_release_channel="__PRIME_AGENT_DEFAULT_RELEASE_CHANNEL__"if [ "$prime_agent_default_release_channel" = "$prime_agent_unconfigured_default_release_channel" ]; then prime_agent_default_release_channel=stablefiprime_agent_release_channel="${PRIME_AGENT_RELEASE_CHANNEL:-$prime_agent_default_release_channel}"prime_agent_package="${PRIME_AGENT_PACKAGE:-prime-agent}"prime_agent_cmd="${PRIME_AGENT_CMD:-prime-agent}"prime_agent_esc=$(printf '\033')prime_agent_original_path="${PATH:-}"prime_agent_reset="${prime_agent_esc}[0m"prime_agent_bold="${prime_agent_esc}[1m"prime_agent_italic="${prime_agent_esc}[3m"prime_agent_hide_cursor="${prime_agent_esc}[?25l"prime_agent_show_cursor="${prime_agent_esc}[?25h"prime_agent_home_cursor="${prime_agent_esc}[H"prime_agent_clear_screen="${prime_agent_esc}[2J${prime_agent_esc}[H"prime_agent_clear_line="${prime_agent_esc}[K"prime_agent_sync_start="${prime_agent_esc}[?2026h"prime_agent_sync_end="${prime_agent_esc}[?2026l"prime_agent_color_text="${prime_agent_esc}[38;2;244;244;245m"prime_agent_color_muted="${prime_agent_esc}[38;2;161;161;170m"prime_agent_color_dim="${prime_agent_esc}[38;2;113;113;122m"prime_agent_color_primary="${prime_agent_esc}[38;2;127;91;213m"prime_agent_color_scan="${prime_agent_esc}[38;2;14;165;233m"prime_agent_color_warning="${prime_agent_esc}[38;2;245;158;11m"readonly prime_agent_unconfigured_base_url prime_agent_unconfigured_default_release_channel prime_agent_base_url prime_agent_default_release_channel prime_agent_release_channel prime_agent_package prime_agent_cmd prime_agent_esc prime_agent_original_pathreadonly prime_agent_reset prime_agent_bold prime_agent_italic prime_agent_hide_cursor prime_agent_show_cursor prime_agent_home_cursor prime_agent_clear_screen prime_agent_clear_linereadonly prime_agent_sync_start prime_agent_sync_endreadonly prime_agent_color_text prime_agent_color_muted prime_agent_color_dim prime_agent_color_primary prime_agent_color_scan prime_agent_color_warning
prime_agent_screen_enabled=0prime_agent_screen_frame=0prime_agent_screen_cols=80prime_agent_screen_rows=24prime_agent_screen_drawn=0prime_agent_screen_last_cols=0prime_agent_screen_last_rows=0prime_agent_screen_layout_ready=0prime_agent_screen_layout_show_logo=0prime_agent_screen_layout_lab_width=0prime_agent_screen_render_lab_width=0prime_agent_screen_compact=0prime_agent_download_dir=prime_agent_bootstrap_kernel_on_install=0prime_agent_screen_title=prime_agent_screen_status=prime_agent_screen_detail=prime_agent_screen_question=prime_agent_animation_frame=0prime_agent_native_stage=prime_agent_native_lock=prime_agent_allow_insecure_http=0# Tests point platform detection at a fake /proc and /lib without needing a container.prime_agent_native_sysroot="${PRIME_AGENT_NATIVE_SYSROOT_FOR_TESTS:-}"
main() { if [ "${1:-}" = --rollback ]; then prime_agent_native_rollback return fi if [ "${1:-}" = --native-platform ]; then prime_agent_native_platform return fi case "${PRIME_AGENT_INSTALL_METHOD:-auto}" in auto|binary|node) ;; *) printf 'error: PRIME_AGENT_INSTALL_METHOD must be auto, binary or node.\n' >&2; exit 1 ;; esac if [ "${PRIME_AGENT_INSTALL_METHOD:-auto}" != node ]; then if native_platform=$(prime_agent_native_platform); then prime_agent_install_native "$native_platform" "$@" return fi if [ "${PRIME_AGENT_INSTALL_METHOD:-auto}" = binary ]; then printf 'error: no compatible compiled archive is available for this platform.\n' >&2 exit 1 fi printf 'Using the Node installation for this platform.\n' >&2 fi prime_agent_install_node "$@"}
prime_agent_install_node() { if [ "$prime_agent_base_url" = "$prime_agent_unconfigured_base_url" ]; then printf 'error: installer download URL is not configured.\n' >&2 printf 'Set PRIME_AGENT_DOWNLOAD_BASE_URL or use the installer published by the release workflow.\n' >&2 exit 1 fi prime_agent_validate_download_base_url
prime_agent_install_traps prime_agent_init_screen if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Installing Prime Agent" "" "" "" else printf '\n\033[1m Installing Prime Agent\033[0m\n\033[2m npm global install\033[0m\n\n' fi
start_preflight_checks
if finish_preflight_checks; then check_status=0 else check_status=$? fi
if [ "$check_status" -ne 0 ]; then if ! install_node_npm_interactive; then exit "$check_status" fi
start_preflight_checks if finish_preflight_checks; then check_status=0 else check_status=$? fi
if [ "$check_status" -ne 0 ]; then exit "$check_status" fi fi
version="$(resolve_prime_agent_version "$@")" tarball_name="$prime_agent_package-$version.tgz" tarball_url="$prime_agent_base_url/releases/v$version/$tarball_name"
confirm_install "$version" "$tarball_url" confirm_kernel_runtime_setup
download_dir=$(create_temp_dir) prime_agent_download_dir="$download_dir" tarball_path="$download_dir/$tarball_name"
download_prime_agent_package "$version" "$tarball_url" "$tarball_path" install_prime_agent_package "$tarball_path" rm -rf "$download_dir" prime_agent_download_dir=
if [ "${PRIME_AGENT_NODE_INSTALLED_STANDALONE:-0}" = 1 ]; then prime_agent_screen "Prime Agent installed" "" "Checking your shell PATH." "" configure_standalone_node_path elif command -v "$prime_agent_cmd" >/dev/null 2>&1; then if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Prime Agent installed" "" "Run it with: $prime_agent_cmd" "" else printf '\nPrime Agent was installed successfully.\n' printf '\nRun it with: %s\n' "$prime_agent_cmd" fi else if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Prime Agent installed" "" "PATH update needed for $prime_agent_cmd." "" prime_agent_restore_terminal else printf '\nPrime Agent was installed successfully.\n' fi cat <<EOFThe $prime_agent_cmd command was installed, but it is not on your PATH yet.Check npm's global bin directory with:
npm bin -g
Then add that directory to your shell PATH.EOF fi}
create_temp_dir() { if command -v mktemp >/dev/null 2>&1; then if tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/prime-agent-install.XXXXXX" 2>/dev/null); then printf '%s' "$tmp_dir" return fi fi
printf 'error: mktemp is required to create a secure temporary directory.\n' >&2 exit 1}
prime_agent_is_loopback_test_base_url() { case "$1" in http://127.0.0.1:*) ;; *) return 1 ;; esac test_port=${1##*:} case "$test_port" in ''|*[!0-9]*) return 1 ;; esac}
prime_agent_validate_download_base_url() { case "$prime_agent_base_url" in https://*) prime_agent_allow_insecure_http=0 ;; http://*) if [ "${PRIME_AGENT_ALLOW_INSECURE_HTTP_FOR_TESTS:-0}" = 1 ] && prime_agent_is_loopback_test_base_url "$prime_agent_base_url"; then prime_agent_allow_insecure_http=1 return fi printf 'error: Prime Agent downloads require an HTTPS base URL.\n' >&2 printf 'Local loopback test feeds require PRIME_AGENT_ALLOW_INSECURE_HTTP_FOR_TESTS=1.\n' >&2 exit 1 ;; *) printf 'error: Prime Agent download base URL must use HTTPS.\n' >&2 exit 1 ;; esac}
prime_agent_curl_download() { if [ "$prime_agent_allow_insecure_http" = 1 ]; then curl --proto '=http,https' --proto-redir '=https' "$@" else curl --proto '=https' --proto-redir '=https' "$@" fi}
prime_agent_install_traps() { trap 'prime_agent_cleanup' EXIT trap 'prime_agent_signal_cleanup 130' INT trap 'prime_agent_signal_cleanup 143' TERM trap 'prime_agent_signal_cleanup 129' HUP}
prime_agent_cleanup() { status=$? if [ -n "${prime_agent_download_dir:-}" ] && [ -d "$prime_agent_download_dir" ]; then rm -rf "$prime_agent_download_dir" fi prime_agent_native_cleanup prime_agent_restore_terminal return "$status"}
prime_agent_signal_cleanup() { prime_agent_restore_terminal exit "$1"}
prime_agent_restore_terminal() { if [ "${prime_agent_screen_enabled:-0}" = 1 ]; then if ( : <>/dev/tty ) 2>/dev/null; then printf '%s%s' "$prime_agent_reset" "$prime_agent_show_cursor" >/dev/tty else printf '%s%s' "$prime_agent_reset" "$prime_agent_show_cursor" >&2 fi fi}
prime_agent_init_screen() { if [ "${PRIME_AGENT_INSTALLER_PLAIN:-0}" = 1 ]; then return fi if [ ! -t 1 ]; then return fi if [ "${TERM:-}" = dumb ]; then return fi prime_agent_screen_enabled=1}
prime_agent_read_terminal_size() { prime_agent_screen_cols=80 prime_agent_screen_rows=24
if size=$(stty size 2>/dev/null </dev/tty); then set -- $size if [ "${1:-}" ] && [ "${2:-}" ]; then case "$1" in *[!0-9]*|"") ;; *) prime_agent_screen_rows="$1" ;; esac case "$2" in *[!0-9]*|"") ;; *) prime_agent_screen_cols="$2" ;; esac fi fi
if [ "$prime_agent_screen_cols" -lt 1 ]; then prime_agent_screen_cols=80 fi if [ "$prime_agent_screen_rows" -lt 1 ]; then prime_agent_screen_rows=24 fi}
prime_agent_screen() { if [ "$prime_agent_screen_enabled" != 1 ]; then return fi
prime_agent_screen_title="${2:-$1}" if [ -z "$prime_agent_screen_title" ]; then prime_agent_screen_title="$1" fi prime_agent_screen_status= prime_agent_screen_detail="${3:-}" prime_agent_screen_question="${4:-}" prime_agent_screen_frame=$((prime_agent_screen_frame + 1)) prime_agent_read_terminal_size prime_agent_init_screen_layout prime_agent_refresh_screen_layout_mode
if [ "$prime_agent_screen_drawn" = 0 ] || [ "$prime_agent_screen_cols" -ne "$prime_agent_screen_last_cols" ] || [ "$prime_agent_screen_rows" -ne "$prime_agent_screen_last_rows" ]; then prime_agent_screen_prefix="${prime_agent_reset}${prime_agent_clear_screen}${prime_agent_hide_cursor}" prime_agent_screen_drawn=1 prime_agent_screen_last_cols="$prime_agent_screen_cols" prime_agent_screen_last_rows="$prime_agent_screen_rows" else prime_agent_screen_prefix="${prime_agent_reset}${prime_agent_home_cursor}${prime_agent_hide_cursor}" fi prime_agent_screen_frame_text=$(prime_agent_render_screen)
if ( : <>/dev/tty ) 2>/dev/null; then printf '%s%s%s%s' "$prime_agent_sync_start" "$prime_agent_screen_prefix" "$prime_agent_screen_frame_text" "$prime_agent_sync_end" >/dev/tty else printf '%s%s%s%s' "$prime_agent_sync_start" "$prime_agent_screen_prefix" "$prime_agent_screen_frame_text" "$prime_agent_sync_end" >&2 fi}
prime_agent_init_screen_layout() { if [ "$prime_agent_screen_layout_ready" = 1 ]; then return fi
prime_agent_screen_layout_ready=1 prime_agent_screen_layout_show_logo=0 prime_agent_screen_layout_lab_width=0 prime_agent_screen_render_lab_width=0 if prime_agent_terminal_size_supports_logo; then prime_agent_screen_layout_show_logo=1 prime_agent_screen_layout_lab_width=$(prime_agent_lab_width_for_cols "$prime_agent_screen_cols") fi}
prime_agent_refresh_screen_layout_mode() { prime_agent_screen_compact=0 prime_agent_screen_render_lab_width=0 if [ "$prime_agent_screen_layout_show_logo" != 1 ]; then return fi if [ "$prime_agent_screen_rows" -lt 17 ]; then prime_agent_screen_compact=1 return fi
max_safe_width=$((prime_agent_screen_cols - 1)) if [ "$max_safe_width" -lt 32 ]; then prime_agent_screen_compact=1 return fi
prime_agent_screen_render_lab_width="$prime_agent_screen_layout_lab_width" if [ "$prime_agent_screen_render_lab_width" -gt "$max_safe_width" ]; then prime_agent_screen_render_lab_width="$max_safe_width" fi}
prime_agent_terminal_size_supports_logo() { [ "$prime_agent_screen_rows" -ge 22 ] && [ "$prime_agent_screen_cols" -ge 42 ]}
prime_agent_lab_width_for_cols() { cols="$1" width=$((cols - 6)) if [ "$width" -gt 78 ]; then width=78 fi if [ "$width" -lt 42 ]; then width=42 fi max_safe_width=$((cols - 1)) if [ "$max_safe_width" -lt 1 ]; then max_safe_width=1 fi if [ "$width" -gt "$max_safe_width" ]; then width="$max_safe_width" fi if [ "$width" -lt 32 ]; then width=32 fi printf '%s' "$width"}
prime_agent_render_screen() { content_height=$(prime_agent_content_height) top=$(((prime_agent_screen_rows - content_height) / 2)) if [ "$top" -lt 0 ]; then top=0 fi
y=0 while [ "$y" -lt "$prime_agent_screen_rows" ]; do content_index=$((y - top)) prime_agent_content_line "$content_index" if [ "${prime_agent_content_is_set:-0}" = 1 ]; then prime_agent_print_centered_line "$prime_agent_content_text" "$prime_agent_content_width" "$prime_agent_content_style" else prime_agent_print_centered_line "" 0 "" fi y=$((y + 1)) done}
prime_agent_content_height() { height=2 if prime_agent_show_logo; then height=$((height + 15)) fi printf '%s' "$height"}
prime_agent_show_logo() { [ "$prime_agent_screen_layout_show_logo" = 1 ] && [ "$prime_agent_screen_compact" != 1 ] && [ "$prime_agent_screen_render_lab_width" -ge 32 ]}
prime_agent_content_line() { index="$1" prime_agent_content_is_set=0 prime_agent_content_text= prime_agent_content_width=0 prime_agent_content_style=
if prime_agent_show_logo; then case "$index" in 0|1|2|3|4|5|6|7|8|9|10|11|12|13) prime_agent_set_lab_line "$index" ;; 14) prime_agent_set_blank_line ;; esac if [ "$prime_agent_content_is_set" = 1 ]; then return fi index=$((index - 15)) fi
if [ "$index" -lt 0 ]; then return fi
if [ "$index" -eq 0 ]; then if [ -n "$prime_agent_screen_question" ]; then prime_agent_set_text_line "$(prime_agent_screen_primary_text)" "$prime_agent_bold$prime_agent_color_text" else prime_agent_set_title_line "$prime_agent_screen_title" fi return fi
if [ "$index" -eq 1 ]; then if [ -n "$prime_agent_screen_question" ]; then prime_agent_set_text_line "Press Enter to continue; type n to cancel." "$prime_agent_color_muted" elif [ -n "$prime_agent_screen_detail" ]; then prime_agent_set_text_line "$prime_agent_screen_detail" "$prime_agent_color_muted" else prime_agent_set_blank_line fi return fi}
prime_agent_screen_primary_text() { if [ -z "$prime_agent_screen_question" ]; then printf '%s' "$prime_agent_screen_title" return fi
case "$prime_agent_screen_question" in *'[Y/n]'*) printf '%s [Y/n] >' "$prime_agent_screen_title" ;; *) printf '%s %s' "$prime_agent_screen_title" "$prime_agent_screen_question" ;; esac}
prime_agent_set_lab_line() { lab_row="$1" prime_agent_lab_width="$prime_agent_screen_render_lab_width"
logo_line=$(prime_agent_logo_line "$lab_row") if [ -n "$logo_line" ]; then logo_start=$(((prime_agent_lab_width - 32) / 2)) logo_end=$((logo_start + 32)) left=$(prime_agent_lab_background_range "$lab_row" 0 "$logo_start") right=$(prime_agent_lab_background_range "$lab_row" "$logo_end" "$prime_agent_lab_width") trace="${left}${prime_agent_color_text}${logo_line}${prime_agent_reset}${right}" else trace=$(prime_agent_lab_background_range "$lab_row" 0 "$prime_agent_lab_width") fi
prime_agent_content_is_set=1 prime_agent_content_text="$trace" prime_agent_content_width="$prime_agent_lab_width" prime_agent_content_style=}
prime_agent_logo_line() { case "$1" in 2) printf ' ▄▄███▀' ;; 3) printf ' ▄▄▄▄▄ ▄█████▀' ;; 4) printf ' ██████▄ ▄██████▀' ;; 5) printf ' ▄███▀███▄ ▄███▀▄██▀' ;; 6) printf ' ███ ▄████▄▄▄████▀▄▄██' ;; 7) printf ' ▀██ ▀█████████▀▀▀▀▀▀' ;; 8) printf ' ▄██ ██████▀▀ ▄███' ;; 9) printf ' █████ ▀█▄▄▄█████▀' ;; 10) printf '███████▄ ████████▀' ;; 11) printf '▀███▀▀ █████▀' ;; esac}
prime_agent_lab_background_range() { lab_row="$1" range_start="$2" range_end="$3" active_style= line= x="$range_start" while [ "$x" -lt "$range_end" ]; do prime_agent_lab_cell "$x" "$lab_row" if [ "$prime_agent_lab_cell_style" != "$active_style" ]; then if [ -n "$active_style" ]; then line="${line}${prime_agent_reset}" fi if [ -n "$prime_agent_lab_cell_style" ]; then line="${line}${prime_agent_lab_cell_style}" fi active_style="$prime_agent_lab_cell_style" fi line="${line}${prime_agent_lab_cell_char}" x=$((x + 1)) done if [ -n "$active_style" ]; then line="${line}${prime_agent_reset}" fi printf '%s' "$line"}
prime_agent_lab_cell() { x="$1" y="$2" width="$prime_agent_lab_width" height=14 frame="$prime_agent_screen_frame" prime_agent_lab_cell_char=" " prime_agent_lab_cell_style=
hash=$(((x * 37 + y * 53 + frame * 11 + x * y * 3) % 101)) if [ "$hash" -lt 3 ]; then prime_agent_lab_cell_char="·" prime_agent_lab_cell_style="$prime_agent_color_dim" fi
center_x=$((width * 36 / 100)) center_y=$((height * 54 / 100)) dx=$((x - center_x)) dy=$((y - center_y)) if [ "$dx" -lt 0 ]; then dx=$((-dx)) fi if [ "$dy" -lt 0 ]; then dy=$((-dy)) fi contour=$((dx + dy * 4 + x / 6 - frame)) if [ "$x" -lt $((width * 82 / 100)) ] && [ $(((contour % 24 + 24) % 24)) -eq 12 ]; then if [ $(((x + y) % 5)) -eq 0 ]; then prime_agent_lab_cell_char="╌" else prime_agent_lab_cell_char="·" fi prime_agent_lab_cell_style="$prime_agent_color_dim" fi
horizon_y=$((height * 58 / 100)) if [ "$y" -eq "$horizon_y" ] && [ $((x % 2)) -eq 0 ] && [ $(((x + frame) % 13)) -lt 2 ]; then prime_agent_lab_cell_char="─" if [ "$x" -gt $((width * 60 / 100)) ]; then prime_agent_lab_cell_style="$prime_agent_color_primary" else prime_agent_lab_cell_style="$prime_agent_color_dim" fi fi
scan_start=$((width / 2)) if [ "$x" -ge "$scan_start" ]; then scan_offset=$((x - scan_start)) if [ $((scan_offset % 5)) -eq 0 ]; then scan_index=$((scan_offset / 5)) scan_top=$((1 + (scan_index + frame / 3) % 3)) scan_bottom=$((height - 2 - (scan_index * 2 + frame / 4) % 3)) if [ "$y" -ge "$scan_top" ] && [ "$y" -le "$scan_bottom" ] && [ $(((y + scan_index + frame) % 6)) -ne 0 ]; then if [ $(((scan_index + y) % 4)) -eq 0 ]; then prime_agent_lab_cell_char="┃" else prime_agent_lab_cell_char="╎" fi prime_agent_lab_cell_style="$prime_agent_color_scan" fi fi fi
trace_index=0 while [ "$trace_index" -lt 3 ]; do case "$trace_index" in 0) base=$((height * 30 / 100)) ;; 1) base=$((height * 49 / 100)) ;; *) base=$((height * 72 / 100)) ;; esac wave=$(((x * 2 + frame + trace_index * 7) % 16)) if [ "$wave" -gt 7 ]; then wave=$((15 - wave)) fi trace_y=$((base + (wave - 3) / 2)) if [ "$y" -eq "$trace_y" ]; then if [ $(((x + frame + trace_index * 13) % 41)) -eq 0 ]; then prime_agent_lab_cell_char="◆" prime_agent_lab_cell_style="$prime_agent_color_warning" elif [ $(((x + frame) % 12)) -eq 0 ]; then prime_agent_lab_cell_char="•" prime_agent_lab_cell_style="$prime_agent_color_primary" else prime_agent_lab_cell_char="·" prime_agent_lab_cell_style="$prime_agent_color_primary" fi fi trace_index=$((trace_index + 1)) done}
prime_agent_set_blank_line() { prime_agent_content_is_set=1 prime_agent_content_text= prime_agent_content_width=0 prime_agent_content_style=}
prime_agent_set_text_line() { max_width=$((prime_agent_screen_cols - 4)) if [ "$max_width" -lt 1 ]; then max_width=1 fi prime_agent_content_text=$(prime_agent_fit_ascii "$1" "$max_width") prime_agent_content_width=${#prime_agent_content_text} prime_agent_content_style="$2" prime_agent_content_is_set=1}
prime_agent_set_title_line() { max_width=$((prime_agent_screen_cols - 4)) if [ "$max_width" -lt 1 ]; then max_width=1 fi prime_agent_content_text=$(prime_agent_fit_ascii "$1" "$max_width") prime_agent_content_width=${#prime_agent_content_text} case "$prime_agent_content_text" in *"Prime Agent"*) prime_agent_content_text=$(prime_agent_style_prime_agent_title "$prime_agent_content_text") prime_agent_content_style= ;; *) prime_agent_content_style="$prime_agent_bold$prime_agent_color_primary" ;; esac prime_agent_content_is_set=1}
prime_agent_style_prime_agent_title() { text="$1" styled= while :; do case "$text" in *"Prime Agent"*) before=${text%%Prime Agent*} rest=${text#*Prime Agent} styled="${styled}${prime_agent_bold}${prime_agent_color_primary}${before}" styled="${styled}${prime_agent_bold}${prime_agent_color_primary}PRIME Agent${prime_agent_reset}" text="$rest" ;; *) styled="${styled}${prime_agent_bold}${prime_agent_color_primary}${text}${prime_agent_reset}" printf '%s' "$styled" return ;; esac done}
prime_agent_fit_ascii() { text="$1" max_width="$2" if [ "${#text}" -le "$max_width" ]; then printf '%s' "$text" return fi if [ "$max_width" -le 3 ]; then printf '%s' "$text" | cut -c 1-"$max_width" return fi cut_width=$((max_width - 3)) printf '%s...' "$(printf '%s' "$text" | cut -c 1-"$cut_width")"}
prime_agent_print_centered_line() { text="$1" width="$2" style="$3" left=$(((prime_agent_screen_cols - width) / 2)) if [ "$left" -lt 0 ]; then left=0 fi if [ -n "$style" ]; then printf '%*s%s%s%s%s\n' "$left" "" "$style" "$text" "$prime_agent_reset" "$prime_agent_clear_line" else printf '%*s%s%s\n' "$left" "" "$text" "$prime_agent_clear_line" fi}
prime_agent_place_prompt_cursor() { max_width=$((prime_agent_screen_cols - 4)) if [ "$max_width" -lt 1 ]; then max_width=1 fi prompt_text=$(prime_agent_fit_ascii "$(prime_agent_screen_primary_text)" "$max_width") prompt_width=${#prompt_text} content_height=$(prime_agent_content_height) top=$(((prime_agent_screen_rows - content_height) / 2)) if [ "$top" -lt 0 ]; then top=0 fi prompt_index=0 if prime_agent_show_logo; then prompt_index=$((prompt_index + 15)) fi row=$((top + prompt_index + 1)) col=$(((prime_agent_screen_cols - prompt_width) / 2 + prompt_width + 2)) if [ "$col" -lt 1 ]; then col=1 fi if [ "$col" -gt "$prime_agent_screen_cols" ]; then col="$prime_agent_screen_cols" fi if ( : <>/dev/tty ) 2>/dev/null; then printf '%s%s%s[%s;%sH' "$prime_agent_reset" "$prime_agent_show_cursor" "$prime_agent_esc" "$row" "$col" >/dev/tty else printf '%s%s%s[%s;%sH' "$prime_agent_reset" "$prime_agent_show_cursor" "$prime_agent_esc" "$row" "$col" >&2 fi}
prime_agent_pulse() { case $((prime_agent_screen_frame % 4)) in 0) printf '.' ;; 1) printf '..' ;; 2) printf '...' ;; *) printf '' ;; esac}
prime_agent_animation_detail_count() { details="$1" case "$details" in *''*) printf '%s\n' "$details" | wc -l | tr -d ' ' ;; *) printf '1' ;; esac}
prime_agent_animation_current_frame() { frame="${prime_agent_animation_frame:-1}" case "$frame" in ""|*[!0-9]*) frame=1 ;; esac if [ "$frame" -lt 1 ]; then frame=1 fi printf '%s' "$frame"}
prime_agent_animation_step_index() { details="$1" detail_count=$(prime_agent_animation_detail_count "$details") frame=$(prime_agent_animation_current_frame) detail_index=$(((frame - 1) / 24 + 1)) if [ "$detail_index" -gt "$detail_count" ]; then detail_index="$detail_count" fi printf '%s' "$detail_index"}
prime_agent_static_progress_title() { case "$1" in *...) printf '%s' "$1" ;; *) printf '%s...' "$1" ;; esac}
prime_agent_animation_status() { status="$1" details="$2" status_mode="$3" case "$status_mode" in static) prime_agent_static_progress_title "$status" ;; *) printf '%s%s' "$status" "$(prime_agent_pulse)" ;; esac}
prime_agent_animation_detail() { details="$1" case "$details" in *''*) detail_index=$(prime_agent_animation_step_index "$details") printf '%s\n' "$details" | sed -n "${detail_index}p" ;; *) printf '%s' "$details" ;; esac}
prime_agent_run_quiet_with_animation() { title="$1" status="$2" detail="$3" shift 3
prime_agent_run_quiet_with_animation_command "$title" "$status" "$detail" pulse "$@"}
prime_agent_run_quiet_with_animation_steps() { title="$1" status="$2" details="$3" shift 3
prime_agent_run_quiet_with_animation_command "$title" "$status" "$details" static "$@"}
prime_agent_run_quiet_with_animation_command() { title="$1" status="$2" details="$3" status_mode="$4" shift 4
if [ "$prime_agent_screen_enabled" != 1 ]; then printf '%s\n' "$status" >&2 "$@" return fi
output_dir=$(create_temp_dir) output_file="$output_dir/output" "$@" >"$output_file" 2>&1 & command_pid=$! prime_agent_animation_frame=0
while kill -0 "$command_pid" 2>/dev/null; do prime_agent_animation_frame=$((prime_agent_animation_frame + 1)) status_display=$(prime_agent_animation_status "$status" "$details" "$status_mode") prime_agent_screen "$title" "$status_display" "$(prime_agent_animation_detail "$details")" "" sleep 0.18 done
if wait "$command_pid"; then command_status=0 else command_status=$? fi
if [ "$command_status" -ne 0 ] && [ -s "$output_file" ]; then prime_agent_restore_terminal printf '\n' >&2 cat "$output_file" >&2 fi rm -rf "$output_dir" return "$command_status"}
prime_agent_prompt_yes_no() { question="$1" detail="$2" input_prompt="$3"
if ( : <>/dev/tty ) 2>/dev/null; then prompt_input=tty exec 3<>/dev/tty elif [ -t 0 ]; then prompt_input=stdin else return 2 fi
if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "$question" "" "$detail" "$input_prompt" prime_agent_place_prompt_cursor "$input_prompt" else printf '%s\n' "$detail" if [ "$prompt_input" = tty ]; then printf '%s ' "$input_prompt" >&3 else printf '%s ' "$input_prompt" >&2 fi fi
if [ "$prompt_input" = tty ]; then if ! IFS= read -r answer <&3; then answer= fi exec 3>&- else if ! IFS= read -r answer; then answer= fi fi
case "$answer" in n|N|no|NO) return 1 ;; esac return 0}
start_preflight_checks() { preflight_dir=$(create_temp_dir) preflight_file="$preflight_dir/preflight" run_preflight_checks >"$preflight_file" & preflight_pid=$!}
finish_preflight_checks() { if [ "$prime_agent_screen_enabled" = 1 ]; then while kill -0 "$preflight_pid" 2>/dev/null; do prime_agent_screen "Checking Node.js and npm$(prime_agent_pulse)" "" "" "" sleep 0.18 done fi
if wait "$preflight_pid"; then preflight_status=0 else preflight_status=$? fi
if [ "$prime_agent_screen_enabled" = 1 ]; then if [ "$preflight_status" -ne 0 ]; then preflight_summary=$(sed -n '1p' "$preflight_file") prime_agent_screen "Node.js 20.6.0 or newer is required" "" "$preflight_summary" "" sleep 0.4 elif [ -s "$preflight_file" ]; then preflight_summary="Existing $prime_agent_cmd command found on PATH." prime_agent_screen "Environment ready" "" "$preflight_summary" "" sleep 0.4 fi else cat "$preflight_file" fi rm -rf "$preflight_dir" return "$preflight_status"}
run_preflight_checks() { status=0 yellow="${prime_agent_esc}[33m" reset="${prime_agent_esc}[0m"
if command -v node >/dev/null 2>&1; then node_version=$(node --version) if ! node -e 'const [major, minor, patch] = process.versions.node.split(".").map(Number); process.exit(major > 20 || (major === 20 && (minor > 6 || (minor === 6 && patch >= 0))) ? 0 : 1)' >/dev/null; then printf 'error: Prime Agent requires Node.js 20.6.0 or newer. Found %s.\n' "$node_version" status=1 fi else printf 'error: Node.js 20.6.0 or newer is required to install Prime Agent.\n' status=1 fi
if ! command -v npm >/dev/null 2>&1; then printf 'error: npm is required to install Prime Agent.\n' status=1 fi
if [ "$status" -ne 0 ]; then printf '\n' fi
if prime_agent_path=$(command -v "$prime_agent_cmd" 2>/dev/null); then printf '%sExisting %s found at: %s%s\n' "$yellow" "$prime_agent_cmd" "$prime_agent_path" "$reset" printf '\n' fi
return "$status"}
resolve_prime_agent_version() { if [ "${1:-}" ]; then case "$1" in stable|beta) release_channel="$1" ;; *) normalize_version "$1" return ;; esac else release_channel="$prime_agent_release_channel" fi
if [ "${PRIME_AGENT_VERSION:-}" ]; then normalize_version "$PRIME_AGENT_VERSION" return fi
if ! command -v curl >/dev/null 2>&1; then printf 'error: curl is required to resolve the latest Prime Agent version.\n' >&2 exit 1 fi
case "$release_channel" in stable|beta) ;; *) printf 'error: invalid Prime Agent release channel: %s\n' "$release_channel" >&2 exit 1 ;; esac
channel_dir=$(create_temp_dir) channel_path="$channel_dir/$release_channel" if ! prime_agent_run_quiet_with_animation \ "Resolving latest release" \ "Resolving latest release" \ "Checking the $release_channel release channel." \ prime_agent_curl_download -fsSL "$prime_agent_base_url/$release_channel" -o "$channel_path"; then rm -rf "$channel_dir" printf 'error: could not resolve latest Prime Agent version from %s/%s\n' "$prime_agent_base_url" "$release_channel" >&2 exit 1 fi channel_version="$(tr -d '[:space:]' <"$channel_path")" rm -rf "$channel_dir" if [ -z "$channel_version" ]; then printf 'error: could not resolve latest Prime Agent version from %s/%s\n' "$prime_agent_base_url" "$release_channel" >&2 exit 1 fi normalize_version "$channel_version"}
normalize_version() { version="${1#v}" case "$version" in "") printf 'error: empty Prime Agent version.\n' >&2 exit 1 ;; *[!0-9A-Za-z.-]*) printf 'error: invalid Prime Agent version: %s\n' "$1" >&2 exit 1 ;; esac printf '%s' "$version"}
install_node_npm_interactive() { method=$(detect_node_install_method) case "$method" in homebrew) label="Homebrew" ;; apt) label="apt" ;; apk) label="apk" ;; standalone) label="standalone Node.js" ;; *) method=standalone label="standalone Node.js" ;; esac
if prime_agent_prompt_yes_no \ "Install Node.js and npm with $label?" \ "Required before Prime Agent can be installed." \ "Install? [Y/n]"; then install_node_npm "$method" "$label" return else prompt_status=$? fi if [ "$prompt_status" -eq 2 ]; then printf 'No terminal detected; install Node.js 20.6.0 or newer and npm, then run this installer again.\n' else printf '\nInstall Node.js 20.6.0 or newer and npm, then run this installer again.\n' fi return 1}
detect_node_install_method() { case "$(uname -s)" in Darwin) if command -v brew >/dev/null 2>&1; then printf 'homebrew' else printf 'standalone' fi ;; Linux) if command -v apt-cache >/dev/null 2>&1 && command -v apt-get >/dev/null 2>&1 && apt_node_candidate_is_new_enough; then printf 'apt' elif command -v apk >/dev/null 2>&1 && apk_node_candidate_is_new_enough; then printf 'apk' else printf 'standalone' fi ;; *) printf 'standalone' ;; esac}
apt_node_candidate_is_new_enough() { version=$(apt-cache policy nodejs 2>/dev/null | awk '/Candidate:/ { print $2; exit }') [ -n "$version" ] && [ "$version" != "(none)" ] && node_version_string_is_new_enough "$version"}
apk_node_candidate_is_new_enough() { version=$(apk search -x nodejs 2>/dev/null | awk -F- '/^nodejs-/ { print $2; exit }') [ -n "$version" ] && node_version_string_is_new_enough "$version"}
node_version_string_is_new_enough() { version="${1#v}" case "$version" in [0-9]*) ;; *) return 1 ;; esac version="${version%%[!0-9.]*}" version_ifs=${IFS- } IFS=. set -- $version IFS=$version_ifs major="${1:-}" minor="${2:-0}" patch="${3:-0}" case "$major" in ''|*[!0-9]*) return 1 ;; esac case "$minor" in ''|*[!0-9]*) minor=0 ;; esac case "$patch" in ''|*[!0-9]*) patch=0 ;; esac
[ "$major" -gt 20 ] && return 0 [ "$major" -eq 20 ] && [ "$minor" -gt 6 ] && return 0 [ "$major" -eq 20 ] && [ "$minor" -eq 6 ] && [ "$patch" -ge 0 ] && return 0 return 1}
install_node_npm() { method="$1" label="$2"
if [ "$prime_agent_screen_enabled" != 1 ]; then printf '\nInstalling Node.js and npm with %s...\n\n' "$label" run_node_install_method "$method" else prepare_sudo_for_node_install "$method" node_install_details="Using $label.Resolving Node.js packages.Downloading Node.js runtime.Installing npm.Preparing Prime Agent setup." prime_agent_run_quiet_with_animation_steps \ "Installing Node.js and npm" \ "Installing Node.js and npm" \ "$node_install_details" \ run_node_install_method "$method" fi
if [ "$method" = standalone ]; then load_standalone_node PRIME_AGENT_NODE_INSTALLED_STANDALONE=1 fi hash -r if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Node.js and npm installed" "" "Continuing Prime Agent setup." "" else printf '\nNode.js and npm are installed.\n\n' fi}
node_install_needs_sudo() { if [ "${EUID:-$(id -u)}" -eq 0 ]; then return 1 fi
case "$1" in apt|apk) return 0 ;; standalone) [ "$(uname -s)" = Linux ] || return 1 command -v xz >/dev/null 2>&1 && return 1 command -v apt-get >/dev/null 2>&1 || command -v apk >/dev/null 2>&1 ;; *) return 1 ;; esac}
prepare_sudo_for_node_install() { method="$1" if ! node_install_needs_sudo "$method"; then return 0 fi
prime_agent_screen "Preparing Node.js install" "" "This may ask for your sudo password." "" prime_agent_restore_terminal printf '\n' sudo -v}
run_node_install_method() { case "$1" in homebrew) install_node_with_homebrew ;; apt) install_node_with_apt ;; apk) install_node_with_apk ;; standalone) install_node_standalone ;; esac}
install_node_with_homebrew() { if brew list node >/dev/null 2>&1; then brew upgrade node else brew install node fi}
install_node_with_apt() { print_sudo_note if [ "${EUID:-$(id -u)}" -eq 0 ]; then apt-get update apt-get install -y nodejs npm else sudo sh -c 'apt-get update && apt-get install -y nodejs npm' fi}
install_node_with_apk() { print_sudo_note run_with_sudo apk add --update-cache nodejs npm}
install_node_standalone() { node_platform=$(detect_node_binary_platform) || { printf 'Unsupported operating system for automatic Node.js install: %s\n' "$(uname -s)" return 1 } node_arch=$(detect_node_binary_arch) || { printf 'Unsupported CPU architecture for automatic Node.js install: %s\n' "$(uname -m)" return 1 } node_dist_base="https://nodejs.org/dist/latest-v22.x" node_base_dir=$(node_standalone_base_dir) node_tmp_dir=$(create_temp_dir)
mkdir -p "$node_tmp_dir" "$node_base_dir"
printf 'Resolving Node.js binary for %s-%s\n' "$node_platform" "$node_arch" prime_agent_curl_download -fsSL "$node_dist_base/SHASUMS256.txt" -o "$node_tmp_dir/SHASUMS256.txt" node_file=$(awk -v suffix="-$node_platform-$node_arch.tar.xz" ' index($2, "node-v") == 1 && length($2) >= length(suffix) && substr($2, length($2) - length(suffix) + 1) == suffix { print $2; exit } ' "$node_tmp_dir/SHASUMS256.txt") if [ -z "$node_file" ]; then printf 'No Node.js binary is available for %s-%s.\n' "$node_platform" "$node_arch" rm -rf "$node_tmp_dir" return 1 fi case "$node_file" in */*|*\\*|*..*) printf 'Unsafe Node.js archive name in checksum manifest: %s\n' "$node_file" rm -rf "$node_tmp_dir" return 1 ;; node-v*-"$node_platform"-"$node_arch".tar.xz) ;; *) printf 'Unexpected Node.js archive name in checksum manifest: %s\n' "$node_file" rm -rf "$node_tmp_dir" return 1 ;; esac
printf 'Downloading Node.js %s\n' "${node_file%.tar.xz}" prime_agent_curl_download -fsSL "$node_dist_base/$node_file" -o "$node_tmp_dir/$node_file" verify_node_standalone_download "$node_tmp_dir" "$node_file" ensure_node_standalone_extract_tools "$node_platform"
node_dir="$node_base_dir/${node_file%.tar.xz}" rm -rf "$node_dir" printf 'Extracting Node.js to %s\n' "$node_dir" tar -xf "$node_tmp_dir/$node_file" -C "$node_base_dir" rm -f "$node_base_dir/current" ln -s "$node_dir" "$node_base_dir/current" rm -rf "$node_tmp_dir" printf 'Node.js installed at %s\n' "$node_dir"}
verify_node_standalone_download() { checksum_dir="$1" checksum_file_name="$2" awk -v file="$checksum_file_name" '$2 == file { print }' "$checksum_dir/SHASUMS256.txt" >"$checksum_dir/SHASUMS256.selected"
if command -v sha256sum >/dev/null 2>&1; then printf 'Verifying Node.js download\n' (cd "$checksum_dir" && sha256sum -c SHASUMS256.selected) elif command -v shasum >/dev/null 2>&1; then printf 'Verifying Node.js download\n' (cd "$checksum_dir" && shasum -a 256 -c SHASUMS256.selected) else printf 'error: sha256sum or shasum is required to verify the Node.js download.\n' return 1 fi}
ensure_node_standalone_extract_tools() { extract_platform="$1"
if [ "$extract_platform" = linux ] && ! command -v xz >/dev/null 2>&1; then printf 'Installing xz-utils for Node.js archive extraction\n' print_sudo_note if command -v apt-get >/dev/null 2>&1; then run_with_sudo apt-get update run_with_sudo apt-get install -y xz-utils elif command -v apk >/dev/null 2>&1; then run_with_sudo apk add --update-cache xz else printf 'xz is required to extract Node.js. Install xz and run this installer again.\n' return 1 fi fi}
load_standalone_node() { PRIME_AGENT_STANDALONE_NODE_BIN="$(node_standalone_base_dir)/current/bin" PATH="$PRIME_AGENT_STANDALONE_NODE_BIN:$PATH" export PRIME_AGENT_STANDALONE_NODE_BIN PATH}
node_standalone_base_dir() { if [ -n "${XDG_DATA_HOME:-}" ]; then printf '%s/prime-agent-node' "$XDG_DATA_HOME" else printf '%s/.local/share/prime-agent-node' "$HOME" fi}
detect_node_binary_platform() { case "$(uname -s)" in Darwin) printf 'darwin' ;; Linux) printf 'linux' ;; *) return 1 ;; esac}
detect_node_binary_arch() { case "$(uname -m)" in x86_64|amd64) printf 'x64' ;; arm64|aarch64) printf 'arm64' ;; armv7l) printf 'armv7l' ;; ppc64le) printf 'ppc64le' ;; s390x) printf 's390x' ;; *) return 1 ;; esac}
print_sudo_note() { if [ "${EUID:-$(id -u)}" -ne 0 ]; then printf 'This may ask for your sudo password.\n\n' fi}
run_with_sudo() { if [ "${EUID:-$(id -u)}" -eq 0 ]; then "$@" else sudo "$@" fi}
configure_standalone_node_path() { if original_prime_agent_path=$(resolve_prime_agent_with_original_path); then case "$original_prime_agent_path" in "$PRIME_AGENT_STANDALONE_NODE_BIN/"*) if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Prime Agent installed" "" "Run it with: $prime_agent_cmd" "" else printf '\nRun it with: %s\n' "$prime_agent_cmd" fi return 0 ;; esac if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Prime Agent installed" "" "PATH update needed for $prime_agent_cmd." "" else printf '%s was installed, but your shell is not using that install yet.\n' "$prime_agent_cmd" printf 'Your shell currently resolves %s to: %s\n' "$prime_agent_cmd" "$original_prime_agent_path" fi else if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Prime Agent installed" "" "PATH update needed for $prime_agent_cmd." "" else printf '%s was installed, but your shell is not using that install yet.\n' "$prime_agent_cmd" fi fi
profile=$(detect_shell_profile) || { if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_restore_terminal printf '\n' fi print_standalone_path_manual_instructions return 0 }
if shell_profile_has_standalone_node_path "$profile"; then if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Prime Agent installed" "" "Run: $(prime_agent_source_profile_command "$profile")" "" else printf '%s already contains %s.\n' "$profile" "$PRIME_AGENT_STANDALONE_NODE_BIN" printf 'Restart your shell or run: %s\n' "$(prime_agent_source_profile_command "$profile")" fi return 0 fi
prompt_add_standalone_node_path "$profile"}
resolve_prime_agent_with_original_path() { saved_path=$PATH PATH=$prime_agent_original_path if command -v "$prime_agent_cmd" 2>/dev/null; then status=0 else status=$? fi PATH=$saved_path return "$status"}
detect_shell_profile() { if [ -n "${PRIME_AGENT_SHELL_PROFILE:-}" ]; then printf '%s' "$PRIME_AGENT_SHELL_PROFILE" return 0 fi if [ -z "${HOME:-}" ]; then return 1 fi
shell_name="${SHELL:-}" shell_name="${shell_name##*/}" case "$shell_name" in zsh) printf '%s/.zshrc' "${ZDOTDIR:-$HOME}" ;; bash) printf '%s/.bashrc' "$HOME" ;; *) if [ -f "$HOME/.zshrc" ]; then printf '%s/.zshrc' "$HOME" elif [ -f "$HOME/.bashrc" ]; then printf '%s/.bashrc' "$HOME" else printf '%s/.profile' "$HOME" fi ;; esac}
shell_profile_has_standalone_node_path() { profile="$1" [ -f "$profile" ] && grep -F "$PRIME_AGENT_STANDALONE_NODE_BIN" "$profile" >/dev/null 2>&1}
prompt_add_standalone_node_path() { profile="$1" path_line=$(standalone_node_path_line)
if ! prime_agent_prompt_yes_no \ "Add standalone Node.js to your PATH?" \ "Updates $profile so future shells can run $prime_agent_cmd." \ "Update PATH? [Y/n]"; then if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_restore_terminal printf '\n' fi print_standalone_path_manual_instructions return 0 fi
mkdir -p "$(dirname "$profile")" { printf '\n# Prime Agent standalone Node.js\n' printf '%s\n' "$path_line" } >>"$profile" if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Prime Agent installed" "" "Run: $(prime_agent_source_profile_command "$profile")" "" else printf 'Added %s to %s.\n' "$PRIME_AGENT_STANDALONE_NODE_BIN" "$profile" printf 'Restart your shell or run: %s\n' "$(prime_agent_source_profile_command "$profile")" fi}
print_standalone_path_manual_instructions() { printf 'Add this to your shell profile to use %s from new shells:\n\n' "$prime_agent_cmd" printf ' %s\n' "$(standalone_node_path_line)" printf '\nThen restart your shell and run: %s\n' "$prime_agent_cmd"}
standalone_node_path_line() { printf 'export PATH="%s:$PATH"' "$PRIME_AGENT_STANDALONE_NODE_BIN"}
prime_agent_shell_quote() { quoted=$(printf '%s' "$1" | sed "s/'/'\\\\''/g") printf "'%s'" "$quoted"}
prime_agent_source_profile_command() { printf '. %s && %s' "$(prime_agent_shell_quote "$1")" "$prime_agent_cmd"}
download_prime_agent_package() { version="$1" tarball_url="$2" tarball_path="$3" download_dir=$(dirname "$tarball_path") tarball_name=$(basename "$tarball_path") checksums_url="$prime_agent_base_url/releases/v$version/SHA256SUMS" checksums_path="$download_dir/SHA256SUMS"
if ! command -v curl >/dev/null 2>&1; then printf 'error: curl is required to download Prime Agent.\n' >&2 exit 1 fi
prime_agent_run_quiet_with_animation \ "Downloading checksums" \ "Downloading release checksums" \ "Prime Agent v$version" \ prime_agent_curl_download -fsSL "$checksums_url" -o "$checksums_path"
prime_agent_run_quiet_with_animation \ "Downloading Prime Agent" \ "Downloading Prime Agent v$version" \ "Fetching the checksummed package." \ prime_agent_curl_download -fsSL "$tarball_url" -o "$tarball_path"
verify_prime_agent_package_checksum "$checksums_path" "$tarball_path"}
verify_prime_agent_package_checksum() { checksums_path="$1" tarball_path="$2" checksum_dir=$(dirname "$tarball_path") tarball_name=$(basename "$tarball_path") selected_checksums_path="$checksum_dir/SHA256SUMS.selected"
if ! awk -v file="$tarball_name" '$2 == file { print; found = 1; exit } END { if (!found) exit 1 }' \ "$checksums_path" >"$selected_checksums_path"; then printf 'error: checksum for %s was not found in %s\n' "$tarball_name" "$checksums_path" >&2 exit 1 fi
if command -v sha256sum >/dev/null 2>&1; then prime_agent_run_quiet_with_animation \ "Verifying download" \ "Verifying Prime Agent download" \ "Checking SHA-256." \ prime_agent_run_checksum_check "$checksum_dir" "$(basename "$selected_checksums_path")" sha256sum elif command -v shasum >/dev/null 2>&1; then prime_agent_run_quiet_with_animation \ "Verifying download" \ "Verifying Prime Agent download" \ "Checking SHA-256." \ prime_agent_run_checksum_check "$checksum_dir" "$(basename "$selected_checksums_path")" shasum else printf 'error: sha256sum or shasum is required to verify the Prime Agent download.\n' >&2 exit 1 fi}
prime_agent_run_checksum_check() { checksum_dir="$1" selected_checksums_name="$2" checker="$3" case "$checker" in sha256sum) (cd "$checksum_dir" && sha256sum -c "$selected_checksums_name") ;; shasum) (cd "$checksum_dir" && shasum -a 256 -c "$selected_checksums_name") ;; esac}
confirm_install() { version="$1" tarball_url="$2"
if prime_agent_prompt_yes_no \ "Install Prime Agent v$version globally with npm?" \ "Downloads the checksummed release and runs npm install -g." \ "Install? [Y/n]"; then return 0 else prompt_status=$? fi
if [ "$prompt_status" -eq 2 ]; then printf 'This will download, verify, and install:\n\n %s\n\n' "$tarball_url" printf 'No terminal detected; continuing without confirmation.\n' return 0 fi
if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Installation cancelled" "" "No changes were made." "" exit 0 fi printf '\nInstallation cancelled.\n' exit 0}
confirm_kernel_runtime_setup() { case "${PRIME_AGENT_BOOTSTRAP_KERNEL_ON_INSTALL:-}" in 1) prime_agent_bootstrap_kernel_on_install=1 return ;; 0) prime_agent_bootstrap_kernel_on_install=0 return ;; esac
if prime_agent_prompt_yes_no \ "Prepare Python runtime now?" \ "Installs uv, Python 3.11, and the Prime Agent runtime." \ "Prepare? [Y/n]"; then prime_agent_bootstrap_kernel_on_install=1 return else prompt_status=$? fi
if [ "$prompt_status" -eq 2 ]; then printf 'No terminal detected; preparing the Python runtime during install.\n' prime_agent_bootstrap_kernel_on_install=1 return fi
prime_agent_bootstrap_kernel_on_install=0 if [ "$prime_agent_screen_enabled" = 1 ]; then prime_agent_screen "Python setup skipped" "" "The runtime can be prepared on first ipython use." "" sleep 0.4 else printf '\nSkipping Python runtime setup.\n' fi}
prime_agent_npm_requires_remote_policy() { npm_version=$(npm --version 2>/dev/null) || return 1 npm_major=${npm_version%%.*} case "$npm_major" in ""|*[!0-9]*) return 1 ;; esac [ "$npm_major" -ge 12 ]}
prime_agent_npm_install() { tarball_path="$1" shift if prime_agent_npm_requires_remote_policy; then # Limit npm 12's required policy overrides to the verified root package. env "$@" npm install -g --no-fund --no-audit --loglevel=error --progress=false \ --allow-remote=all --allow-scripts="$tarball_path" "$tarball_path" else env "$@" npm install -g --no-fund --no-audit --loglevel=error --progress=false "$tarball_path" fi}
install_prime_agent_package() { tarball_path="$1" if [ "$prime_agent_bootstrap_kernel_on_install" = 1 ]; then npm_install_details="Preparing global install.Linking command binaries.Installing runtime packages.Preloading search tools.Preparing Python kernel.Finalizing npm install." prime_agent_run_quiet_with_animation_steps \ "Installing Prime Agent" \ "Installing Prime Agent" \ "$npm_install_details" \ prime_agent_npm_install "$tarball_path" PRIME_AGENT_BOOTSTRAP_TOOLS_ON_INSTALL=1 PRIME_AGENT_BOOTSTRAP_KERNEL_ON_INSTALL=1 PRIME_AGENT_INSTALL_UV=1 else npm_install_details="Preparing global install.Linking command binaries.Installing runtime packages.Preloading search tools.Finalizing npm install." prime_agent_run_quiet_with_animation_steps \ "Installing Prime Agent" \ "Installing Prime Agent" \ "$npm_install_details" \ prime_agent_npm_install "$tarball_path" PRIME_AGENT_BOOTSTRAP_TOOLS_ON_INSTALL=1 fi}
# Supported glibc releases carry no libc suffix; musl builds are published separately.prime_agent_native_glibc() { native_glibc_version=$(getconf GNU_LIBC_VERSION 2>/dev/null) || return 1 printf '%s\n' "$native_glibc_version" | awk '$1 == "glibc" { split($2, v, "."); if (v[1] > 2 || (v[1] == 2 && v[2] >= 17)) ok=1 } END { exit !ok }'}
# Alpine and other musl distributions ship the loader under a fixed name; `ldd`# without arguments prints its musl banner and is the fallback for stripped images.prime_agent_native_musl() { for native_musl_loader in "$prime_agent_native_sysroot"/lib/ld-musl-*.so.1; do [ -e "$native_musl_loader" ] && return 0 done ldd 2>&1 | grep -q musl}
# Bun's default x64 build needs AVX2; hosts without it need the baseline build.# An unreadable CPU inventory selects baseline, which runs on every x86-64 CPU.prime_agent_native_avx2() { awk '/^flags[[:space:]]*:/ { for (i = 3; i <= NF; i++) if ($i == "avx2") found = 1 } END { exit !found }' \ "$prime_agent_native_sysroot/proc/cpuinfo" 2>/dev/null}
prime_agent_native_platform() { native_libc_suffix= case "$(uname -s)" in Darwin) native_os_version=$(sw_vers -productVersion) || return 1 case "${native_os_version%%.*}" in ''|*[!0-9]*) return 1 ;; esac [ "${native_os_version%%.*}" -ge 13 ] || return 1 native_os=darwin ;; Linux) native_os=linux if prime_agent_native_glibc; then native_libc_suffix= elif prime_agent_native_musl; then native_libc_suffix=-musl else return 1 fi ;; *) return 1 ;; esac case "$(uname -m)" in arm64|aarch64) printf '%s-arm64%s' "$native_os" "$native_libc_suffix" ;; x86_64|amd64) if [ "$native_os" = linux ] && ! prime_agent_native_avx2; then printf '%s-x64%s-baseline' "$native_os" "$native_libc_suffix" else printf '%s-x64%s' "$native_os" "$native_libc_suffix" fi ;; *) return 1 ;; esac}
prime_agent_native_cleanup() { if [ -n "${prime_agent_native_lock:-}" ] && [ "$(cat "$prime_agent_native_lock/pid" 2>/dev/null || :)" = "$$" ] && [ -n "${native_root:-}" ] && { [ -e "$native_root/.activation-state" ] || [ -L "$native_root/.activation-state" ]; }; then if prime_agent_native_recover_activation; then prime_agent_native_prune_releases else printf 'Could not recover the interrupted activation; inspect %s/.activation-state before retrying.\n' "$native_root" >&2 fi fi if [ -n "${prime_agent_native_activation_target:-}" ] && [ -n "${prime_agent_native_activation_previous:-}" ] && [ "$(readlink "$native_root/bin/prime-agent" 2>/dev/null || :)" = "$prime_agent_native_activation_target" ] && [ "$(readlink "$native_root/bin/previous" 2>/dev/null || :)" != "$prime_agent_native_activation_previous" ]; then prime_agent_native_atomic_link "$prime_agent_native_activation_previous" "$native_root/bin/previous" || printf 'Could not finish retaining the previous release; recover it from %s/releases.\n' "$native_root" >&2 fi prime_agent_native_activation_target= prime_agent_native_activation_previous= if [ -n "${prime_agent_native_stage:-}" ]; then rm -rf "$prime_agent_native_stage" prime_agent_native_stage= fi if [ -n "${prime_agent_native_lock:-}" ] && [ "$(cat "$prime_agent_native_lock/pid" 2>/dev/null || :)" = "$$" ]; then rm -f "$prime_agent_native_lock/pid" rmdir "$prime_agent_native_lock" fi prime_agent_native_lock= prime_agent_native_discard_adopted_root}
# A first install that never activated a release must not keep the ownership# marker: the next run would find a managed tree holding no executable. Only a# root this run created, still empty apart from what this run created, is given up.prime_agent_native_discard_adopted_root() { [ "${prime_agent_native_root_adopted:-0}" = 1 ] || return 0 prime_agent_native_root_adopted=0 [ -n "${native_root:-}" ] || return 0 if [ -e "$native_root/bin/prime-agent" ] || [ -L "$native_root/bin/prime-agent" ]; then return 0; fi [ -z "$(ls -A "$native_root" 2>/dev/null | grep -vxE '\.managed|bin|releases' || :)" ] || return 0 rmdir "$native_root/bin" "$native_root/releases" 2>/dev/null || : if [ -e "$native_root/bin" ] || [ -e "$native_root/releases" ]; then return 0; fi rm -f "$native_root/.managed" rmdir "$native_root" 2>/dev/null || :}
prime_agent_native_prepare_root() { native_root="${PRIME_AGENT_INSTALL_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/prime-agent}" case "$native_root" in /*) ;; *) printf 'error: install directory must be absolute.\n' >&2; exit 1 ;; esac mkdir -p "$native_root" native_root=$(CDPATH= cd "$native_root" && pwd -P) if [ -L "$native_root/.managed" ] || [ -L "$native_root/releases" ] || [ -L "$native_root/bin" ]; then printf 'error: managed installation directories must not be symlinks.\n' >&2; exit 1 fi prime_agent_native_root_adopted=0 if [ -e "$native_root/.managed" ]; then [ "$(cat "$native_root/.managed")" = prime-agent-native-v1 ] || { printf 'error: unrecognized installation owner in %s.\n' "$native_root" >&2; exit 1; } elif [ -n "$(ls -A "$native_root")" ]; then printf 'error: refusing to take ownership of nonempty directory %s.\n' "$native_root" >&2 exit 1 else # Ownership taken now is given back if this run never activates a release. prime_agent_native_root_adopted=1 fi # Never steal a lock: even stale-lock recovery can race with another installer. if ! mkdir "$native_root/.install-lock" 2>/dev/null; then native_lock_pid=$(cat "$native_root/.install-lock/pid" 2>/dev/null || :) printf 'error: installation is locked (pid %s). After confirming no installer is running, remove %s/.install-lock.\n' "$native_lock_pid" "$native_root" >&2 exit 1 fi prime_agent_native_lock="$native_root/.install-lock" printf '%s\n' "$$" >"$prime_agent_native_lock/pid" printf 'prime-agent-native-v1\n' >"$native_root/.managed" mkdir -p "$native_root/releases" "$native_root/bin" for native_link in prime-agent previous; do if [ -e "$native_root/bin/$native_link" ] || [ -L "$native_root/bin/$native_link" ]; then [ -L "$native_root/bin/$native_link" ] && prime_agent_native_valid_target "$(readlink "$native_root/bin/$native_link")" || { printf 'error: unrecognized managed command target.\n' >&2; exit 1; } fi done prime_agent_native_sweep_orphan_stages prime_agent_native_stage=$(mktemp -d "$native_root/.install.XXXXXX") prime_agent_native_recovered=0 prime_agent_native_recover_activation || exit 1 if [ "$prime_agent_native_recovered" = 1 ]; then prime_agent_native_prune_releases fi if [ "${PRIME_AGENT_EXPECTED_CURRENT+x}" = x ] && [ "$(readlink "$native_root/bin/prime-agent" 2>/dev/null || :)" != "$PRIME_AGENT_EXPECTED_CURRENT" ]; then printf 'error: the active release changed; retry the update.\n' >&2; exit 1 fi}
prime_agent_native_rollback() { prime_agent_install_traps prime_agent_native_prepare_root [ -L "$native_root/bin/previous" ] || { printf 'error: no previous compiled release is available.\n' >&2; exit 1; } native_previous=$(readlink "$native_root/bin/previous") native_current=$(readlink "$native_root/bin/prime-agent") if [ -n "${PRIME_AGENT_EXPECTED_PREVIOUS:-}" ] && [ "$native_previous" != "$PRIME_AGENT_EXPECTED_PREVIOUS" ]; then printf 'error: the previous release changed while planning rollback; retry.\n' >&2; exit 1 fi [ "$native_previous" != "$native_current" ] || { printf 'error: no different previous release is available.\n' >&2; exit 1; } prime_agent_native_verify_release_target "$native_previous" previous || exit 1 if prime_agent_native_verify_release_target "$native_current" current >/dev/null 2>&1; then prime_agent_native_activate "$native_previous" "$native_current" else # Repair only the active link; keep the healthy rollback target instead of retaining damage. prime_agent_native_atomic_link "$native_previous" "$native_root/bin/prime-agent" || exit 1 fi prime_agent_native_prune_releases printf 'Restored the previous compiled release.\n' prime_agent_native_cleanup}
prime_agent_native_valid_target() { prime_agent_native_parse_target "$1" return $?}
prime_agent_native_parse_target() { native_parsed_target="$1" case "$native_parsed_target" in ../releases/*/prime-agent) ;; *) return 1 ;; esac native_parsed_name=${native_parsed_target#../releases/} native_parsed_name=${native_parsed_name%/prime-agent} case "$native_parsed_name" in ''|.|..|*/*|*[!0-9A-Za-z.-]*) return 1 ;; esac native_parsed_digest_suffix=${native_parsed_name##*-} native_parsed_digest=${native_parsed_digest_suffix%%.*} [ "${#native_parsed_digest}" -eq 64 ] || return 1 case "$native_parsed_digest" in *[!0-9a-f]*) return 1 ;; esac if [ "$native_parsed_digest_suffix" != "$native_parsed_digest" ]; then native_parsed_unique=${native_parsed_digest_suffix#"$native_parsed_digest".} [ "${#native_parsed_unique}" -eq 6 ] || return 1 case "$native_parsed_unique" in *[!0-9A-Za-z]*) return 1 ;; esac fi native_parsed_prefix=${native_parsed_name%-"$native_parsed_digest_suffix"} # Longest platform suffix first: a shorter arm matches the prefix of a longer # platform name, so `<version>-linux-x64-musl` must not be read as `linux-x64`. case "$native_parsed_prefix" in *-linux-x64-musl-baseline) native_parsed_platform=linux-x64-musl-baseline ;; *-linux-x64-musl) native_parsed_platform=linux-x64-musl ;; *-linux-x64-baseline) native_parsed_platform=linux-x64-baseline ;; *-linux-arm64-musl) native_parsed_platform=linux-arm64-musl ;; *-darwin-arm64) native_parsed_platform=darwin-arm64 ;; *-darwin-x64) native_parsed_platform=darwin-x64 ;; *-linux-arm64) native_parsed_platform=linux-arm64 ;; *-linux-x64) native_parsed_platform=linux-x64 ;; *) return 1 ;; esac native_parsed_version=${native_parsed_prefix%-"$native_parsed_platform"} printf '%s\n' "$native_parsed_version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$' || return 1 native_parsed_dir="$native_root/releases/$native_parsed_name"}
prime_agent_native_validate_asset() { native_asset_release_dir="$1" native_asset_relative="$2" native_asset_path="$native_asset_release_dir/$native_asset_relative" [ -f "$native_asset_path" ] && [ ! -L "$native_asset_path" ] || return 1 case "$native_asset_relative" in */*) native_asset_parent_relative=${native_asset_relative%/*} ;; *) native_asset_parent_relative= ;; esac if [ -n "$native_asset_parent_relative" ]; then native_asset_expected_parent="$native_asset_release_dir/$native_asset_parent_relative" else native_asset_expected_parent="$native_asset_release_dir" fi native_asset_actual_parent=$(CDPATH= cd "$(dirname "$native_asset_path")" 2>/dev/null && pwd -P) || return 1 [ "$native_asset_actual_parent" = "$native_asset_expected_parent" ]}
prime_agent_native_validate_release_metadata() { native_metadata_target="$1" prime_agent_native_parse_target "$native_metadata_target" || return 1 native_metadata_dir="$native_parsed_dir" native_metadata_version="$native_parsed_version" native_metadata_digest="$native_parsed_digest" [ -d "$native_metadata_dir" ] && [ ! -L "$native_metadata_dir" ] || return 1 [ "$(CDPATH= cd "$native_metadata_dir" 2>/dev/null && pwd -P)" = "$native_metadata_dir" ] || return 1 for native_metadata_asset in prime-agent package.json install.sh prime-agent-runtime/pyproject.toml prime-agent-runtime/src/rlm/repl.py theme/prime.json export-html/template.html photon_rs_bg.wasm .archive-sha256 .install-source; do prime_agent_native_validate_asset "$native_metadata_dir" "$native_metadata_asset" || return 1 done [ "$(cat "$native_metadata_dir/.archive-sha256")" = "$native_metadata_digest" ] || return 1 native_metadata_package_version=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$native_metadata_dir/package.json") [ "$native_metadata_package_version" = "$native_metadata_version" ] || return 1 native_metadata_source=$(cat "$native_metadata_dir/.install-source") case "$native_metadata_source" in http://*|https://*) ;; *) return 1 ;; esac}
prime_agent_native_probe_timeout() { # The first run of a freshly extracted executable can be slow: Rosetta 2 translates # the whole binary before it prints anything, and a cold page cache adds more. A # 51 MB x64 executable measured 11.3s cold and 0.2s warm on Apple Silicon. native_probe_timeout=60 case "${PRIME_AGENT_PROBE_TIMEOUT_SECONDS:-}" in ''|*[!0-9]*) ;; *) # $((...)) reads leading-zero constants as octal: "010" would become an # 8-second deadline while being reported as 10, and "08"/"09" are invalid # octal and abort the arithmetic under set -eu. Strip the leading zeroes # so the accepted value stays decimal; an all-zero value strips to the # empty string and falls back to the default like any invalid value. native_probe_timeout="${PRIME_AGENT_PROBE_TIMEOUT_SECONDS#"${PRIME_AGENT_PROBE_TIMEOUT_SECONDS%%[!0]*}"}" [ -n "$native_probe_timeout" ] && [ "$native_probe_timeout" -le 600 ] || native_probe_timeout=60 ;; esac printf '%s\n' "$native_probe_timeout"}
prime_agent_native_probe() ( # macOS does not ship timeout; keep the deadline independent of Node and Python. native_probe_pid= trap ' if [ -n "$native_probe_pid" ]; then kill -KILL "$native_probe_pid" 2>/dev/null || : wait "$native_probe_pid" 2>/dev/null || : fi ' EXIT trap 'exit 130' INT trap 'exit 143' TERM trap 'exit 129' HUP "$@" & native_probe_pid=$! native_probe_timeout=$(prime_agent_native_probe_timeout) # date +%s truncates to whole seconds, so add one to avoid killing a probe early. native_probe_deadline=$(($(date +%s) + native_probe_timeout + 1)) while kill -0 "$native_probe_pid" 2>/dev/null; do if [ "$(date +%s)" -ge "$native_probe_deadline" ]; then printf 'error: executable probe timed out after %s seconds.\n' "$native_probe_timeout" >&2 kill -KILL "$native_probe_pid" 2>/dev/null || : wait "$native_probe_pid" 2>/dev/null || : native_probe_pid= exit 124 fi sleep 0.1 done if wait "$native_probe_pid"; then native_probe_status=0; else native_probe_status=$?; fi native_probe_pid= exit "$native_probe_status")
# Bun's musl executables link against libstdc++, which Alpine does not preinstall.# The loader either names the library or reports a wall of C++ relocation failures.prime_agent_native_probe_missing_libstdcxx() { native_probe_log="$1" [ -f "$native_probe_log" ] || return 1 grep -q 'libstdc++\.so\.6' "$native_probe_log" && return 0 # musl names no library when every C++ runtime symbol is unresolved, so match # the mangled and Itanium ABI names it does report. grep -Eq 'Error relocating .*: (_Z|__cxa_|__dynamic_cast|__once_proxy)[A-Za-z0-9_]*: symbol not found' "$native_probe_log"}
# One actionable line beats sixty relocation errors the user cannot act on.prime_agent_native_report_missing_libstdcxx() { printf 'error: the compiled executable needs the libstdc++ runtime library, which is missing here.\n' >&2 printf 'Install it and run this installer again (Alpine: apk add --no-cache libstdc++).\n' >&2 printf 'To install the Node.js build instead, set PRIME_AGENT_INSTALL_METHOD=node.\n' >&2}
prime_agent_native_verify_release_target() { native_verify_target="$1" native_verify_label="$2" if ! prime_agent_native_validate_release_metadata "$native_verify_target"; then printf 'error: invalid %s release metadata or assets. The active release was kept.\n' "$native_verify_label" >&2 return 1 fi native_verify_dir="$native_metadata_dir" native_verify_version="$native_metadata_version" if ! prime_agent_native_probe "$native_verify_dir/prime-agent" --version >"$prime_agent_native_stage/$native_verify_label.version" 2>"$prime_agent_native_stage/$native_verify_label.probe.log"; then cat "$prime_agent_native_stage/$native_verify_label.probe.log" >&2 printf 'error: the %s release executable could not be validated. The active release was kept.\n' "$native_verify_label" >&2 return 1 fi if [ "$(cat "$prime_agent_native_stage/$native_verify_label.version")" != "$native_verify_version" ]; then printf 'error: the %s release executable reports a different version. The active release was kept.\n' "$native_verify_label" >&2 return 1 fi if ! prime_agent_native_probe "$native_verify_dir/prime-agent" --help >"$prime_agent_native_stage/$native_verify_label.help" 2>"$prime_agent_native_stage/$native_verify_label.help.log"; then cat "$prime_agent_native_stage/$native_verify_label.help.log" >&2 printf 'error: the %s release executable failed its help probe. The active release was kept.\n' "$native_verify_label" >&2 return 1 fi}
prime_agent_native_sweep_orphan_stages() { for native_orphan_stage in "$native_root"/.install.*; do [ -e "$native_orphan_stage" ] || [ -L "$native_orphan_stage" ] || continue native_orphan_name=${native_orphan_stage##*/} native_orphan_suffix=${native_orphan_name#.install.} [ "${#native_orphan_suffix}" -eq 6 ] || continue case "$native_orphan_suffix" in *[!0-9A-Za-z]*) continue ;; esac [ -d "$native_orphan_stage" ] && [ ! -L "$native_orphan_stage" ] || continue rm -rf "$native_orphan_stage" done}
prime_agent_native_release_in_use() { native_usage_executable="$1" if native_lsof=$(command -v lsof 2>/dev/null); then : elif [ -x /usr/sbin/lsof ]; then native_lsof=/usr/sbin/lsof elif [ -x /usr/bin/lsof ]; then native_lsof=/usr/bin/lsof else return 2 fi if "$native_lsof" -n -F p "$native_usage_executable" >"$prime_agent_native_stage/lsof.out" 2>"$prime_agent_native_stage/lsof.err"; then return 0 else native_lsof_status=$? fi [ "$native_lsof_status" -eq 1 ] && [ ! -s "$prime_agent_native_stage/lsof.err" ] && return 1 return 2}
prime_agent_native_prune_releases() { native_prune_current=$(readlink "$native_root/bin/prime-agent" 2>/dev/null || :) native_prune_previous=$(readlink "$native_root/bin/previous" 2>/dev/null || :) for native_prune_dir in "$native_root"/releases/*; do [ -e "$native_prune_dir" ] || [ -L "$native_prune_dir" ] || continue [ -d "$native_prune_dir" ] && [ ! -L "$native_prune_dir" ] || continue native_prune_target="../releases/${native_prune_dir##*/}/prime-agent" [ "$native_prune_target" != "$native_prune_current" ] || continue [ "$native_prune_target" != "$native_prune_previous" ] || continue prime_agent_native_validate_release_metadata "$native_prune_target" || continue if prime_agent_native_release_in_use "$native_prune_dir/prime-agent"; then continue else native_prune_usage_status=$? fi [ "$native_prune_usage_status" -eq 1 ] || continue rm -rf "$native_prune_dir" done}
prime_agent_native_recover_activation() { native_activation_state="$native_root/.activation-state" if [ ! -e "$native_activation_state" ] && [ ! -L "$native_activation_state" ]; then return 0 fi if [ ! -f "$native_activation_state" ] || [ -L "$native_activation_state" ] || [ "$(wc -l <"$native_activation_state" | tr -d ' ')" != 2 ]; then printf 'error: invalid activation recovery state at %s. Restore bin/prime-agent and bin/previous, then remove it.\n' "$native_activation_state" >&2 return 1 fi native_recovery_target=$(sed -n '1p' "$native_activation_state") native_recovery_previous=$(sed -n '2p' "$native_activation_state") prime_agent_native_verify_release_target "$native_recovery_target" recovery-target || return 1 if [ -n "$native_recovery_previous" ]; then prime_agent_native_verify_release_target "$native_recovery_previous" recovery-previous || return 1 fi native_recovery_current=$(readlink "$native_root/bin/prime-agent" 2>/dev/null || :) if [ "$native_recovery_current" = "$native_recovery_target" ]; then if [ -n "$native_recovery_previous" ] && [ "$native_recovery_previous" != "$native_recovery_target" ] && [ "$(readlink "$native_root/bin/previous" 2>/dev/null || :)" != "$native_recovery_previous" ]; then prime_agent_native_atomic_link "$native_recovery_previous" "$native_root/bin/previous" || return 1 fi elif [ "$native_recovery_current" != "$native_recovery_previous" ]; then printf 'error: activation recovery is ambiguous: bin/prime-agent matches neither recorded release. Restore the links using %s before retrying.\n' "$native_activation_state" >&2 return 1 fi rm -f "$native_activation_state" || return 1 prime_agent_native_recovered=1}
prime_agent_native_check_public_link() { native_public_bin="${PRIME_AGENT_BIN_DIR:-$HOME/.local/bin}" case "$native_public_bin" in /*) ;; *) printf 'error: bin directory must be absolute.\n' >&2; exit 1 ;; esac if [ "${PRIME_AGENT_INSTALL_LINK:-1}" = 0 ]; then return; fi case "$prime_agent_cmd" in ''|.|..|*/*) printf 'error: command name must be a basename.\n' >&2; exit 1 ;; esac if [ -e "$native_public_bin/$prime_agent_cmd" ] || [ -L "$native_public_bin/$prime_agent_cmd" ]; then if [ ! -L "$native_public_bin/$prime_agent_cmd" ] || [ "$(readlink "$native_public_bin/$prime_agent_cmd")" != "$native_root/bin/prime-agent" ]; then printf 'error: refusing to replace existing command %s/%s.\n' "$native_public_bin" "$prime_agent_cmd" >&2 printf 'Choose PRIME_AGENT_BIN_DIR or set PRIME_AGENT_INSTALL_LINK=0.\n' >&2 exit 1 fi fi mkdir -p "$native_public_bin"}
prime_agent_native_validate_archive() { LC_ALL=C tar -tzf "$native_archive" >"$prime_agent_native_stage/entries" awk ' /^\// || /(^|\/)\.\.(\/|$)/ || /\\/ { bad=1 } END { exit bad } ' "$prime_agent_native_stage/entries" || { printf 'error: unsafe archive path.\n' >&2; exit 1; } LC_ALL=C tar -tvzf "$native_archive" >"$prime_agent_native_stage/types" awk 'substr($0, 1, 1) != "-" && substr($0, 1, 1) != "d" { bad=1 } END { exit bad }' \ "$prime_agent_native_stage/types" || { printf 'error: archive contains links or special files.\n' >&2; exit 1; }}
prime_agent_native_atomic_link() { native_link_stage=$(mktemp -d "$prime_agent_native_stage/link.XXXXXX") || return 1 ln -s "$1" "$native_link_stage/link" || return 1 # The destination is an executable link, never a directory link. mv -f "$native_link_stage/link" "$2" || return 1 rmdir "$native_link_stage" || return 1}
prime_agent_native_activate() { prime_agent_native_activation_target="$1" prime_agent_native_activation_previous="$2" { printf '%s\n' "$1" printf '%s\n' "$2" } >"$prime_agent_native_stage/activation-state" mv -f "$prime_agent_native_stage/activation-state" "$native_root/.activation-state" prime_agent_native_atomic_link "$1" "$native_root/bin/prime-agent" if [ -n "$2" ] && [ "$1" != "$2" ]; then prime_agent_native_atomic_link "$2" "$native_root/bin/previous" fi rm -f "$native_root/.activation-state" prime_agent_native_activation_target= prime_agent_native_activation_previous=}
prime_agent_release_is_node_only() { awk -v file="$2" ' $2 ~ /\.tar\.gz$/ { compiled=1 } $2 == file { count++; if (NF != 2 || length($1) != 64 || $1 ~ /[^0-9a-fA-F]/) invalid=1 } END { exit compiled || invalid || count != 1 } ' "$1"}
prime_agent_install_native() { native_platform="$1" shift if [ "$prime_agent_base_url" = "$prime_agent_unconfigured_base_url" ]; then printf 'error: set PRIME_AGENT_DOWNLOAD_BASE_URL or use the published installer.\n' >&2; exit 1 fi prime_agent_validate_download_base_url prime_agent_install_traps prime_agent_init_screen native_version=$(resolve_prime_agent_version "$@") printf '%s\n' "$native_version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$' || { printf 'error: invalid native release version.\n' >&2; exit 1; } # Select the release format before touching native-owned installation paths. prime_agent_download_dir=$(create_temp_dir) native_file="prime-agent-$native_version-$native_platform.tar.gz" native_checksums="$prime_agent_download_dir/SHA256SUMS" prime_agent_run_quiet_with_animation "Downloading Prime Agent" "Downloading release checksums" "Prime Agent v$native_version" \ prime_agent_curl_download -fsSL --connect-timeout 10 --max-time 120 "$prime_agent_base_url/releases/v$native_version/SHA256SUMS" -o "$native_checksums" awk -v file="$native_file" '$2 == file { count++; hash=$1; fields=NF } END { if (count != 1 || fields != 2 || length(hash) != 64 || hash ~ /[^0-9a-fA-F]/) exit 1; print tolower(hash) " " file }' \ "$native_checksums" >"$prime_agent_download_dir/selected.sha256" || { if [ "${PRIME_AGENT_INSTALL_METHOD:-auto}" = auto ] && prime_agent_release_is_node_only "$native_checksums" "$prime_agent_package-$native_version.tgz"; then rm -rf "$prime_agent_download_dir" prime_agent_download_dir= printf 'This release only provides npm packages; using the Node installation.\n' >&2 prime_agent_install_node "$native_version" return fi printf 'error: expected one valid checksum for %s.\n' "$native_file" >&2; exit 1; } if [ "${PRIME_AGENT_INSTALLER_NONINTERACTIVE:-0}" != 1 ]; then if prime_agent_prompt_yes_no "Install Prime Agent v$native_version?" "Downloads and verifies the compiled application." "Install? [Y/n]"; then : else native_prompt_status=$? [ "$native_prompt_status" = 2 ] || return 0 fi fi prime_agent_native_prepare_root mv "$prime_agent_download_dir/selected.sha256" "$prime_agent_native_stage/selected.sha256" rm -rf "$prime_agent_download_dir" prime_agent_download_dir= native_archive="$prime_agent_native_stage/$native_file" if [ -n "${PRIME_AGENT_EXPECTED_SHA256:-}" ] && [ "$(awk '{print $1}' "$prime_agent_native_stage/selected.sha256")" != "$PRIME_AGENT_EXPECTED_SHA256" ]; then printf 'error: release manifest and checksum inventory disagree.\n' >&2; exit 1 fi prime_agent_run_quiet_with_animation "Downloading Prime Agent" "Downloading compiled Prime Agent" "$native_platform" \ prime_agent_curl_download -fsSL --connect-timeout 10 --max-time 300 "$prime_agent_base_url/releases/v$native_version/$native_file" -o "$native_archive" if command -v sha256sum >/dev/null 2>&1; then native_checker=sha256sum; else native_checker=shasum; fi prime_agent_run_quiet_with_animation "Verifying Prime Agent" "Verifying SHA-256" "Checking the downloaded archive." \ prime_agent_run_checksum_check "$prime_agent_native_stage" selected.sha256 "$native_checker" prime_agent_native_validate_archive mkdir "$prime_agent_native_stage/application" tar -xzf "$native_archive" -C "$prime_agent_native_stage/application" native_extracted="$prime_agent_native_stage/application" for native_asset in prime-agent package.json install.sh prime-agent-runtime/pyproject.toml prime-agent-runtime/src/rlm/repl.py theme/prime.json export-html/template.html photon_rs_bg.wasm; do [ -f "$native_extracted/$native_asset" ] || { printf 'error: missing archive asset: %s\n' "$native_asset" >&2; exit 1; } done native_probe_status=0 prime_agent_native_probe "$native_extracted/prime-agent" --version >"$prime_agent_native_stage/version" 2>"$prime_agent_native_stage/probe.log" || native_probe_status=$? if [ "$native_probe_status" -ne 0 ]; then # A missing libstdc++ is one package away, so name it instead of downloading Node. if prime_agent_native_probe_missing_libstdcxx "$prime_agent_native_stage/probe.log"; then prime_agent_native_cleanup prime_agent_native_report_missing_libstdcxx exit 1 fi cat "$prime_agent_native_stage/probe.log" >&2 prime_agent_native_cleanup # A timeout means the executable never answered, not that it cannot run here. if [ "$native_probe_status" -eq 124 ]; then native_probe_hint="Set PRIME_AGENT_PROBE_TIMEOUT_SECONDS to a larger value and run the installer again." if [ "${PRIME_AGENT_INSTALL_METHOD:-auto}" = auto ]; then printf 'The compiled executable did not answer in time; using the Node installation. %s\n' "$native_probe_hint" >&2 prime_agent_install_node "$native_version" return fi printf 'error: the compiled executable did not answer within %s seconds. %s\n' \ "$(prime_agent_native_probe_timeout)" "$native_probe_hint" >&2 exit 1 fi if [ "${PRIME_AGENT_INSTALL_METHOD:-auto}" = auto ]; then printf 'The compiled executable cannot run here; using the Node installation.\n' >&2 prime_agent_install_node "$native_version" return fi printf 'error: the compiled executable cannot run on this machine.\n' >&2; exit 1 fi [ "$(cat "$prime_agent_native_stage/version")" = "$native_version" ] || { printf 'error: archive version mismatch.\n' >&2; exit 1; } prime_agent_native_probe "$native_extracted/prime-agent" --help >"$prime_agent_native_stage/help" prime_agent_native_check_public_link native_digest=$(awk '{ print $1 }' "$prime_agent_native_stage/selected.sha256") native_release_name="$native_version-$native_platform-$native_digest" native_destination="$native_root/releases/$native_release_name" if [ -e "$native_destination" ] || [ -L "$native_destination" ]; then # Reinstalls activate fresh assets without modifying a running release. native_destination=$(mktemp -d "$native_destination.XXXXXX") rmdir "$native_destination" native_release_name=${native_destination##*/} fi printf '%s\n' "$native_digest" >"$native_extracted/.archive-sha256" printf '%s\n' "$prime_agent_base_url" >"$native_extracted/.install-source" mv "$native_extracted" "$native_destination" if [ "${PRIME_AGENT_BOOTSTRAP_KERNEL_ON_INSTALL:-}" != 0 ]; then confirm_kernel_runtime_setup if [ "$prime_agent_bootstrap_kernel_on_install" = 1 ]; then if ! prime_agent_run_quiet_with_animation "Preparing Python" "Preparing Python runtime" "Installing uv and Python dependencies." \ env PRIME_AGENT_INSTALL_UV=1 "$native_destination/prime-agent" --prime-agent-bootstrap; then printf 'Python preparation failed; retry on first Python use.\n' >&2 fi fi fi prime_agent_native_check_public_link native_target="../releases/$native_release_name/prime-agent" native_previous= if [ -L "$native_root/bin/prime-agent" ]; then native_previous=$(readlink "$native_root/bin/prime-agent") fi # A fresh public command must point to an already activated executable. if [ -z "$native_previous" ]; then prime_agent_native_activate "$native_target" "" fi if [ "${PRIME_AGENT_INSTALL_LINK:-1}" != 0 ] && [ ! -L "$native_public_bin/$prime_agent_cmd" ]; then ln -sn "$native_root/bin/prime-agent" "$native_public_bin/$prime_agent_cmd" fi # Keep an existing release active if creating the public command loses a race. if [ -n "$native_previous" ]; then if prime_agent_native_verify_release_target "$native_previous" current >/dev/null 2>&1; then prime_agent_native_activate "$native_target" "$native_previous" else prime_agent_native_atomic_link "$native_target" "$native_root/bin/prime-agent" || exit 1 fi fi prime_agent_native_prune_releases if [ "${PRIME_AGENT_INSTALL_LINK:-1}" != 0 ]; then prime_agent_native_configure_path || printf 'Add %s to PATH to run Prime Agent.\n' "$native_public_bin" >&2 fi prime_agent_screen "Prime Agent installed" "" "Run it with: $prime_agent_cmd" "" printf 'Installed Prime Agent %s at %s\n' "$native_version" "$native_root/bin/prime-agent" prime_agent_native_cleanup}
prime_agent_native_configure_path() { case ":$prime_agent_original_path:" in *":$native_public_bin:"*) return ;; esac native_path_line="export PATH=$(prime_agent_shell_quote "$native_public_bin"):\$PATH" if native_profile=$(detect_shell_profile); then if ! grep -Fx "$native_path_line" "$native_profile" >/dev/null 2>&1; then mkdir -p "$(dirname "$native_profile")" printf '\n# Prime Agent\n%s\n' "$native_path_line" >>"$native_profile" fi fi printf 'For this shell, run: %s\n' "$native_path_line"}
main "$@"