Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
156 kB · 4083 lines
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084# Aesthetic Computer Development Environment Fish Config (Docker)
# Set environment variables to prevent ETXTBSY errorsset -gx NETLIFY_CLI_TELEMETRY_DISABLED 1set -gx NODE_DISABLE_COMPILE_CACHE 1
if test -f /home/me/envs/load_envs.fish source /home/me/envs/load_envs.fish if functions -q load_envs load_envs # Load devcontainer envs conditionally. endend
# Add Deno to PATHset -gx PATH /home/me/.deno/bin $PATH
# Enable `fnm` support.
if test -f $HOME/.fnm/fnm set -gx PATH $HOME/.fnm $PATH fnm env --use-on-cd --log-level=quiet | source fnm use lts-jod --silent-if-unchanged 2>/dev/nullend
# Symlink a VSCode workspace as needed.# On macOS the relationship is reversed: /workspaces is a synthetic.conf symlink# to the user's home, so the repo already lives at ~/aesthetic-computer and the# codespaces-style symlink would loop/fail. Skip on Darwin.if test -d /workspaces/aesthetic-computer; and test (uname) != Darwin if test ! -L ~/aesthetic-computer echo "Symlinking /workspaces/aesthetic-computer to ~" ln -s /workspaces/aesthetic-computer ~ endend
# Persist Copilot CLI sessions across container restarts# Now handled by Docker volume mount (aesthetic-copilot-data)# The volume is mounted at /home/me/.copilot in devcontainer.json# No symlink needed - just ensure proper permissionsif test -d ~/.copilot # Fix permissions if needed (volume may have wrong owner after restart) if not test -O ~/.copilot sudo chown -R me:me ~/.copilot 2>/dev/null endend
function fish_prompt # Show shell and directory for LLM context set_color cyan echo -n 'fish' set_color normal echo -n ' ' set_color yellow echo -n (prompt_pwd) set_color normal echo -n ' > 'end
function dns set -l domain $argv[1] echo "https://iwantmyname.com/dashboard/dns/$domain"end
function fish_greeting if test "$nogreet" = true return end
printf "🧩 Hi @$AESTHETIC!\n\n" # Show the new KidLisp source tree shortcut
# printf "Ask with 'umm' and forget with 'nvm'\n" # printf "or use 'code' and 'done' with 'copy'\n" # printf "to generate and get code.\n\n" # printf "🆕 Try 'aider' to make edits: https://github.com/paul-gauthier/aider?tab=readme-ov-file#usage\n\n" # printf "📋 Clipboard also requires `xhost +local:docker` to be set on the host."end
function ac-tv # Detect Codespaces and construct the appropriate base URL if test -n "$CODESPACES"; and test -n "$CODESPACE_NAME"; and test -n "$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN" set -l default_base "https://$CODESPACE_NAME-8888.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN/api/tv" else set -l default_base "https://localhost:8888/api/tv" end set -l base $default_base set -l preview true set -l query_params set -l script_args
for arg in $argv switch $arg case '--preview' 'preview' set preview true case '--no-preview' 'no-preview' '--json' 'json' set preview false case '--raw' set preview true set script_args $script_args '--raw' case '--max-width=*' '--maxwidth=*' '--max-height=*' '--maxheight=*' '--pause=*' '--pauseMs=*' '--pausems=*' set preview true set script_args $script_args $arg case '--base=*' set base (string replace -r '^--base=' '' -- $arg) case 'base=*' set base (string replace 'base=' '' -- $arg) case '--limit=*' '--types=*' set query_params $query_params (string replace -r '^--' '' -- $arg) set script_args $script_args $arg case 'limit=*' 'types=*' set query_params $query_params $arg set script_args $script_args $arg case '--*' # Forward unknown long flags to preview script if used set script_args $script_args $arg case '*=*' set query_params $query_params $arg set script_args $script_args $arg case '*' set query_params $query_params $arg set script_args $script_args $arg end end
set -l url $base if test (count $query_params) -gt 0 set -l query (string join '&' $query_params) set url "$base?"$query end
if test $preview = true set -l script "/workspaces/aesthetic-computer/reference/tools/recording/tv-preview.mjs" if not test -f $script echo "❌ Preview script missing at $script" >&2 return 1 end
if test $base != $default_base set script_args $script_args "--base=$base" end
printf "📺 Preview %s\n" $url node --no-warnings $script $script_args return $status end
printf "📺 GET %s\n" $url
set -l response (curl --silent --show-error --insecure $url) set -l curl_status $status if test $curl_status -ne 0 echo "❌ Failed to reach $url" >&2 return 1 end
set -l body (string join "\n" $response)
if type -q jq printf '%s\n' $body | jq else if type -q python3 printf '%s\n' $body | python3 -m json.tool else printf '%s\n' $body endend
# Run public kidlisp CLI when installed.function __ac_kidlisp_cli --description "Internal helper for kidlisp CLI" if command -q kidlisp kidlisp $argv return $status end
return 127end
# KidLisp source tree helper (used by ac-kidlisp compatibility commands)function __ac_kidlisp_source_tree --description "Internal KidLisp source tree helper" if test "$argv[1]" = "--test-colors" node --no-warnings /workspaces/aesthetic-computer/kidlisp/tools/source-tree.mjs --test-colors return end
if test "$argv[1]" = "--test-css-colors" node --no-warnings /workspaces/aesthetic-computer/kidlisp/tools/source-tree.mjs --test-css-colors return end
if test "$argv[1]" = "--debug-colors" node --no-warnings /workspaces/aesthetic-computer/kidlisp/tools/source-tree.mjs --debug-colors return end
set -l piece_name $argv[1] set -l extra_args $argv[2..-1]
if test -z "$piece_name" echo "Usage: ac-kidlisp source-tree <piece> [--source]" return 1 end
# Remove $ prefix if present if string match -q '$*' $piece_name set piece_name (string sub -s 2 $piece_name) end
# Prefer public kidlisp CLI when available if __ac_kidlisp_cli tree $piece_name $extra_args return $status end
# Local fallback while kidlisp-cli implementation is still spec-first node --no-warnings /workspaces/aesthetic-computer/kidlisp/tools/source-tree.mjs $piece_name $extra_argsend
# ac-st compatibility wrapper (preferred usage is now: ac-kidlisp source-tree ...)function ac-st --description "KidLisp source tree viewer" ac-kidlisp source-tree $argvend
# Legacy short command (kept for muscle memory)function st --description "Deprecated: use ac-kidlisp source-tree or ac-st" echo "⚠️ st is deprecated; use `ac-kidlisp source-tree` or `kidlisp tree`." ac-kidlisp source-tree $argvend
# Alternative function for those who prefer the $ syntaxfunction dollarpiece ac-kidlisp source-tree $argvend
# AC Pack - Package pieces for Teia with cover GIF generation# Usage: ac-pack '$ceo' or ac-pack 'ceo' [--density N] (from any directory)function ac-pack if test (count $argv) -lt 1 echo "Usage: ac-pack PIECE_NAME [--density N]" echo "Example: ac-pack '\$ceo' or ac-pack 'ceo'" echo " ac-pack '\$bop' --density 8" return 1 end set -l piece_name $argv[1] set -l current_dir (pwd) # Store original directory echo "📂 Packaging $piece_name from $current_dir" # Run the ac-pack.mjs script from the teia directory cd /workspaces/aesthetic-computer/teia # Run the packaging with all arguments plus target directory command node ac-pack.mjs $argv --target-dir "$current_dir" # Check if packaging was successful if test $status -eq 0 echo "✅ AC Pack complete! Files created in $current_dir" else echo "❌ AC Pack failed with exit code $status" end # Return to original directory cd $current_dirend
# AC Login - Authenticate with Aesthetic Computer (OAuth Device Code Flow)# Usage: ac-login [status|logout|token]# Stores access token in $AC_TOKEN environment variable for use in scriptsfunction ac-login set -l cmd $argv[1] if test "$cmd" = "status" node /workspaces/aesthetic-computer/tezos/ac-login.mjs status return $status else if test "$cmd" = "logout" node /workspaces/aesthetic-computer/tezos/ac-login.mjs logout set -e AC_TOKEN set -e AC_USER_EMAIL return 0 else if test "$cmd" = "token" node /workspaces/aesthetic-computer/tezos/ac-login.mjs token return $status else # Login and capture token node /workspaces/aesthetic-computer/tezos/ac-login.mjs if test $status -eq 0 # Read token from file and export to environment if test -f ~/.ac-token set -l token_json (cat ~/.ac-token) set -gx AC_TOKEN (echo $token_json | jq -r '.access_token') set -gx AC_USER_EMAIL (echo $token_json | jq -r '.user.email // .user.name // .user.sub') echo "" echo "🔑 Token exported to \$AC_TOKEN" echo "👤 User exported to \$AC_USER_EMAIL" echo "" echo "Example usage:" echo " curl -H \"Authorization: Bearer \$AC_TOKEN\" https://aesthetic.computer/api/keep-mint" end end return $status endend
# AC Token - Get current access token (shortcut)# Usage: ac-token [--refresh]function ac-token if test "$argv[1]" = "--refresh" # Re-read from file if test -f ~/.ac-token set -l token_json (cat ~/.ac-token) set -gx AC_TOKEN (echo $token_json | jq -r '.access_token') set -gx AC_USER_EMAIL (echo $token_json | jq -r '.user.email // .user.name // .user.sub') echo "✅ Token refreshed from ~/.ac-token" else echo "❌ No token file found. Run: ac-login" return 1 end else if test -n "$AC_TOKEN" echo $AC_TOKEN else # Try to load from file if test -f ~/.ac-token set -l token_json (cat ~/.ac-token) set -gx AC_TOKEN (echo $token_json | jq -r '.access_token') echo $AC_TOKEN else echo "❌ Not logged in. Run: ac-login" >&2 return 1 end endend
# AC Keeps - Interactive CLI for keeping KidLisp pieces on Tezos# Usage: ac-keeps [list|keep|wallet|status] [args...]# Commands:# ac-keeps - Interactive mode# ac-keeps list - List your pieces# ac-keeps list --top - List by popularity# ac-keeps keep $code - Keep a piece (you pay 5ꜩ)# ac-keeps wallet - Connect Tezos wallet# ac-keeps status $code - Check if piece is keptfunction ac-keeps node /workspaces/aesthetic-computer/tezos/ac-keeps.mjs $argvend
# AC Keep - Create self-contained HTML bundles for Tezos KEEPS# Usage: ac-keep '$bop' or ac-keep 'bop' (from any directory)# Creates a single HTML file that runs offline with all dependencies embedded# Filenames: @author-$piece-timestamp.html (matching bios.mjs download format)function ac-keep if test (count $argv) -lt 1 echo "Usage: ac-keep PIECE_NAME" echo "Example: ac-keep '\$bop' or ac-keep 'bop'" echo "" echo "Creates self-contained HTML bundles in tezos/keep-bundles/" echo "Output files (named like downloads from bios.mjs):" echo " • \$piece-@author-timestamp.html (uncompressed)" echo " • \$piece-@author-timestamp.brotli.html (Brotli, for Tezos)" echo " • \$piece-@author-timestamp.lisp.html (gzip, for browser/drag-drop)" return 1 end set -l piece_name $argv[1] # Remove $ prefix if present set piece_name (string replace -r '^\$' '' -- $piece_name) echo "📦 Creating KEEP bundle for \$$piece_name..." # Run the bundle-keep-html.mjs script and capture output set -l output (node /workspaces/aesthetic-computer/tezos/bundle-keep-html.mjs $piece_name 2>&1) set -l exit_status $status # Print the output echo $output if test $exit_status -eq 0 echo "" echo "✅ KEEP bundle created!" echo "📁 Files in: /workspaces/aesthetic-computer/tezos/keep-bundles/" else echo "❌ KEEP bundle failed with exit code $exit_status" end return $exit_statusend
# AC Keep Test - Build and serve a KEEP bundle for testing# Usage: ac-keep-test '$bop' or ac-keep-test 'bop'function ac-keep-test if test (count $argv) -lt 1 echo "Usage: ac-keep-test PIECE_NAME" echo "Example: ac-keep-test '\$bop'" return 1 end set -l piece_name $argv[1] # Remove $ prefix if present set piece_name (string replace -r '^\$' '' -- $piece_name) # Build the bundle and capture output echo "📦 Creating KEEP bundle for \$$piece_name..." node /workspaces/aesthetic-computer/tezos/bundle-keep-html.mjs $piece_name if test $status -ne 0 return 1 end echo "" echo "🌐 Starting test server on http://localhost:8082..." echo " Browse to http://localhost:8082/ and select the .gzip.html file" echo " Press Ctrl+C to stop" echo "" # Start server in the keep-bundles directory cd /workspaces/aesthetic-computer/tezos/keep-bundles python3 -m http.server 8082end
# Usage: ac-unpack [zip-file] [port] - unpacks and tests TEIA packages locally# If no zip file specified, finds the most recent one in current directoryfunction ac-unpack set -l current_dir (pwd) set -l zip_file $argv[1] set -l port (test (count $argv) -ge 2; and echo $argv[2]; or echo "8080") echo "📦 Unpacking TEIA package from $current_dir" # Run the ac-unpack.mjs script from the teia directory cd /workspaces/aesthetic-computer/teia if test -n "$zip_file" # Zip file specified - use full path if it's in the original directory if not test -f "$zip_file" set zip_file "$current_dir/$zip_file" end echo "📂 Using specified file: $zip_file" node ac-unpack.mjs "$zip_file" $port "$current_dir" else # No zip file specified - let ac-unpack find the latest one echo "🔍 Looking for latest zip file in $current_dir..." node ac-unpack.mjs "" $port "$current_dir" end # Return to original directory cd $current_dir if test $status -eq 0 echo "✅ AC Unpack complete! Server running on port $port" echo "🌐 Open http://localhost:$port in your browser to test" else echo "❌ AC Unpack failed with exit code $status" endend
# Usage: ac-ship [zip-file] [--platforms mac,windows,linux] - packages TEIA zips as Electron desktop apps# If no zip file specified, finds the most recent one in current directoryfunction ac-ship set -l current_dir (pwd) set -l args $argv echo "🚢 Shipping Electron apps from $current_dir" # Run the ac-ship.mjs script from the teia directory cd /workspaces/aesthetic-computer/teia # Process arguments to handle relative paths set -l processed_args for arg in $args if not string match -q -- '--*' $arg; and test -f "$current_dir/$arg" # This looks like a zip file in the original directory set processed_args $processed_args "$current_dir/$arg" else set processed_args $processed_args $arg end end node ac-ship.mjs $processed_args # Return to original directory cd $current_dir if test $status -eq 0 echo "✅ AC Ship complete! Electron apps built successfully" echo "📱 Check the output for app locations" else echo "❌ AC Ship failed with exit code $status" endend
# AC Record - Record pieces as MP4 or GIF using the orchestrator# Usage: ac-record PIECE_NAME [--duration=<value>] [--width N] [--height N] [--gif] [--sixel]# Duration accepts suffixes: `s` for seconds (e.g. 3s) or `f` for frames (e.g. 120f). Default: 5s (300f).# Example: ac-record '$bair' --duration=3s or ac-record 'orbital' --duration=120f --width 512 --height 512 --giffunction ac-record if test (count $argv) -lt 1 echo "Usage: ac-record PIECE_NAME [OPTIONS]" echo "Example: ac-record '$bair'" echo " ac-record 'orbital' --duration=120f --width 512 --height 512" echo " ac-record '$ceo' --gif --duration=3s" echo "" echo "Options:" echo " --duration VAL Recording length. Append 's' for seconds or 'f' for frames" echo " (default: 5s = 300f). Plain numbers are treated as frames." echo " --width N Video width (default: 1024)" echo " --height N Video height (default: 1024)" echo " --gif Output as GIF instead of MP4" echo " --gif-fps N Override GIF framerate (default: 50, allowed: 100,50,25,20,10,5,4,2,1)" echo " --gif-25 Convenience flag for 25fps GIF output" echo " --gif-compress Ultra-compress GIF for smallest file size" echo " --sixel Display result in terminal as sixel image" return 1 end
set -l piece_name $argv[1] set -l current_dir (pwd)
set -l frames 300 # ≈5 seconds at 60fps set -l duration_seconds "5.00" set -l width 1024 set -l height 1024 set -l gif_flag "" set -l sixel_flag "" set -l duration_spec "" set -l density "" set -l gif_fps "" set -l gif_compress "" # Process optional arguments set -l i 2 while test $i -le (count $argv) set -l arg $argv[$i] switch $arg case '--duration=*' set duration_spec (string split -m 1 '=' -- $arg)[2] case '--duration' set i (math $i + 1) if test $i -le (count $argv) set duration_spec $argv[$i] else echo "❌ Missing value for --duration" >&2 cd $current_dir return 1 end case '--frames' set i (math $i + 1) if test $i -le (count $argv) set -l frames_arg $argv[$i] set duration_spec (string join '' $frames_arg 'f') printf "⚠️ --frames is deprecated; use --duration=%sf instead\n" $frames_arg >&2 else echo "❌ Missing value for --frames" >&2 cd $current_dir return 1 end case '--width' set i (math $i + 1) if test $i -le (count $argv) set width $argv[$i] end case '--height' set i (math $i + 1) if test $i -le (count $argv) set height $argv[$i] end case '--density' set i (math $i + 1) if test $i -le (count $argv) set density $argv[$i] end case '--gif' set gif_flag "--gif" case '--sixel' set sixel_flag "sixel" case '--gif-fps' set i (math $i + 1) if test $i -le (count $argv) set gif_fps $argv[$i] end case '--gif-25' set gif_fps 25 case '--gif25' set gif_fps 25 case '--gif-compress' set gif_compress "--gif-compress" case '*' echo "⚠️ Ignoring unknown option: $arg" >&2 end set i (math $i + 1) end
if test -n "$duration_spec" set duration_spec (string trim (string lower $duration_spec)) set -l parse_result (python3 -c "import sysspec = sys.argv[1].strip().lower()if not spec: raise SystemExit(1)if spec.endswith('s'): seconds = float(spec[:-1] or '0') frames = int(round(seconds * 60))elif spec.endswith('f'): frames = int(round(float(spec[:-1] or '0'))) seconds = frames / 60.0else: frames = int(round(float(spec))) seconds = frames / 60.0if frames <= 0: raise SystemExit(1)print(frames)print(f'{seconds:.2f}')" $duration_spec)
if test $status -ne 0 echo "❌ Invalid --duration value: $duration_spec" >&2 cd $current_dir return 1 end
set frames $parse_result[1] set duration_seconds $parse_result[2] end
if test $frames -le 0 echo "❌ Duration must be greater than 0" >&2 cd $current_dir return 1 end
echo "🎬 Recording $piece_name from $current_dir" echo "📊 Settings: {$frames} frames (~{$duration_seconds}s) @ {$width}x{$height}" if test -n "$gif_flag" if test -n "$gif_fps" printf "🎞️ Output format: GIF @ %sfps\n" $gif_fps else echo "🎞️ Output format: GIF @ 50fps (default)" end else echo "🎞️ Output format: MP4" end if test -n "$density" echo "🔍 Density: $density" set -l final_width (math "$width * $density") set -l final_height (math "$height * $density") set final_width (string replace -r '\.0+$' '' $final_width) set final_height (string replace -r '\.0+$' '' $final_height) printf "📐 Output pixels: %sx%s\n" $final_width $final_height end # Run the orchestrator from the recording directory cd /workspaces/aesthetic-computer/reference/tools/recording
# Pass the original duration spec to orchestrator, or default if none specified set -l duration_arg if test -n "$duration_spec" set duration_arg $duration_spec else set duration_arg "5s" # Default to 5 seconds end
set -l cmd "node" "orchestrator.mjs" $piece_name $duration_arg $width $height if test -n "$gif_flag" set cmd $cmd $gif_flag end if test -n "$sixel_flag" set cmd $cmd $sixel_flag end if test -n "$density" set cmd $cmd "--density" $density end if test -n "$gif_fps" set cmd $cmd "--gif-fps" $gif_fps end if test -n "$gif_compress" set cmd $cmd $gif_compress end # Execute the recording command $cmd
if test $status -eq 0 echo "✅ AC Record complete! Video created in output directory"
set -l output_base_dir "/workspaces/aesthetic-computer/reference/tools/output" set -l piece_clean (string replace '$' '' $piece_name)
if test -n "$gif_flag" set -l latest_gif (find $output_base_dir -name "*$piece_clean*.gif" -type f -printf "%T@ %p\n" 2>/dev/null | sort -nr | head -n1 | cut -d' ' -f2-) if test -n "$latest_gif" cp "$latest_gif" "$current_dir/" set -l filename (basename "$latest_gif") echo "📁 Copied $filename to $current_dir" end else set -l latest_mp4 (find $output_base_dir -name "*$piece_clean*.mp4" -type f -printf "%T@ %p\n" 2>/dev/null | sort -nr | head -n1 | cut -d' ' -f2-) if test -n "$latest_mp4" cp "$latest_mp4" "$current_dir/" set -l filename (basename "$latest_mp4") echo "📁 Copied $filename to $current_dir" end end else echo "❌ AC Record failed with exit code $status" end
cd $current_dirend
# 🩸 Artery - Direct connection to Aesthetic Computer workbench# Interactive REPL for controlling AC, seeing console logs, and executing JavaScript# Usage: artery [command] [args...]# Examples:# artery # Show help# artery jump prompt # Navigate to prompt piece# artery current # Show current piece# artery repl # Interactive REPL modefunction artery if test (count $argv) -eq 0 # No args - show help node /workspaces/aesthetic-computer/artery/artery.mjs else # Pass through to artery node /workspaces/aesthetic-computer/artery/artery.mjs $argv endend
# 🩸 AC REPL - Quick shortcut to artery REPL mode# Live JavaScript console with AC console logs# Usage: ac-repl# Commands in REPL:# .jump <piece> - Navigate to piece# .current - Show current piece# .panel [action] - Control AC panel# .exit - Close arteryfunction ac-repl echo "🩸 Starting Artery REPL - Live connection to Aesthetic Computer" echo " 💉 Console logs will appear in real-time" echo " 🎯 Type .jump <piece> to navigate" echo " 🔍 Type JavaScript to execute in AC context" echo "" artery replend
# 🩸 Artery TUI - Interactive curses-style interface for AC# Usage: ac-artery or artery-tui# Features:# - Menu-driven interface with keyboard navigation# - Open AC panel, jump to pieces, run tests# - Live REPL with console log output# - WebGPU performance monitoringfunction ac-artery node /workspaces/aesthetic-computer/artery/artery-tui.mjsend
# Artery with hot-reload for development# Watches artery-tui.mjs and artery.mjs for changes and auto-restartsfunction ac-artery-dev node /workspaces/aesthetic-computer/artery/artery-dev.mjsend
function artery-tui ac-arteryend
# Automated testing for AC pieces# Usage: test-notepat [duration_ms]# Example: test-notepat 60000 (runs for 60 seconds)function test-notepat node /workspaces/aesthetic-computer/.vscode/tests/test-notepat.mjs $argvend
# Test line drawing tool# Usage: test-line [duration_ms]# Example: test-line 30000 (runs for 30 seconds)function test-line node /workspaces/aesthetic-computer/.vscode/tests/test-line.mjs $argvend
# Test toss piece# Usage: test-toss [duration_ms]# Example: test-toss 5000 (runs for 5 seconds)function test-toss node /workspaces/aesthetic-computer/.vscode/tests/test-toss.mjs $argvend
function test-melody node /workspaces/aesthetic-computer/.vscode/tests/test-melody.mjs $argvend
function test-playlist node /workspaces/aesthetic-computer/.vscode/tests/test-playlist.mjs $argvend
function test-chords node /workspaces/aesthetic-computer/.vscode/tests/test-chords.mjs $argvend
function test-generative-waltz node /workspaces/aesthetic-computer/.vscode/tests/test-generative-waltz.mjs $argvend
# 🌐 KidLisp.com Probe - Control kidlisp.com in Simple Browser via CDP# Usage:# kidlisp # Show status# kidlisp eval <js> # Evaluate JS in kidlisp.com# kidlisp theme # Toggle theme# kidlisp clear # Clear editor# kidlisp set <code> # Set editor content# kidlisp play # Press play buttonfunction kidlisp node /workspaces/aesthetic-computer/kidlisp-tools/kidlisp-probe.mjs $argvend
# always start in aesthetic-computer directory if there was a greetingif not test "$nogreet" = true cd ~/aesthetic-computerend
# rebuild the container after exiting with a special code ;)
# alias reload 'exit 70'
# reload fish configalias reload 'source /workspaces/aesthetic-computer/.devcontainer/config.fish'alias refish 'source ~/.config/fish/config.fish'
# Workaround for fish script execution on SELinux/btrfs bind mounts# The "No such file or directory" error happens when fish tries to execute# scripts via shebang on /workspaces (which is a bind mount from host).# Use `run script.fish` instead of `./script.fish` or `fish script.fish`function run --description "Run a fish script using source (workaround for bind mount issue)" if test (count $argv) -eq 0 echo "Usage: run <script.fish> [args...]" return 1 end set -l script $argv[1] set -l args $argv[2..-1] if not test -f $script echo "File not found: $script" return 1 end # Use source to run the script in current shell source $script $argsend
# Port visibility helper - sets port 8888 to public in Codespacesalias port-public 'source /workspaces/aesthetic-computer/.devcontainer/scripts/set-port-public.fish'
# SSH to Windows hostalias win 'ssh me@host.docker.internal'
# set default editor to emacsset -gx TERM xterm-256colorset -gx EDITOR emacsset -gx PATH $PATH /home/me/.local/bin
# add dotnet tools to pathset -Ux PATH $PATH $HOME/.dotnet/tools# add stuff to path# set -gx PATH $PATH $HOME/isomorphic_copy/bin# use temporary clipboard file# set -gx ISOCP_USE_FILE 1# set -gx DISPLAY FAKE
set -gx DENO_INSTALL /home/me/.denoset -gx PATH $PATH $DENO_INSTALL/bin
# enable vi supportset fish_cursor_default blockset fish_cursor_insert lineset fish_cursor_replace_one underscoreset fish_vi_force_cursor truefish_vi_key_bindings
# add homebrew to path (only if we are on linux)#switch (uname)# case Linux# eval (/home/linuxbrew/.linuxbrew/bin/brew shellenv)#end
# include user binaries in the shell pathfish_add_path ~/.local/bin
# add rust binaries to the shell pathfish_add_path ~/.cargo/bin
# add nanos ops binaries to the shell pathfish_add_path ~/.ops/bin
# ============================================================================# AC Emacs Management Commands# ============================================================================
# Emacs log directoryset -gx AC_EMACS_LOG_DIR /home/me/.emacs-logs
# Initialize emacs log directoryfunction ac-emacs-init-logs mkdir -p $AC_EMACS_LOG_DIRend
# Kill zombie emacs daemon and restart freshfunction ac-emacs-restart echo "🔄 Restarting emacs daemon..." ac-emacs-init-logs # Kill any existing emacs processes pkill -9 -f "emacs.*daemon" 2>/dev/null pkill -9 emacs 2>/dev/null pkill -9 emacsclient 2>/dev/null sleep 1 # Start fresh daemon with logging echo "🚀 Starting fresh emacs daemon..." set -l timestamp (date +%Y%m%d_%H%M%S) set -l log_file $AC_EMACS_LOG_DIR/daemon_$timestamp.log set -l config_path /home/me/aesthetic-computer/dotfiles/dot_config/emacs.el # Log startup info echo "=== Emacs Daemon Start ===" > $log_file echo "Timestamp: "(date -Iseconds) >> $log_file echo "Config: $config_path" >> $log_file echo "===========================" >> $log_file # Start daemon in background with timeout (don't use tee - it hangs) emacs -q --daemon -l $config_path >> $log_file 2>&1 & set -l daemon_pid $last_pid # Create/update symlink to latest log ln -sf $log_file $AC_EMACS_LOG_DIR/latest.log # Wait for daemon to be responsive (max 30 seconds) echo "⏳ Waiting for daemon..." set -l attempts 0 while test $attempts -lt 30 if timeout 2 emacsclient -e t >/dev/null 2>&1 echo "✅ Emacs daemon ready!" echo "📝 Logs: $log_file" # Start the crash monitor in background (if not already running) if not pgrep -f "ac-emacs-crash-monitor" >/dev/null 2>&1 fish -c "ac-emacs-crash-monitor" &>/dev/null & disown end
echo "" echo "💡 To connect UI: aesthetic-now (or ac-aesthetic)" return 0 end sleep 1 set attempts (math $attempts + 1) end echo "❌ Emacs daemon failed to start (timeout)" echo "📝 Check logs: $log_file" return 1end
# Full restart including UI reconnectionfunction ac-emacs-full-restart ac-emacs-restart and aesthetic-nowend
# Health check - verify emacs has correct config loadedfunction ac-emacs-health-check if not pgrep -f "emacs.*daemon" >/dev/null echo "❌ Emacs daemon not running" return 1 end if not timeout 3 emacsclient -e t >/dev/null 2>&1 echo "❌ Emacs daemon unresponsive" return 1 end # Check if our config functions are defined set -l has_backend (emacsclient -e '(fboundp (quote aesthetic-backend))' 2>/dev/null) if test "$has_backend" != "t" echo "❌ Emacs loaded wrong config (aesthetic-backend not defined)" echo " This usually means crash monitor restarted emacs incorrectly" echo " Run: ac-emacs-restart" return 1 end # Check if backend has run set -l backend_started (emacsclient -e 'ac--backend-started' 2>/dev/null) echo "✅ Emacs daemon healthy" echo " Config: aesthetic-backend defined" echo " Backend started: $backend_started" return 0end
# Crash diary viewer - live tail of crash log with pretty formattingfunction ac-crash-diary ac-emacs-init-logs set -l crash_log $AC_EMACS_LOG_DIR/crashes.log clear echo "" echo " ╔══════════════════════════════════════════════════════════════╗" echo " ║ 💥 EMACS CRASH DIARY ║" echo " ║ Log: $crash_log" echo " ╚══════════════════════════════════════════════════════════════╝" echo "" if not test -f $crash_log echo " 📭 No crashes recorded yet. That's good!" echo "" echo " Press Ctrl+C to exit, or wait for crashes to be logged..." echo "" # Create empty file and watch it touch $crash_log else # Show summary set -l crash_count (grep -c "EMACS CRASH DETECTED" $crash_log 2>/dev/null || echo 0) set -l restart_count (grep -c "Auto-restarting emacs" $crash_log 2>/dev/null || echo 0) set -l last_crash (grep "EMACS CRASH DETECTED" $crash_log 2>/dev/null | tail -1) echo " 📊 Summary:" echo " Total crashes: $crash_count" echo " Auto-restarts: $restart_count" echo "" echo " ────────────────────────────────────────────────────────────" echo "" end # Live tail with syntax highlighting tail -f $crash_log 2>/dev/null | while read -l line # Color code different types of lines if string match -q "*CRASH DETECTED*" "$line" set_color red --bold echo " $line" set_color normal else if string match -q "*Auto-restarting*" "$line" set_color yellow echo " $line" set_color normal else if string match -q "*✅*" "$line" set_color green echo " $line" set_color normal else if string match -q "*❌*" "$line" set_color red echo " $line" set_color normal else if string match -q "*===*" "$line" set_color cyan echo " $line" set_color normal else if string match -q "*---*" "$line" set_color brblack echo " $line" set_color normal else echo " $line" end endend
# Calculate health check timeout based on system load.# Under heavy load, the daemon may be slow to respond without being dead.function ac--health-check-timeout set -l load_1m (string split ' ' < /proc/loadavg)[1] set -l num_cpus (nproc 2>/dev/null; or echo 2) set -l load_ratio (math "$load_1m / $num_cpus") if test (math "$load_ratio > 2.0") -eq 1 echo 30 else if test (math "$load_ratio > 1.0") -eq 1 echo 20 else echo 10 endend
# Background crash monitor - detects when emacs dies unexpectedlyfunction ac-emacs-crash-monitor ac-emacs-init-logs set -l crash_log $AC_EMACS_LOG_DIR/crashes.log set -l check_interval 5 # Check every 5 seconds set -l max_restarts 5 # Max restarts before giving up set -l restart_count 0 set -l restart_window 300 # Reset restart count after 5 mins of stability set -l last_restart_time 0 # Record that we started monitoring echo "["(date -Iseconds)"] 🔍 Crash monitor started (auto-restart enabled)" >> $crash_log while true # Get current PID set -l daemon_pid (pgrep -f "emacs.*daemon" 2>/dev/null | head -1) if test -z "$daemon_pid" # Daemon is gone - log the crash echo "" >> $crash_log echo "============================================" >> $crash_log echo "🔴 EMACS CRASH DETECTED" >> $crash_log echo "============================================" >> $crash_log echo "Time: "(date -Iseconds) >> $crash_log echo "" >> $crash_log # Try to capture crash details echo "--- System State ---" >> $crash_log echo "Memory:" >> $crash_log free -h 2>/dev/null >> $crash_log echo "" >> $crash_log echo "Load:" >> $crash_log uptime >> $crash_log echo "" >> $crash_log # Check dmesg for OOM or signals echo "--- Kernel messages (last 30s) ---" >> $crash_log dmesg --time-format=iso 2>/dev/null | tail -30 | grep -iE "oom|kill|emacs|signal|memory|segfault" >> $crash_log echo "" >> $crash_log # Check journalctl for emacs-related messages echo "--- Journal (emacs) ---" >> $crash_log journalctl --since "1 minute ago" 2>/dev/null | grep -i emacs >> $crash_log echo "" >> $crash_log # Check if there's a core dump echo "--- Core dumps ---" >> $crash_log coredumpctl list 2>/dev/null | grep -i emacs | tail -3 >> $crash_log echo "" >> $crash_log # Check emacs daemon log if it exists set -l latest_log (ls -t $AC_EMACS_LOG_DIR/daemon_*.log 2>/dev/null | head -1) if test -n "$latest_log" echo "--- Last daemon log (last 50 lines) ---" >> $crash_log tail -50 "$latest_log" >> $crash_log end echo "============================================" >> $crash_log # Reset restart count if enough time has passed set -l now (date +%s) if test (math "$now - $last_restart_time") -gt $restart_window set restart_count 0 end # Check if we should auto-restart if test $restart_count -lt $max_restarts set restart_count (math "$restart_count + 1") set last_restart_time $now echo "["(date -Iseconds)"] 🔄 Auto-restarting emacs (attempt $restart_count/$max_restarts)..." >> $crash_log # Create warning file echo "Emacs crashed at "(date -Iseconds)", auto-restart #$restart_count" > /tmp/emacs-crash-warning
# Signal the aesthetic reconnect loop BEFORE killing emacsclient echo (date -Iseconds) > /tmp/emacs-crash-restart-signal
# Remove any stale startup lock from previous session rm -f /tmp/emacs-backend-startup.lock
# Clean up any zombie processes pkill -9 emacs 2>/dev/null pkill -9 emacsclient 2>/dev/null sleep 1 # Restart daemon with correct config (must use -q -l to load our config, not ~/.emacs) set -l config_path /home/me/aesthetic-computer/dotfiles/dot_config/emacs.el emacs -q --daemon -l $config_path 2>&1 | tee -a $crash_log # Wait for it to come up sleep 2 set -l new_pid (pgrep -f "emacs.*daemon" 2>/dev/null | head -1) if test -n "$new_pid" echo "["(date -Iseconds)"] ✅ Emacs daemon restarted (PID: $new_pid)" >> $crash_log # Auto-start aesthetic-backend after crash recovery # This runs in background so it doesn't block the monitor echo "["(date -Iseconds)"] 🚀 Starting aesthetic-backend..." >> $crash_log fish -c "sleep 3 && emacsclient -e '(aesthetic-backend)'" &>/dev/null & disown else echo "["(date -Iseconds)"] ❌ Failed to restart emacs daemon" >> $crash_log end else echo "["(date -Iseconds)"] ⛔ Max restarts ($max_restarts) reached, stopping monitor" >> $crash_log echo "Emacs crashed repeatedly - manual intervention needed" > /tmp/emacs-crash-warning break end else # Daemon is running - also check if it's responsive # BUT skip responsiveness check if aesthetic-backend is still starting up set -l startup_lock /tmp/emacs-backend-startup.lock if test -f $startup_lock # Check how old the lock file is (don't wait forever if it's stale) set -l lock_age (math (date +%s) - (stat -c %Y $startup_lock 2>/dev/null; or echo 0)) if test $lock_age -lt 120 # Lock valid for 2 minutes max # Startup in progress - skip responsiveness check, just log echo "["(date -Iseconds)"] ⏳ Startup lock active ($lock_age""s) - skipping responsiveness check" >> $crash_log else # Stale lock - remove it and check responsiveness echo "["(date -Iseconds)"] ⚠️ Stale startup lock ($lock_age""s) - removing" >> $crash_log rm -f $startup_lock # Use load-aware timeout to prevent false positives under heavy load set -l health_timeout (ac--health-check-timeout) if not timeout $health_timeout emacsclient -e t >/dev/null 2>&1 echo "["(date -Iseconds)"] ⚠️ Daemon unresponsive (PID: $daemon_pid, timeout: $health_timeout""s), forcing restart..." >> $crash_log echo (date -Iseconds) > /tmp/emacs-crash-restart-signal pkill -9 emacsclient 2>/dev/null sleep 0.2 pkill -9 -f "emacs.*daemon" 2>/dev/null end end else # No startup lock - normal responsiveness check with load-aware timeout. # Be gentler for a fresh daemon and require consecutive failures before kill. set -l health_timeout (ac--health-check-timeout) set -l required_failures 1 set -l daemon_age (ps -p $daemon_pid -o etimes= 2>/dev/null | string trim) if test -n "$daemon_age"; and test "$daemon_age" -lt 180 # Fresh daemons can be busy creating tabs/PTYs and miss one probe. set health_timeout (math "max($health_timeout, 30)") set required_failures 2 end if not timeout $health_timeout emacsclient -e t >/dev/null 2>&1 set -g ac_unresponsive_count (math (set -q ac_unresponsive_count; and echo $ac_unresponsive_count; or echo 0) + 1) echo "["(date -Iseconds)"] ⚠️ Daemon unresponsive (PID: $daemon_pid, timeout: $health_timeout""s, sample: $ac_unresponsive_count/$required_failures)" >> $crash_log if test $ac_unresponsive_count -ge $required_failures echo "["(date -Iseconds)"] ⚠️ Daemon unresponsive threshold reached, forcing restart..." >> $crash_log # Signal the aesthetic reconnect loop before killing echo (date -Iseconds) > /tmp/emacs-crash-restart-signal # Kill stale emacsclient processes BEFORE killing daemon # so they don't linger as frozen terminals pkill -9 emacsclient 2>/dev/null sleep 0.2 pkill -9 -f "emacs.*daemon" 2>/dev/null set -g ac_unresponsive_count 0 # Loop will detect it's gone and restart end else if set -q ac_unresponsive_count; and test $ac_unresponsive_count -gt 0 set -g ac_unresponsive_count 0 end # Daemon is responsive - also check CPU usage set -l cpu_usage (ps -p $daemon_pid -o %cpu= 2>/dev/null | string trim | string split '.')[1] if test -n "$cpu_usage"; and test "$cpu_usage" -gt 85 set -g ac_high_cpu_count (math (set -q ac_high_cpu_count; and echo $ac_high_cpu_count; or echo 0) + 1) if test $ac_high_cpu_count -ge 3 echo "["(date -Iseconds)"] ⚠️ Sustained high CPU ($cpu_usage%) for 3 checks, forcing restart..." >> $crash_log echo (date -Iseconds) > /tmp/emacs-crash-restart-signal pkill -9 emacsclient 2>/dev/null sleep 0.2 pkill -9 -f "emacs.*daemon" 2>/dev/null set -g ac_high_cpu_count 0 else echo "["(date -Iseconds)"] ⚠️ High CPU ($cpu_usage%) sample $ac_high_cpu_count/3" >> $crash_log end else if set -q ac_high_cpu_count; and test $ac_high_cpu_count -gt 0 set -g ac_high_cpu_count 0 end end end end sleep $check_interval endend
# Kill emacs entirely (daemon + any clients)function ac-emacs-kill echo "💀 Killing all emacs processes..." # Also kill the crash monitor pkill -f "ac-emacs-crash-monitor" 2>/dev/null pkill -9 -f "emacs.*daemon" 2>/dev/null pkill -9 emacs 2>/dev/null pkill -9 emacsclient 2>/dev/null echo "✅ All emacs processes killed"end
# Check emacs daemon statusfunction ac-emacs-status set -l daemon_pid (pgrep -f "emacs.*daemon" 2>/dev/null) if test -n "$daemon_pid" echo "🟢 Emacs daemon running (PID: $daemon_pid)" if timeout 3 emacsclient -e t >/dev/null 2>&1 echo "✅ Daemon is responsive" else echo "⚠️ Daemon process exists but NOT responsive (zombie)" end else echo "🔴 Emacs daemon not running" # Check for crash warning if test -f /tmp/emacs-crash-warning echo "⚠️ Last crash: "(cat /tmp/emacs-crash-warning) end end # Check crash monitor status set -l monitor_pid (pgrep -f "ac-emacs-crash-monitor" 2>/dev/null) if test -n "$monitor_pid" echo "🔍 Crash monitor active (PID: $monitor_pid)" else echo "⚠️ Crash monitor not running" endend
# Start the crash monitor manuallyfunction ac-emacs-start-monitor if pgrep -f "ac-emacs-crash-monitor" >/dev/null 2>&1 echo "Crash monitor already running" return 0 end if not pgrep -f "emacs.*daemon" >/dev/null 2>&1 echo "❌ No emacs daemon to monitor" return 1 end echo "🔍 Starting crash monitor..." fish -c "ac-emacs-crash-monitor" &>/dev/null & disown echo "✅ Crash monitor started"end
# View emacs crash logsfunction ac-emacs-logs ac-emacs-init-logs if test "$argv[1]" = "crashes" if test -f $AC_EMACS_LOG_DIR/crashes.log bat $AC_EMACS_LOG_DIR/crashes.log 2>/dev/null || cat $AC_EMACS_LOG_DIR/crashes.log else echo "No crash logs found" end else if test "$argv[1]" = "latest" if test -f $AC_EMACS_LOG_DIR/latest.log bat $AC_EMACS_LOG_DIR/latest.log 2>/dev/null || cat $AC_EMACS_LOG_DIR/latest.log else echo "No daemon logs found" end else if test "$argv[1]" = "tail" if test -f $AC_EMACS_LOG_DIR/crashes.log tail -f $AC_EMACS_LOG_DIR/crashes.log else echo "No crash logs found" end else if test "$argv[1]" = "list" echo "📁 Emacs logs in $AC_EMACS_LOG_DIR:" ls -lth $AC_EMACS_LOG_DIR/ 2>/dev/null || echo "No logs directory" else echo "Usage: ac-emacs-logs [crashes|latest|tail|list]" echo "" echo " crashes - View crash history" echo " latest - View latest daemon startup log" echo " tail - Follow crash log in real-time" echo " list - List all log files" endend
# Clear the crash warningfunction ac-emacs-ack rm -f /tmp/emacs-crash-warning echo "✅ Crash warning cleared"end
# === SIXEL IMAGE DISPLAY ===
# Display an image (URL or local path) as sixel in the emacs terminal# Usage: ac-pix <url-or-path> [size]# Examples:# ac-pix https://example.com/image.png# ac-pix /tmp/my-image.jpg# ac-pix https://example.com/image.png 80 (80 pixels wide)function ac-pix if test (count $argv) -lt 1 echo "Usage: ac-pix <url-or-path> [size]" echo "" echo "Display an image as sixel graphics in the emacs terminal." echo "" echo "Arguments:" echo " url-or-path URL (http/https/ipfs) or local file path" echo " size Max pixel dimension (default: 60)" echo "" echo "Examples:" echo " ac-pix https://example.com/photo.png" echo " ac-pix /tmp/thumbnail.jpg 100" echo " ac-pix ipfs://Qm... 40" return 1 end
set -l input $argv[1] set -l size 60 if test (count $argv) -ge 2 set size $argv[2] end
# Find emacsclient PTY for direct sixel output set -l pty (pgrep -f 'emacsclient.*-nw' | head -1 | xargs -I{} readlink /proc/{}/fd/0 2>/dev/null) if test -z "$pty" echo "❌ No emacsclient terminal found" return 1 end
set -l tmpfile /tmp/ac-pix-(date +%s%N).img
# Handle different input types if string match -rq '^https?://' -- $input # HTTP/HTTPS URL echo "📥 Fetching $input..." if not curl -sL --max-time 15 -o $tmpfile $input echo "❌ Failed to download image" return 1 end else if string match -rq '^ipfs://' -- $input # IPFS URL - try multiple gateways set -l cid (string replace 'ipfs://' '' $input) set -l gateways \ "https://cloudflare-ipfs.com/ipfs/" \ "https://ipfs.io/ipfs/" \ "https://gateway.pinata.cloud/ipfs/" \ "https://w3s.link/ipfs/" set -l downloaded 0 for gateway in $gateways echo "📥 Trying $gateway..." if curl -sL --max-time 10 -o $tmpfile "$gateway$cid" 2>/dev/null if test -s $tmpfile set downloaded 1 break end end end if test $downloaded -eq 0 echo "❌ Failed to fetch from IPFS" return 1 end else # Local file path if not test -f $input echo "❌ File not found: $input" return 1 end set tmpfile $input end
# Verify file has content if not test -s $tmpfile echo "❌ Empty or invalid image" test "$tmpfile" != "$input"; and rm -f $tmpfile return 1 end
# Convert to sixel and write directly to emacsclient's terminal # Note: [0] is ImageMagick syntax for first frame, needs quoting to avoid fish array interpretation set -l sixel (/usr/bin/magick "$tmpfile"'[0]' -resize "$size"x"$size>" sixel:- 2>/dev/null) if test -z "$sixel" echo "❌ Failed to convert image to sixel" test "$tmpfile" != "$input"; and rm -f $tmpfile return 1 end
# Clean up temp file if we created one test "$tmpfile" != "$input"; and rm -f $tmpfile
# Output newline + sixel directly to emacsclient's PTY printf '\n%s\n' $sixel > $pty echo "" # Add space after sixel in eatend
# Full aesthetic platform restart (emacs + reconnect artery)function ac-restart echo "🔄 Full aesthetic platform restart..." ac-emacs-restart if test $status -eq 0 echo "🩸 Reconnecting to artery..." emacsclient -nw -c --eval '(aesthetic-backend (quote "artery"))' endend
# === EMACS TAB TESTING & DIAGNOSTICS ===
# Run emacs tab unit testsfunction ac-test-tabs /workspaces/aesthetic-computer/scripts/test-emacs-tabs.fish $argvend
# Quick diagnose current daemon statefunction ac-diagnose if timeout 3 emacsclient -e '(ac-diagnose-all)' >/dev/null 2>&1 echo "✅ Diagnostics logged to .emacs-logs/emacs-debug.log" echo "" tail -30 /workspaces/aesthetic-computer/.emacs-logs/emacs-debug.log | grep -E "DIAGNOSE|BUFFER|TIMER|PROC" else echo "❌ Daemon not responsive - cannot run diagnostics" endend
# Start emacs CPU profilerfunction ac-profile-start if emacsclient -e '(ac-profile-start)' >/dev/null 2>&1 echo "🔬 CPU profiler started" else echo "❌ Could not start profiler - daemon not responsive?" endend
# Stop profiler and show summaryfunction ac-profile-stop if emacsclient -e '(ac-profile-stop)' >/dev/null 2>&1 echo "🔬 Profiler stopped - report in .emacs-logs/emacs-profile.log" echo "" if test -f /workspaces/aesthetic-computer/.emacs-logs/emacs-profile.log tail -50 /workspaces/aesthetic-computer/.emacs-logs/emacs-profile.log end else echo "❌ Could not stop profiler - daemon not responsive?" endend
# Show emacs profiler report interactivelyfunction ac-profile-report emacsclient -e '(ac-profile-report)'end
# Watch daemon CPU in real-timefunction ac-watch-cpu echo "Watching emacs CPU usage (Ctrl+C to stop)..." while true set cpu (ps aux | grep "emacs.*daemon" | grep -v grep | awk '{print $3}') set mem (ps aux | grep "emacs.*daemon" | grep -v grep | awk '{print $4}') set responsive "?" if timeout 1 emacsclient -e t >/dev/null 2>&1 set responsive "✓" else set responsive "✗" end echo (date '+%H:%M:%S')" CPU: $cpu% MEM: $mem% Responsive: $responsive" sleep 2 endend
# Start/restart CDP tunnel for VS Code controlfunction ac-cdp-tunnel echo "🩸 Managing CDP tunnel..." # Kill any existing tunnel set -l existing (pgrep -f "ssh.*9333.*(host.docker.internal|172.17.0.1)" 2>/dev/null) if test -n "$existing" echo " Killing existing tunnel (PID: $existing)..." kill $existing 2>/dev/null sleep 1 end # Determine host address - try host.docker.internal first, fallback to gateway set -l host_addr "host.docker.internal" if not getent hosts host.docker.internal >/dev/null 2>&1 # Use Docker gateway IP as fallback set host_addr "172.17.0.1" end # Start new tunnel echo " Starting new CDP tunnel to $host_addr..." ssh -f -N -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=5 -L 9333:127.0.0.1:9333 me@$host_addr 2>/dev/null sleep 1 # Verify if nc -zv localhost 9333 2>&1 | grep -q "Connected" echo "✅ CDP tunnel online (localhost:9333 → $host_addr:9333)" return 0 else echo "❌ CDP tunnel failed - is VS Code running on host with --remote-debugging-port=9333?" return 1 endend
# Check CDP tunnel statusfunction ac-cdp-status set -l tunnel_pid (pgrep -f "ssh.*9333" 2>/dev/null) if test -n "$tunnel_pid" echo "🟢 CDP tunnel process running (PID: $tunnel_pid)" if nc -zv localhost 9333 2>&1 | grep -q "Connected" echo "✅ Port 9333 is accessible" else echo "⚠️ Port 9333 not accessible (tunnel may be broken)" end else echo "🔴 CDP tunnel not running" endend
# Assume the daemon is running when entering emacs.
# For fast config reloading - simple approachfunction platform emacsclient -nw -c --eval '(aesthetic-backend (quote "artery"))'end
function ensure-emacs-daemon-ready set -l timeout 240 set -l silent 0 set -l force_restart 0 argparse 't/timeout=' 's/silent' 'f/force' -- $argv or return 1
if set -q _flag_timeout set timeout $_flag_timeout end if set -q _flag_silent set silent 1 end if set -q _flag_force set force_restart 1 end
# Initialize logging ac-emacs-init-logs set -l timestamp (date +%Y%m%d_%H%M%S) set -l log_file $AC_EMACS_LOG_DIR/daemon_$timestamp.log set -l config_path /home/me/aesthetic-computer/dotfiles/dot_config/emacs.el set -l wait_step 2 set -l next_report 10
if test $force_restart -eq 1 if test $silent -ne 1 echo "♻️ Forcing emacs daemon restart..." end pkill -f "emacs.*daemon" 2>/dev/null sleep 2 else if pgrep -f "emacs.*daemon" >/dev/null if timeout 3 emacsclient -e t >/dev/null 2>&1 # Verify the correct config is loaded (not a bare emacs --daemon) set -l has_config (timeout 3 emacsclient -e '(fboundp (quote aesthetic-backend))' 2>/dev/null) if test "$has_config" = "t" if test $silent -ne 1 echo "✅ Emacs daemon already running with correct config" end return 0 else if test $silent -ne 1 echo "⚠️ Emacs daemon running but wrong config loaded, restarting..." end pkill -f "emacs.*daemon" 2>/dev/null sleep 2 end else if test $silent -ne 1 echo "⚠️ Emacs daemon process unresponsive, restarting..." end pkill -f "emacs.*daemon" 2>/dev/null sleep 2 end end
if not test -f $config_path echo "❌ Emacs configuration not found at $config_path" return 1 end
if test $silent -ne 1 echo "🚀 Bootstrapping emacs daemon (timeout: $timeout s)" echo "📜 Logs: $log_file" end
# Log startup info echo "=== Emacs Daemon Start ===" > $log_file echo "Timestamp: "(date -Iseconds) >> $log_file echo "Config: $config_path" >> $log_file echo "===========================" >> $log_file # Start daemon with output to log file command emacs -q --daemon -l $config_path >> $log_file 2>&1 & set -l daemon_pid $last_pid if test -z "$daemon_pid" set daemon_pid (jobs -l | tail -n1 | awk '{print $2}') end echo "PID: $daemon_pid" >> $log_file # Create symlink to latest log ln -sf $log_file $AC_EMACS_LOG_DIR/latest.log
set -l elapsed 0 while test $elapsed -lt $timeout if timeout 3 emacsclient -e t >/dev/null 2>&1 if test $silent -ne 1 echo "✅ Emacs daemon is ready!" end # Start crash monitor in background (if not already running) if not pgrep -f "ac-emacs-crash-monitor" >/dev/null 2>&1 fish -c "ac-emacs-crash-monitor" &>/dev/null & disown %last 2>/dev/null end echo "[DEBUG] ensure-emacs-daemon-ready returning 0" return 0 end
if test -n "$daemon_pid" if not kill -0 $daemon_pid 2>/dev/null if test $silent -ne 1 echo "❌ Emacs daemon exited unexpectedly. Check $log_file for details." if test -f $log_file tail -n 20 $log_file end end return 1 end end
sleep $wait_step set elapsed (math "$elapsed + $wait_step")
if test $silent -ne 1 if test $elapsed -ge $next_report echo "⏳ Waiting for emacs daemon... ($elapsed/$timeout s)" if test -f $log_file tail -n 5 $log_file end set next_report (math "$next_report + 10") end end end
if test $silent -ne 1 echo "❌ Emacs daemon did not become ready within $timeout seconds." if test -f $log_file tail -n 20 $log_file end end
return 1end
# ⏲️ Wait on `entry.fish` to touch the `.waiter` file.
function aesthetic # Parse flags: --force (full kill+restart), --no-wait (skip .waiter) set -l force 0 set -l no_wait 0 for arg in $argv switch $arg case "--force" set force 1 case "--no-wait" set no_wait 1 end end
# Only kill if --force is passed. Otherwise ensure-emacs-daemon-ready # will reuse a healthy daemon or restart an unhealthy one. if test $force -eq 1 echo "🔄 Force restart: killing existing emacs..." ac-emacs-kill end
# Check if --no-wait flag is passed if test $no_wait -eq 1 echo "Skipping wait for .waiter file..." else # Check if entry.fish already completed (container is ready) set -l skip_wait 0 if test -f /home/me/.waiter echo "✅ Container already configured, skipping wait..." set skip_wait 1 end if test $skip_wait -eq 0 # Docker not up or emacs not ready - wait for waiter file clear set -l config_count 0 set -l last_log_hash "" set -l entry_log /tmp/entry-fish.log while not test -f /home/me/.waiter # Only redraw if log content changed (reduces flicker) set -l current_log_hash "" if test -f $entry_log set current_log_hash (tail -n 12 $entry_log 2>/dev/null | md5sum 2>/dev/null | cut -d' ' -f1) end if test "$current_log_hash" != "$last_log_hash" -o $config_count -eq 0 -o (math "$config_count % 10") -eq 0 clear # Show banner (static, no animation to reduce flicker) toilet "Configuring..." -f future | lolcat -f # Show entry.fish progress if log exists if test -f $entry_log set -l log_lines (wc -l < $entry_log 2>/dev/null | string trim) if test -n "$log_lines" -a "$log_lines" -gt 0 echo "" echo "─────────────────────────────────────────" tail -n 12 $entry_log 2>/dev/null echo "─────────────────────────────────────────" end else echo "" echo "📄 Waiting for entry log at $entry_log" end echo "📄 Log: $entry_log (latest: /home/me/.entry-logs/latest.log)" echo "⏱️ Waited: $config_count s" set last_log_hash $current_log_hash end sleep 1 set config_count (math $config_count + 1) end sudo rm /home/me/.waiter 2>/dev/null end end echo "[DEBUG] About to call ensure-emacs-daemon-ready..." if not ensure-emacs-daemon-ready --timeout=360 echo "❌ Cannot connect to emacs daemon. Please check your emacs configuration." return 1 end echo "[DEBUG] ensure-emacs-daemon-ready succeeded!" echo "[DEBUG] TTY: "(tty 2>&1) echo "[DEBUG] TERM: $TERM" # Connect to emacs and run aesthetic-backend to create all tabs # Auto-reconnect loop: if the crash monitor restarts the daemon, # this will detect the signal file and reconnect automatically. set -l reconnect_max 5 set -l reconnect_count 0 while true echo "🚀 Connecting to aesthetic platform..." emacsclient -nw -c --eval '(aesthetic-backend (quote "artery"))' set -l exit_code $status
# Check if crash monitor triggered a restart (signal file) if test -f /tmp/emacs-crash-restart-signal rm -f /tmp/emacs-crash-restart-signal set reconnect_count (math "$reconnect_count + 1") if test $reconnect_count -ge $reconnect_max echo "" echo "⛔ Too many crash-restarts ($reconnect_max), giving up auto-reconnect." echo "💡 Run: aesthetic-now" return 1 end echo "" echo "🔄 Daemon was crash-restarted, reconnecting ($reconnect_count/$reconnect_max)..." # Wait for daemon to be ready if not ensure-emacs-daemon-ready --timeout=60 echo "❌ Daemon didn't come back after crash restart." return 1 end continue end
# Normal exit (user quit emacs, or unrelated error) if test $exit_code -ne 0 echo "" echo "⚠️ emacsclient exited with code $exit_code" echo "💡 If the '💻 Aesthetic' VS Code task is frozen, restart it from the Task menu." echo " Or run: ac-emacs-restart && then restart the task" end return $exit_code endend
# Convenience alias for skipping the waitfunction aesthetic-now aesthetic --no-waitend
# Direct aesthetic function that skips waiting entirelyfunction aesthetic-direct echo "🚀 Starting aesthetic directly (no wait)..."
if not ensure-emacs-daemon-ready --timeout=300 --silent echo "❌ Cannot connect to emacs daemon." return 1 end
# Connect to emacs with aesthetic-backend (with crash-restart reconnect) set -l reconnect_max 5 set -l reconnect_count 0 while true emacsclient -nw -c --eval '(aesthetic-backend (quote "artery"))' set -l exit_code $status if test -f /tmp/emacs-crash-restart-signal rm -f /tmp/emacs-crash-restart-signal set reconnect_count (math "$reconnect_count + 1") if test $reconnect_count -ge $reconnect_max echo "⛔ Too many crash-restarts, giving up." return 1 end echo "🔄 Daemon crash-restarted, reconnecting ($reconnect_count/$reconnect_max)..." if not ensure-emacs-daemon-ready --timeout=60 --silent echo "❌ Daemon didn't come back." return 1 end continue end return $exit_code endend
# TODO: Automatically kill online mode and go to offline mode if necessary.
# Helper function to check emacs daemon statusfunction check-daemon if pgrep -f "emacs.*daemon" >/dev/null echo "✅ Emacs daemon process found" if emacsclient -e t >/dev/null 2>&1 echo "✅ Emacs daemon is responsive" else echo "❌ Emacs daemon found but not responsive" end else echo "❌ No emacs daemon process found" endend
# Helper function to restart emacs daemonfunction restart-daemon echo "🔄 Restarting emacs daemon..." if ensure-emacs-daemon-ready --force --timeout=360 check-daemon else echo "❌ Failed to restart emacs daemon." check-daemon endend
function ac-site # ac-site now runs lith (monolith) instead of netlify dev ac-lithend
function ac-lith cd ~/aesthetic-computer/lith echo "🪨 Starting lith (monolith server)..." echo "🔍 Cleaning up any stuck processes..." pkill -f "lith/server.mjs" 2>/dev/null sleep 1 echo "🔌 Killing port 8888..." timeout 5 npx kill-port 8888 2>/dev/null; or true # Load env vars from system/.env if present if test -f ~/aesthetic-computer/system/.env for line in (cat ~/aesthetic-computer/system/.env | grep -v '^#' | grep -v '^$' | grep '=') set -l parts (string split -m1 '=' $line) if test (count $parts) -ge 2 set -gx $parts[1] $parts[2] end end end echo "🚀 Starting server on https://localhost:8888..." node --watch server.mjsend
function ac-deploy --description 'Deploy lith: push + pull + restart on droplet' cd ~/aesthetic-computer set LITH_IP "209.38.133.33"
echo "🪨 Deploying to lith ($LITH_IP)..."
# Push local changes if any set -l status_output (git status --porcelain) if test -n "$status_output" echo "⚠️ You have uncommitted changes. Commit first." return 1 end
echo "📤 Pushing to origin..." git push origin main; or begin; echo "❌ Push failed"; return 1; end
echo "🔄 Pulling on droplet..." ssh -o StrictHostKeyChecking=no root@$LITH_IP "cd /opt/ac && git pull origin main && git rev-parse --short HEAD > system/public/.commit-ref && cat system/public/.commit-ref"
echo "🔁 Restarting lith..." ssh root@$LITH_IP "systemctl restart lith"
sleep 3 set -l version (curl -s "https://aesthetic.computer/api/version" | python3 -c "import json,sys; print(json.load(sys.stdin)['deployed'])" 2>/dev/null) echo "✅ Deployed: $version"end
function ac-media cd ~/aesthetic-computer/system echo "📦 Starting Caddy media server on :8111..." npm run media-server-caddyend
# ac - Smart command: cd with no args, jump to piece with args function ac --description 'cd to aesthetic-computer or jump to piece' if test (count $argv) -eq 0 cd ~/aesthetic-computer else set piece_path $argv[1] echo "🎯 Jumping to: $piece_path" set response (curl -s -k -X POST https://localhost:8889/jump -H "Content-Type: application/json" -d "{\"piece\": \"$piece_path\"}") # Show appropriate feedback based on connection status if string match -q "*vscodeConnected*true*" $response echo "✅ Sent to VSCode extension" else if string match -q "*Jump request sent*" $response echo "✅ Sent to browser clients" else echo "$response" end endend
function ac-help --description "List available ac-* commands" set -l filter $argv[1] set -l names (functions -n | string match -r '^ac(-|$)' | string match -v 'ac--*' | sort -u)
if test -n "$filter" set names (printf "%s\n" $names | string match -i "*$filter*") end
if test (count $names) -eq 0 echo "No ac-* commands match '$filter'" return 1 end
echo "AC commands ("(count $names)"):" for name in $names echo " $name" endend
function ac-offline echo "🐭 Starting offline mode..." ac cd system/public cp system/offline-index.html system/public/index.html npx http-server -p 8888 -c-1 -g -b -S -C ../../ssl-dev/localhost.pem -K ../../ssl-dev/localhost-key.pemend
function ac-url clear ac npm run -s url $argvend
# 🖼️ Extension views dev server (3D process tree viz at localhost:5555)function ac-views clear cd ~/aesthetic-computer/vscode-extension echo "🖼️ Starting Extension Views Dev Server on http://localhost:5555" echo " Open dev.html in browser or toggle local mode in VS Code extension" npx --yes serve views -l 5555end
alias ac-watch 'ac; npm run watch'
# 📱 Dev log monitoring function with dynamic file detectionfunction ac-dev-log set log_dir "/tmp/dev-logs" if not test -d $log_dir echo "🔍 No dev logs directory found at $log_dir" echo "💡 Try touching/drawing on your iPhone to generate logs" return 1 end echo "📱 Monitoring ALL device logs in real-time..." echo "🎨 Touch/draw on any device to see live logs..." echo "🔄 Watching for new files in: $log_dir" echo "----------------------------------------" # Kill any existing tail/inotify processes for clean start pkill -f "tail.*$log_dir" 2>/dev/null pkill -f "inotifywait.*$log_dir" 2>/dev/null # Function to start monitoring a single log file function monitor_log_file set logfile $argv[1] set log_name (basename "$logfile" .log) tail -f "$logfile" 2>/dev/null | while read line # Extract JSON part (everything after first '{') set json_start (string match -r '\{.*' "$line") if test -n "$json_start" # Try to format with jq for colors set formatted (echo "$json_start" | jq -C . 2>/dev/null) if test $status -eq 0 # Extract timestamp/prefix part set prefix (string replace -r '\{.*' '' "$line") echo "📱 $log_name: $prefix$formatted" else echo "📱 $log_name: $line" end else echo "📱 $log_name: $line" end end & end # Monitor existing log files for logfile in $log_dir/*.log if test -f "$logfile" echo "🔍 Found existing log: "(basename "$logfile") monitor_log_file "$logfile" end end # Monitor for new log files being created (fallback if inotify not available) if command -v inotifywait >/dev/null 2>&1 echo "�️ Using inotifywait for new file detection" inotifywait -m -e create -e moved_to --format '%w%f' "$log_dir" 2>/dev/null | while read new_file if string match -q "*.log" "$new_file" echo "🆕 New log file detected: "(basename "$new_file") monitor_log_file "$new_file" end end & else echo "⏰ Using polling for new file detection (inotify not available)" # Fallback: poll for new files every 2 seconds while true for logfile in $log_dir/*.log if test -f "$logfile" set log_name (basename "$logfile" .log) # Check if we're already monitoring this file if not pgrep -f "tail.*$logfile" >/dev/null echo "🆕 New log file detected: $log_name" monitor_log_file "$logfile" end end end sleep 2 end & end # Keep the function running with proper interrupt handling echo "✅ Monitoring started. Press Ctrl+C to stop." # Use read to wait for interrupt (Ctrl+C) - this is more reliable in Fish # The read command will be interrupted by Ctrl+C and return control echo "Press any key to stop monitoring, or Ctrl+C..." read -s # Cleanup on exit echo "🛑 Stopping all monitoring processes..." pkill -f "tail.*$log_dir" 2>/dev/null pkill -f "inotifywait.*$log_dir" 2>/dev/null echo "✅ Monitoring stopped"end
# 📱 List all available device logsfunction ac-dev-logs set log_dir "/tmp/dev-logs" if not test -d $log_dir echo "🔍 No dev logs directory found" return 1 end echo "📱 Available device logs:" ls -la $log_dir/*.log 2>/dev/null | while read line echo " $line" endend
# 📱 Clean old device logsfunction ac-dev-log-clean set log_dir "/tmp/dev-logs" if test -d $log_dir echo "🧹 Cleaning old device logs..." rm -f $log_dir/*.log echo "✨ Done!" else echo "🔍 No dev logs directory found" endend
# 📱 Improved dev log monitoring with proper Ctrl+C support (test version)function ac-dev-log-new set log_dir "/tmp/dev-logs" if not test -d $log_dir echo "🔍 No dev logs directory found at $log_dir" echo "💡 Try touching/drawing on your iPhone to generate logs" return 1 end echo "📱 Monitoring ALL device logs in real-time..." echo "🎨 Touch/draw on any device to see live logs..." echo "🔄 Watching for new files in: $log_dir" echo "✅ Press Ctrl+C to stop monitoring" echo "----------------------------------------" # Kill any existing monitoring processes for clean start pkill -f "tail.*$log_dir" 2>/dev/null pkill -f "inotifywait.*$log_dir" 2>/dev/null # Trap Ctrl+C to clean up properly function cleanup_logs --on-signal INT echo "" echo "🛑 Stopping log monitoring..." pkill -f "tail.*$log_dir" 2>/dev/null pkill -f "inotifywait.*$log_dir" 2>/dev/null echo "✨ Monitoring stopped!" return 0 end # Function to start monitoring a single log file function start_monitor set logfile $argv[1] set log_name (basename "$logfile" .log) tail -f "$logfile" 2>/dev/null | while read line # Extract JSON part (everything after first '{') set json_start (string match -r '\{.*' "$line") if test -n "$json_start" # Try to format with jq for colors set formatted (echo "$json_start" | jq -C . 2>/dev/null) if test $status -eq 0 # Extract timestamp/prefix part set prefix (string replace -r '\{.*' '' "$line") echo "📱 $log_name: $prefix$formatted" else echo "📱 $log_name: $line" end else echo "📱 $log_name: $line" end end & end # Start monitoring existing files set -l monitored_files for logfile in $log_dir/*.log if test -f "$logfile" echo "🔍 Found existing log: "(basename "$logfile") start_monitor "$logfile" set -a monitored_files "$logfile" end end # Simple loop that responds quickly to Ctrl+C if command -v inotifywait >/dev/null 2>&1 echo "👁️ Using inotifywait for new file detection" # Watch for new files inotifywait -m -e create -e moved_to --format '%w%f' "$log_dir" 2>/dev/null & set inotify_pid $last_pid while true # Read from inotify if available if jobs -q %$inotify_pid # Process new files... # (simplified for testing) end # Check for new files manually as backup for logfile in $log_dir/*.log if test -f "$logfile"; and not contains "$logfile" $monitored_files set log_name (basename "$logfile" .log) echo "🆕 New log file detected: $log_name" start_monitor "$logfile" set -a monitored_files "$logfile" end end sleep 1 end else echo "⏰ Using polling for file detection" while true # Check for new files for logfile in $log_dir/*.log if test -f "$logfile"; and not contains "$logfile" $monitored_files set log_name (basename "$logfile" .log) echo "🆕 New log file detected: $log_name" start_monitor "$logfile" set -a monitored_files "$logfile" end end sleep 2 end endend# 📱 Simple improved dev log monitoring (test version)function ac-dev-log-simple set log_dir "/tmp/dev-logs" if not test -d $log_dir echo "🔍 No dev logs directory found at $log_dir" return 1 end echo "📱 Monitoring device logs..." echo "✅ Press Ctrl+C to stop" echo "----------------------------------------" # Kill existing processes pkill -f "tail.*$log_dir" 2>/dev/null # Simple approach - just monitor all existing files for logfile in $log_dir/*.log if test -f "$logfile" set log_name (basename "$logfile" .log) echo "🔍 Monitoring: $log_name" tail -f "$logfile" | while read line echo "📱 $log_name: $line" end & end end # Simple loop that can be interrupted echo "🔄 Running... (Ctrl+C to stop)" while true sleep 1 endend# alias ac-kidlisp 'ac; npm run test:kidlisp'# Session server (simplified - no Emacs special handling)function ac-session echo "🎮 Starting session server..." ac cd session-server echo "🔍 Cleaning up any stuck processes..." pkill -f "nodemon.*session.mjs" 2>/dev/null sleep 1 npx kill-port 8889 2>/dev/null echo "🚀 Starting session server on port 8889..." PORT=8889 NODE_ENV=development npx nodemon -I --watch session.mjs session.mjsend
# Oven service (tape video processing)function ac-oven echo "🔥 Starting oven service..." ac cd oven echo "🔍 Cleaning up any stuck processes..." pkill -f "node.*server.mjs" 2>/dev/null sleep 1 npx kill-port 3002 2>/dev/null echo "🚀 Starting oven server on https://localhost:3002..." npm run devend
# Silo service (data & storage dashboard)function ac-silo echo "🏗️ Starting silo service..." ac cd silo
echo "🔍 Cleaning up any stuck processes..." pkill -f "node.*silo/server.mjs" 2>/dev/null sleep 1 npx kill-port 3003 2>/dev/null
# SSH tunnel to DO droplet MongoDB (localhost:27018 → droplet:27017) if not pgrep -f "ssh.*27018:localhost:27017.*64.23.151.169" >/dev/null 2>&1 echo "🔗 Setting up SSH tunnel to DO droplet MongoDB..." ssh -fN -L 27018:localhost:27017 -i ~/.ssh/silo-deploy-key root@64.23.151.169 2>/dev/null and echo " tunnel established (localhost:27018)" or echo " tunnel failed (check SSH key)" else echo "🔗 SSH tunnel already active (localhost:27018)" end
echo "🚀 Starting silo on https://localhost:3003..." npm run devend
alias ac-stripe-print 'ac; npm run stripe-print-micro'alias ac-stripe-ticket 'ac; npm run stripe-ticket-micro'alias ac-extension 'ac; cd vscode-extension; npm run build; ac'
# KidLisp command entrypoint.# Primary path: public `kidlisp` CLI (when installed).# Fallback path: local repo scripts (until CLI implementation fully lands).function ac-kidlisp set -l cmd $argv[1] test -z "$cmd"; and set cmd "test"
if command -q kidlisp switch $cmd case source-tree st tree __ac_kidlisp_cli tree $argv[2..-1] case watch __ac_kidlisp_cli test --watch $argv[2..-1] case test direct __ac_kidlisp_cli test $argv[2..-1] case help --help -h __ac_kidlisp_cli --help case '*' __ac_kidlisp_cli $argv end return $status end
ac switch $cmd case source-tree st tree __ac_kidlisp_source_tree $argv[2..-1] case watch echo "🔍 Running kidlisp tests in watch mode..." npm run test:kidlisp -- $argv[2..-1] case test direct echo "🚀 Running kidlisp tests directly..." npm run test:kidlisp:direct -- $argv[2..-1] case help --help -h echo "kidlisp CLI not installed; using local fallback." echo "Usage:" echo " ac-kidlisp test" echo " ac-kidlisp watch" echo " ac-kidlisp source-tree <piece>" echo "" echo "Install CLI for full command parity: https://kidlisp.com/install" case '*' if test (count $argv) -gt 0 # Compatibility path: treat bare value as piece reference. __ac_kidlisp_source_tree $argv else echo "Usage: ac-kidlisp [test|watch|source-tree <piece>]" end endend
# Pipe command output to session server build stream (for live build progress)# Usage: some-command | ac-pipefunction ac-pipe while read -l line # Escape double quotes in the line for JSON set escaped_line (string replace -a '"' '\"' -- $line) /usr/bin/curl -s -X POST https://session-server.aesthetic.computer/build-stream --header "Content-Type: application/json" --data "{\"line\": \"$escaped_line\"}" > /dev/null 2>&1 echo $line # Also echo to terminal endend
# ATProto PDS Admin - SSH into PDS server and run pdsadminfunction ac-at # ASCII art header with colors set_color cyan echo " █████╗ ████████╗██████╗ ██████╗ ██████╗ ████████╗ ██████╗" echo "██╔══██╗╚══██╔══╝██╔══██╗██╔══██╗██╔═══██╗╚══██╔══╝██╔═══██╗" echo "███████║ ██║ ██████╔╝██████╔╝██║ ██║ ██║ ██║ ██║" echo "██╔══██║ ██║ ██╔═══╝ ██╔══██╗██║ ██║ ██║ ██║ ██║" echo "██║ ██║ ██║ ██║ ██║ ██║╚██████╔╝ ██║ ╚██████╔╝" echo "╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝" set_color blue echo " Personal Data Server - aesthetic.computer" set_color cyan echo "🕷️ at.aesthetic.computer 💾 165.227.120.137" echo "📦 at-blobs-aesthetic-computer 🦋 ATProto Federation" set_color normal echo "" if test (count $argv) -eq 0 # No arguments: drop into interactive shell ssh -o StrictHostKeyChecking=no -i ~/.ssh/aesthetic_pds root@165.227.120.137 else # With arguments: run pdsadmin command directly ssh -o StrictHostKeyChecking=no -i ~/.ssh/aesthetic_pds root@165.227.120.137 "pdsadmin $argv" endend
# Backward compatibility aliasalias ac-pds='ac-at'
# Agent memory helpers (ac-prefixed, like the rest of the shell UX)function ac-memory --description "Run agent memory CLI" ac npm run -s agent-memory -- $argvend
function ac-memory-list --description "List remembered sessions" ac npm run -s agent-memory:list -- $argvend
function ac-memory-remember --description "Create a new session from remembered context" ac npm run -s agent-memory:remember -- $argvend
function ac-memory-hooks --description "Enable git hooks for memory sync" ac npm run -s agent-memory:install-hooksend
function ac-memory-sync --description "Import recent Codex sessions into local memory" ac npm run -s agent-memory:sync-codex -- $argvend
function ac-memory-flush --description "Flush queued local memory records to remote" ac npm run -s agent-memory:flush -- $argvend
# Claude Code CLI agentfunction ac-agent claude $argvend
# Ollama LLM daemon managementfunction ac-llama set -l command $argv[1] switch $command case start echo "🦙 Starting Ollama daemon..." # Check if already running if pgrep -f "ollama serve" >/dev/null echo "⚠️ Ollama daemon already running" if curl -s http://localhost:11434/api/version >/dev/null 2>&1 echo "✅ Daemon is responsive" return 0 else echo "⚠️ Daemon process exists but not responsive, restarting..." pkill -f "ollama serve" 2>/dev/null sleep 2 end end # Start the daemon nohup ollama serve > /tmp/ollama.log 2>&1 & set daemon_pid $last_pid echo "🚀 Ollama daemon started (PID: $daemon_pid)" # Wait for it to become responsive set -l timeout 30 set -l elapsed 0 while test $elapsed -lt $timeout if curl -s http://localhost:11434/api/version >/dev/null 2>&1 echo "✅ Ollama daemon is ready!" return 0 end sleep 1 set elapsed (math $elapsed + 1) end echo "❌ Ollama daemon didn't become ready within $timeout seconds" echo "📋 Check logs at /tmp/ollama.log" return 1 case stop echo "🛑 Stopping Ollama daemon..." pkill -f "ollama serve" 2>/dev/null if test $status -eq 0 echo "✅ Ollama daemon stopped" else echo "⚠️ No Ollama daemon was running" end case restart echo "🔄 Restarting Ollama daemon..." ac-llama stop sleep 2 ac-llama start case status if pgrep -f "ollama serve" >/dev/null echo "✅ Ollama daemon process is running" if curl -s http://localhost:11434/api/version >/dev/null 2>&1 set -l ollama_version (curl -s http://localhost:11434/api/version 2>/dev/null | jq -r '.version' 2>/dev/null) echo "✅ Daemon is responsive (version: $ollama_version)" # List loaded models echo "📦 Checking available models..." set -l models (curl -s http://localhost:11434/api/tags 2>/dev/null | jq -r '.models[]?.name' 2>/dev/null) if test -n "$models" echo "Models available:" for model in $models echo " • $model" end else echo " (no models loaded)" end else echo "❌ Daemon process exists but not responsive" echo "📋 Check logs at /tmp/ollama.log" end else echo "❌ Ollama daemon is not running" end case logs if test -f /tmp/ollama.log echo "📋 Ollama logs (last 50 lines):" tail -n 50 /tmp/ollama.log else echo "⚠️ No log file found at /tmp/ollama.log" end case '*' echo "Usage: ac-llama [start|stop|restart|status|logs]" echo "" echo "Commands:" echo " start - Start the Ollama daemon" echo " stop - Stop the Ollama daemon" echo " restart - Restart the Ollama daemon" echo " status - Check daemon status and list models" echo " logs - Show recent daemon logs" return 1 endend
# alias ac-shell 'ac; ac-url; ac-tunnel; fish'# alias ac-offline 'ac; cd system/public; npx http-server -p 8888 -c-1 -g -b -S -C ../../ssl-dev/localhost.pem -K ../../ssl-dev/localhost-key.pem'alias ac-redis 'clear; ac; npm run redis'alias ac-udp 'ssh root@157.245.134.225' # ac monolith udp server management
# Send aesthetic.computer playlist to TV cast coordinationfunction ac-ff-playlist cd ~/aesthetic-computer ./rebroadcast.shend
# Connect to the aesthetic platform (emacs TUI)alias ac-aesthetic 'aesthetic-now'
alias ac-servers 'clear; ac; npm run -s servers; env nogreet=true fish'alias ac-chat-system 'clear; ac; npm run -s chat; cd nanos; npm run chat-system:dev; fish'alias ac-chat-sotce 'clear; ac; npm run -s chat; cd nanos; npm run chat-sotce:dev; fish'alias ac-chat-clock 'clear; ac; npm run -s chat; cd nanos; npm run chat-clock:dev; fish'alias ac-logger 'ac; cd system; npx netlify logs:function index'alias sotce-net 'ac; cd system; npx netlify logs:function sotce-net'alias acw 'cd ~/aesthetic-computer/system; npm run watch'
# GitHub Copilot CLI (LLM agent in terminal)# Uses GH_TOKEN from vault for auth, runs interactively# Default to Claude Opus 4.5 for best quality# Use --continue to resume last session after rebootalias ac-llm 'clear; ac; claude'alias ac-llm-continue 'clear; ac; claude --continue'alias ac-llm-resume 'clear; ac; claude --resume'
# Mail sync (mbsync + mu index for mu4e)# Syncs both mail@aesthetic.computer and me@jas.lifefunction ac-mail clear set -l mbsyncrc ~/.mbsyncrc set -l msmtprc ~/.msmtprc set -l authinfo ~/.authinfo set -l maildir_ac ~/.mail set -l maildir_jas ~/.mail-jas set -l maildir_all ~/.mail-all
# Check for required binaries if not command -q mbsync echo "📬 Mail tools not installed. Installing..." sudo dnf install -y isync maildir-utils msmtp 2>/dev/null if not command -q mbsync echo "❌ Failed to install mail tools (isync/maildir-utils/msmtp)" echo " Waiting... (press Ctrl+C to exit)" sleep infinity return 1 end end
# Configs come from vault via devault.fish (~/.mbsyncrc, ~/.msmtprc) if not test -f $mbsyncrc echo "❌ Missing $mbsyncrc — run devault.fish to copy from vault" echo " Waiting... (press Ctrl+C to exit)" sleep infinity return 1 end
if not test -f $msmtprc echo "❌ Missing $msmtprc — run devault.fish to copy from vault" echo " Waiting... (press Ctrl+C to exit)" sleep infinity return 1 end
chmod 600 $mbsyncrc $msmtprc
# Create maildirs mkdir -p $maildir_ac $maildir_jas
# Unified maildir root via symlinks (for mu to index both) mkdir -p $maildir_all ln -sfn $maildir_ac $maildir_all/ac ln -sfn $maildir_jas $maildir_all/jas
# Initialize mu database if needed if not test -d "$HOME/.cache/mu/xapian" echo "📬 Initializing mu database..." mu init --maildir=$maildir_all --my-address=mail@aesthetic.computer --my-address=me@jas.life 2>/dev/null end
echo "📬 mail — sync loop (mail@aesthetic.computer + me@jas.life)" echo "────────────────────────────────────────────────────────────" echo ""
# Initial sync echo "🔄 Initial sync..." mbsync ac-mail 2>&1 mbsync jas-mail 2>&1 mu index --quiet 2>/dev/null echo "✅ Sync complete. "(date '+%H:%M:%S') echo ""
# Periodic sync loop (every 5 minutes, matching mu4e-update-interval) while true sleep 300 echo "🔄 Syncing... "(date '+%H:%M:%S') mbsync ac-mail 2>&1 mbsync jas-mail 2>&1 mu index --quiet 2>/dev/null echo "✅ Done. "(date '+%H:%M:%S') endend
# Process viewer (htop for monitoring system resources)alias ac-top 'clear; htop'
alias cat 'bat -p' # use bat for syntax highlighting instead of the `cat` default
# 📶 Signal messenger (gurk TUI)function ac-signal clear echo "📶 Signal Messenger (gurk)" echo ""
if not command -q gurk echo "⏳ gurk not found, installing from source..." if not command -q cargo echo "❌ Rust/cargo not found. Cannot build gurk." echo " Waiting... (press Ctrl+C to exit)" sleep infinity return 1 end cargo install --git https://github.com/boxdot/gurk-rs gurk if test $status -ne 0 echo "❌ Failed to build gurk" echo " Waiting... (press Ctrl+C to exit)" sleep infinity return 1 end end
# signal-cli must be available (used by gurk as backend) if not command -q signal-cli echo "❌ signal-cli not found. Install it first." echo " Waiting... (press Ctrl+C to exit)" sleep infinity return 1 end
gurkend
# 🧟 Zombie process monitor and cleanupfunction ac-zombie-monitor echo "🧟 Starting zombie process monitor..." echo "📊 Checking every 30 seconds for zombie accumulation" echo "🔪 Will clean up if zombies > 100" echo "Press Ctrl+C to stop monitoring" while true set zombie_count (ps aux | grep -c defunct) set timestamp (date '+%H:%M:%S') if test $zombie_count -gt 100 echo "[$timestamp] ⚠️ Found $zombie_count zombies - cleaning up..." # Find parent processes with zombie children and log them ps aux | grep defunct | awk '{print $2}' | while read zpid set parent_pid (ps -o ppid= -p $zpid 2>/dev/null | string trim) if test -n "$parent_pid" set parent_cmd (ps -o cmd= -p $parent_pid 2>/dev/null | string trim) echo " 🧟 Zombie $zpid has parent $parent_pid: $parent_cmd" end end echo " 🧹 Attempting cleanup by reaping zombies..." # Signal parent processes to reap their zombie children ps aux | grep defunct | awk '{print $2}' | while read zpid set parent_pid (ps -o ppid= -p $zpid 2>/dev/null | string trim) if test -n "$parent_pid"; and test "$parent_pid" != "1" # Send SIGCHLD to parent to trigger reaping kill -CHLD $parent_pid 2>/dev/null end end sleep 2 set new_zombie_count (ps aux | grep -c defunct) echo " 📊 Zombies after cleanup: $new_zombie_count (was $zombie_count)" else echo "[$timestamp] ✅ System healthy: $zombie_count zombies" end sleep 30 endend
# Quick zombie status checkfunction ac-zombie-status set zombie_count (ps aux | grep -c defunct) echo "🧟 Current zombie count: $zombie_count" if test $zombie_count -gt 10 echo "📊 Top zombie-producing parents:" ps aux | grep defunct | awk '{print $2}' | while read zpid set parent_pid (ps -o ppid= -p $zpid 2>/dev/null | string trim) if test -n "$parent_pid" ps -o pid,cmd= -p $parent_pid 2>/dev/null end end | sort | uniq -c | sort -rn | head -5 endend
# set up an ngrok tunnel
function ac-tunnel # Run in foreground for Emacs eat terminals (shows logs) # Skip auto-restart loop when in Emacs - just run once # Use AC_EMACS_MODE instead of INSIDE_EMACS to avoid conflicts with tools if test -n "$AC_EMACS_MODE" echo "🚇 Starting tunnel (foreground mode for Emacs)..." ac npm run tunnel return end set tmp (mktemp) ngrok start --config ngrok.yml --all 2>$tmp set ngrok_exit $status set err (cat $tmp) rm -f $tmp
if test $ngrok_exit -ne 0 if string match -q '*ERR_NGROK_334*' $err clear echo "🟢 tunnel already online — watching..." # Add timeout to prevent infinite loops (5 minutes max) set timeout_count 0 set max_timeout 60 # 60 * 5 seconds = 5 minutes while test $timeout_count -lt $max_timeout sleep 5 set timeout_count (math $timeout_count + 1) if not curl --silent --max-time 2 --output /dev/null https://local.aesthetic.computer echo "🔁 tunnel down, restarting..." # Kill any existing ngrok processes to ensure clean restart pkill -f ngrok 2>/dev/null sleep 2 ac-tunnel return end end echo "⏱️ Tunnel monitoring timeout reached (5 minutes)" return else echo "❌ ngrok error:" echo $err end else echo "🚀 tunnel started successfully!" endend
function ac-function # Watch Netlify function logs # Usage: ac-function [function-name] # If no function name is provided, lists all functions if test (count $argv) -eq 0 echo "📋 Watching all Netlify function logs..." npx netlify logs:function else echo "📋 Watching logs for function: $argv[1]" npx netlify logs:function $argv[1] endend
# a shell-gpt shortcut (must be all lowercase / otherwise quoted)
function umm # Use string escape to handle special characters set -l args (string join " " $argv) # Pass the joined, escaped string to sgpt sgpt --chat umm "$args"end
function umms sgpt --show-chat ummend
function code if set -q argv[1] set -l args (string join " " $argv) sgpt --code --chat code "$args" else sgpt --code --editor --chat code endend
function codes sgpt --show-chat codeend
function copy # Extract everything from the chat after the last "assistant: " line. set content (sgpt --show-chat code | tac | sed '/^assistant: /q' | tac | sed '1s/^assistant: //') printf "%s\n" $content | xclip -selection clipboardend
function done rm /tmp/chat_cache/code 2>/dev/null echo "bye :)"end
function ok rm /tmp/chat_cache/umm 2>/dev/null echo "bye :)"end
function forget rm /tmp/chat_cache/umm 2>/dev/null echo "umm, i forgot :)"end
# Increase Node.js heap size
set -x NODE_OPTIONS "--max-old-space-size=4096"
# Set Google Application Credentials
set -x GOOGLE_APPLICATION_CREDENTIALS /home/me/aesthetic-computer/nanos/gcp-service.key.json
set -x PATH /google-cloud-sdk/bin $PATH
alias nvm forget
# use tab to autocomplete the first suggestion
bind \t complete-select-first
function ac-deploy-session set script_path ~/aesthetic-computer/session-server/deploy.fish if test -f $script_path fish $script_path $argv else echo "❌ Deploy script not found at $script_path" return 1 endend
function clipboard set content $argv set -l hosts host.docker.internal $HOST_IP 172.17.0.1 for host in $hosts echo "🧪 trying $host..." if echo "" | nc -z -w 0.15 $host 12345 2>/dev/null echo "✅ sending to $host" printf "%s\n" $content | nc $host 12345 return end end echo "❌ clipboard: No reachable host for clipboard relay"end
# Automatically reload the fish config when it changes. 25.04.28.00.12
# if status --is-interactive
# function __fish_watch_config --description "Watch and reload config.fish on save"
# while inotifywait --quiet -e close_write ~/.config/fish/config.fish
# echo "🐟 Reloading config.fish..."
# source ~/.config/fish/config.fish
# end
# end
# set pidfile ~/.cache/fish-config-watcher.pid
# if not test -e $pidfile; or not kill (cat $pidfile) ^/dev/null
# __fish_watch_config & disown
# echo $last_pid > $pidfile
# end
# end
# ac-shop - Shopify CLI for Aesthetic Computerfunction ac-shop node /workspaces/aesthetic-computer/ac-shop/shopify.mjs $argvend
# ac-tezbot - Tezos daemon helpersset -gx TEZBOT_SCRIPT /workspaces/aesthetic-computer/tezos/ac-tezbot.mjs
function tezbot --description "Tezos daemon interface" if test (count $argv) -eq 0 node $TEZBOT_SCRIPT status else node $TEZBOT_SCRIPT $argv endend
function tezbot-start --description "Start tezbot daemon" node $TEZBOT_SCRIPT status >/dev/null 2>&1 if test $status -eq 0 echo "🟢 Tezbot already running" else echo "🚀 Starting tezbot..." nohup node $TEZBOT_SCRIPT > /tmp/tezbot.log 2>&1 & sleep 0.5 node $TEZBOT_SCRIPT status endend
function tezbot-stop --description "Stop tezbot daemon" node $TEZBOT_SCRIPT stopend
function tz-balance --description "Get Tezos wallet balance via tezbot" node $TEZBOT_SCRIPT cmd balanceend
function tz-status --description "Get Tezos contract status via tezbot" node $TEZBOT_SCRIPT cmd statusend
function tz-tokens --description "List minted tokens via tezbot" node $TEZBOT_SCRIPT cmd tokensend
# 🎮 KidLisp Playdate Build and Simulate Functions
# Build a KidLisp program for Playdate# Usage: ac-playdate-build examples/bop.lispfunction ac-playdate-build if test (count $argv) -lt 1 echo "Usage: ac-playdate-build <source.lisp>" echo "Example: ac-playdate-build examples/bop.lisp" return 1 end set -l source_file $argv[1] cd /workspaces/aesthetic-computer/kidlisp-playdate source build.fish $source_fileend
# Run the last built Playdate game on host simulator# Usage: ac-playdate-simulate [game-name]# If no game name provided, uses 'bop' as defaultfunction ac-playdate-simulate set -l game_name (test (count $argv) -ge 1; and echo $argv[1]; or echo "bop") set -l pdx_path "/workspaces/aesthetic-computer/kidlisp-playdate/build/$game_name.pdx" if not test -d $pdx_path echo "❌ Game not found: $pdx_path" echo "💡 Build it first with: ac-playdate-build examples/$game_name.lisp" return 1 end echo "🎮 Deploying $game_name to host simulator..." # Kill existing simulator ssh jas@172.17.0.1 "pkill -9 -f Playdate" 2>/dev/null sleep 0.3 # Copy and run scp -r $pdx_path jas@172.17.0.1:~/ ssh jas@172.17.0.1 "DISPLAY=:0 nohup ~/PlaydateSDK/bin/PlaydateSimulator ~/$game_name.pdx >/dev/null 2>&1 &" echo "✅ $game_name running on host simulator"end
# Build and immediately simulate# Usage: ac-playdate examples/bop.lispfunction ac-playdate if test (count $argv) -lt 1 echo "Usage: ac-playdate <source.lisp>" echo "Example: ac-playdate examples/bop.lisp" return 1 end set -l source_file $argv[1] set -l game_name (basename $source_file .lisp) ac-playdate-build $source_file and ac-playdate-simulate $game_nameend
# 🖥️ Electron App Management (SSH to Mac host)# Restarts the Aesthetic Computer Electron app on the host Mac
function ac-electron-restart --description "Restart the Aesthetic Computer Electron app on host Mac" set -l host "jas@host.docker.internal" set -l ac_path "/Users/jas/Desktop/code/aesthetic-computer/ac-electron" echo "🔄 Restarting Aesthetic Computer Electron app..." # Kill existing Electron processes (our app only, not VS Code) ssh -o StrictHostKeyChecking=no $host "pkill -f '$ac_path/node_modules/electron'" 2>/dev/null sleep 1 # Start the app again ssh -o StrictHostKeyChecking=no $host "cd $ac_path && nohup npm start > /tmp/ac-electron.log 2>&1 &" echo "✅ Electron app restarting... check /tmp/ac-electron.log on host for output"end
function ac-electron-stop --description "Stop the Aesthetic Computer Electron app on host Mac" set -l host "jas@host.docker.internal" set -l ac_path "/Users/jas/Desktop/code/aesthetic-computer/ac-electron" echo "🛑 Stopping Aesthetic Computer Electron app..." ssh -o StrictHostKeyChecking=no $host "pkill -f '$ac_path/node_modules/electron'" 2>/dev/null echo "✅ Electron app stopped"end
function ac-electron-start --description "Start the Aesthetic Computer Electron app on host Mac" set -l host "jas@host.docker.internal" set -l ac_path "/Users/jas/Desktop/code/aesthetic-computer/ac-electron" echo "🚀 Starting Aesthetic Computer Electron app..." ssh -o StrictHostKeyChecking=no $host "cd $ac_path && nohup npm start > /tmp/ac-electron.log 2>&1 &" echo "✅ Electron app started"end
function ac-electron-dev --description "Toggle dev mode for Electron app (loads files from repo)" set -l host "jas@host.docker.internal" set -l dev_flag ".ac-electron-dev" # Check current state set -l is_dev (ssh -o StrictHostKeyChecking=no $host "test -f ~/$dev_flag && echo yes || echo no" 2>/dev/null) if test "$argv[1]" = "status" if test "$is_dev" = "yes" echo "🔧 Dev mode is ENABLED" echo " Files load from: ~/aesthetic-computer/ac-electron/" else echo "📦 Dev mode is DISABLED" echo " Files load from: app bundle" end return 0 end if test "$argv[1]" = "on" ssh -o StrictHostKeyChecking=no $host "touch ~/$dev_flag" echo "🔧 Dev mode ENABLED - restart Electron app to apply" echo " Run: ac-electron-restart" return 0 end if test "$argv[1]" = "off" ssh -o StrictHostKeyChecking=no $host "rm -f ~/$dev_flag" echo "📦 Dev mode DISABLED - restart Electron app to apply" echo " Run: ac-electron-restart" return 0 end # Toggle if test "$is_dev" = "yes" ssh -o StrictHostKeyChecking=no $host "rm -f ~/$dev_flag" echo "📦 Dev mode DISABLED - restart Electron app to apply" else ssh -o StrictHostKeyChecking=no $host "touch ~/$dev_flag" echo "🔧 Dev mode ENABLED - restart Electron app to apply" end echo " Run: ac-electron-restart"end
function ac-electron-reload --description "Reload all Electron windows (dev mode: picks up file changes)" set -l host "jas@host.docker.internal" echo "♻️ Reloading Electron windows..." # Use osascript to send Cmd+R to Aesthetic Computer windows ssh -o StrictHostKeyChecking=no $host 'osascript -e "tell application \"Aesthetic Computer\" to activate" -e "delay 0.2" -e "tell application \"System Events\" to keystroke \"r\" using command down"' 2>/dev/null echo "✅ Reload triggered"end
# 🎸 Ableton M4L Console Tunnel# Listen for console.log/error/warn from M4L devices via UDP
function ac-ableton-tunnel --description "Listen for Ableton M4L device console logs" set -l port 7777 set -l host "jas@host.docker.internal" echo "🎸 AC Ableton Console Tunnel" echo " Listening for M4L device logs on UDP port $port..." echo " (Ctrl+C to stop)" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Run nc on the Mac via SSH to listen for UDP messages # Max sends to 127.0.0.1:7777 on the Mac ssh -o StrictHostKeyChecking=no -t $host "nc -lu $port" 2>/dev/nullend
function ac-ableton-tunnel-simple --description "Simple UDP listener (run on Mac directly)" echo "🎸 AC Ableton Console (Simple Mode)" echo " Run this on your Mac (not in devcontainer):" echo "" echo " nc -lu 7777" echo "" echo " Or with formatting:" echo " nc -lu 7777 | while read line; do echo \"\$(date '+%H:%M:%S') \$line\"; done"end
# 🖥️ Machine Info / SSH Helpers# Read machine configs from vault/machines.json
function __ac_machines_file --description "Resolve machines.json with local cache fallback" set -l machines_cache "$HOME/.cache/ac/machines.json"
# The vault is a sibling checkout in the devcontainer, but lives inside # the repo on native Macs. Prefer explicit environment configuration and # then try both supported layouts so ac-host works in either environment. set -l candidates if set -q AESTHETIC_VAULT set -a candidates "$AESTHETIC_VAULT/machines.json" end if set -q AC_ROOT set -a candidates "$AC_ROOT/aesthetic-computer-vault/machines.json" end set -a candidates \ "/workspaces/aesthetic-computer/aesthetic-computer-vault/machines.json" \ "$HOME/aesthetic-computer/aesthetic-computer-vault/machines.json"
for machines_file in $candidates if test -f $machines_file mkdir -p (dirname $machines_cache) cp $machines_file $machines_cache 2>/dev/null echo $machines_file return 0 end end
if test -f $machines_cache echo $machines_cache return 0 end
return 1end
function ac-host --description "Show current host SSH config from machines.json" set -l machines_file (__ac_machines_file)
if test $status -ne 0 -o -z "$machines_file" echo "❌ machines.json not found in vault or local cache" return 1 end set -l machine_key $argv[1] if test -z "$machine_key" # Show all machines echo "📍 Available machines (from vault/machines.json):" echo "" cat $machines_file | jq -r '.machines | to_entries[] | "\(.value.emoji // "🖥️") \(.key): \(.value.label) [\(.value.os // "unknown")]"' echo "" echo "Usage: ac-host <machine-key> [ssh|ip|info|raw]" echo "Examples:" echo " ac-host jeffrey-macbook ssh # SSH to the machine" echo " ac-host ff1-dvveklza ip # Show just the IP" echo " ac-host mac-mini info # Show full info" return 0 end set -l action $argv[2] test -z "$action"; and set action info set -l machine_data (jq -c --arg key "$machine_key" '.machines[$key]' $machines_file) if test "$machine_data" = "null" -o -z "$machine_data" echo "❌ Machine '$machine_key' not found" echo "Available: "(jq -r '.machines | keys | join(", ")' $machines_file) return 1 end set -l ip (echo $machine_data | jq -r '.ip // empty') set -l user (echo $machine_data | jq -r '.user // "jas"') set -l host (echo $machine_data | jq -r '.host // empty') set -l label (echo $machine_data | jq -r '.label // .key') set -l ssh_port (echo $machine_data | jq -r '.ssh.port // .port // empty') set -l ssh_key (echo $machine_data | jq -r '.ssh.keyFile // empty') set -l ssh_enabled (echo $machine_data | jq -r '.ssh.enabled // true') # Use host if specified (e.g., host.docker.internal), else ip set -l target $host if test -z "$target" set target $ip end
if string match -q "~/*" $ssh_key set ssh_key "$HOME/"(string sub -s 3 -- $ssh_key) end switch $action case ssh if test "$ssh_enabled" = "false" echo "❌ SSH is disabled for $machine_key in machines.json" return 1 end if test -z "$target" echo "❌ No IP/host configured for $machine_key" return 1 end echo "🔗 Connecting to $label ($user@$target)..." set -l ssh_args -o StrictHostKeyChecking=no if test -n "$ssh_port" set ssh_args $ssh_args -p $ssh_port end if test -n "$ssh_key" set ssh_args $ssh_args -i $ssh_key end ssh $ssh_args $user@$target case ip if test -n "$ip" echo $ip else if test -n "$host" echo $host else echo "❌ No IP configured" return 1 end case info echo "📋 $label" echo $machine_data | jq ' del( .password, .apiKey, .token, .accessToken, .secret, .privateKey, .ssh.password, .ssh.apiKey, .ssh.token, .ssh.privateKey ) ' case raw echo "📋 $label (raw)" echo $machine_data | jq . case '*' echo "Usage: ac-host <machine-key> [ssh|ip|info|raw]" return 1 endend
function ac-machines --description "List all machines from vault/machines.json" ac-hostend
function ac-host-nmap --description "Run nmap scan on local network via current host" set -l machines_file (__ac_machines_file) if test $status -ne 0 -o -z "$machines_file" echo "❌ machines.json not found in vault or local cache" return 1 end set -l search_term $argv[1]
set -l hosts_to_try (jq -r ' .machines | to_entries[] | select((.value.ip // "") != "" and (.value.ssh.enabled // true)) | .key ' $machines_file)
if test (count $hosts_to_try) -eq 0 echo "❌ No candidate hosts with IP + SSH enabled found in machines.json" return 1 end
for host_key in $hosts_to_try set -l host_data (jq -c --arg key "$host_key" '.machines[$key]' $machines_file) if test "$host_data" = "null" continue end
set -l ip (echo $host_data | jq -r '.ip // empty') set -l user (echo $host_data | jq -r '.user // "jas"') set -l label (echo $host_data | jq -r '.label') set -l ssh_port (echo $host_data | jq -r '.ssh.port // empty') set -l ssh_key (echo $host_data | jq -r '.ssh.keyFile // empty')
if test -z "$ip" continue end
if string match -q "~/*" $ssh_key set ssh_key "$HOME/"(string sub -s 3 -- $ssh_key) end
set -l probe_args -o ConnectTimeout=1 -o StrictHostKeyChecking=no -o BatchMode=yes if test -n "$ssh_port" set probe_args $probe_args -p $ssh_port end if test -n "$ssh_key" set probe_args $probe_args -i $ssh_key end
# Quick connectivity check (1 second timeout) if ssh $probe_args $user@$ip "echo ok" 2>/dev/null | grep -q ok echo "🔍 Running nmap via $label ($ip)..."
set -l run_args -o StrictHostKeyChecking=no if test -n "$ssh_port" set run_args $run_args -p $ssh_port end if test -n "$ssh_key" set run_args $run_args -i $ssh_key end
if test -n "$search_term" # Search for specific term ssh $run_args $user@$ip "nmap -sn 192.168.1.0/24 2>/dev/null | grep -B2 -i '$search_term'" else # Full scan ssh $run_args $user@$ip "nmap -sn 192.168.1.0/24 2>/dev/null" end return $status end end echo "❌ No reachable host found to run nmap" echo "Tried: $hosts_to_try" echo "" echo "Make sure one of your machines is online and has the correct IP in machines.json" echo "You can update IPs by running on the host: hostname -I | awk '{print \$1}'" return 1end
# 🖼️ FF1 Art Computer Helpers
function __ac_ff1_find_host --description "Find a reachable host to run network commands" set -l machines_file "/workspaces/aesthetic-computer/aesthetic-computer-vault/machines.json" set -l hosts_to_try (jq -r ' .machines | to_entries[] | select((.value.ip // "") != "" and (.value.ssh.enabled // true)) | .key ' $machines_file)
for host_key in $hosts_to_try set -l host_data (jq -c --arg key "$host_key" '.machines[$key]' $machines_file 2>/dev/null) if test "$host_data" = "null" -o -z "$host_data" continue end
set -l ip (echo $host_data | jq -r '.ip // empty') set -l user (echo $host_data | jq -r '.user // "jas"') set -l ssh_port (echo $host_data | jq -r '.ssh.port // empty') set -l ssh_key (echo $host_data | jq -r '.ssh.keyFile // empty')
if test -z "$ip" continue end
if string match -q "~/*" $ssh_key set ssh_key "$HOME/"(string sub -s 3 -- $ssh_key) end
set -l probe_args -o ConnectTimeout=1 -o StrictHostKeyChecking=no -o BatchMode=yes if test -n "$ssh_port" set probe_args $probe_args -p $ssh_port end if test -n "$ssh_key" set probe_args $probe_args -i $ssh_key end
# Quick connectivity check (1 second timeout) if ssh $probe_args $user@$ip "echo ok" 2>/dev/null | grep -q ok echo "$user@$ip" return 0 end end return 1end
function __ac_ff1_scan_network --description "Scan network for FF1 device" set -l host_target (__ac_ff1_find_host) if test -z "$host_target" echo "" return 1 end # Run nmap on the host and look for FF1 set -l result (ssh -o StrictHostKeyChecking=no $host_target "nmap -sn 192.168.1.0/24 2>/dev/null | grep -A1 'FF1'" 2>/dev/null) if test -n "$result" # Extract IP from result like "Nmap scan report for FF1-DVVEKLZA (192.168.1.164)" echo $result | grep -oP '\d+\.\d+\.\d+\.\d+' | head -1 else echo "" endend
function __ac_ff1_update_ip --description "Update FF1 IP in machines.json" set -l new_ip $argv[1] set -l machines_file "/workspaces/aesthetic-computer/aesthetic-computer-vault/machines.json" # Use jq to update the IP set -l tmp_file (mktemp) cat $machines_file | jq ".machines[\"ff1-dvveklza\"].ip = \"$new_ip\"" > $tmp_file mv $tmp_file $machines_fileend
function ac-ff1 --description "Control FF1 Art Computer (direct network access)" set -l machines_file "/workspaces/aesthetic-computer/aesthetic-computer-vault/machines.json" set -l ff1_data (cat $machines_file 2>/dev/null | jq -r '.machines["ff1-dvveklza"]') set -l ff1_ip (echo $ff1_data | jq -r '.ip') set -l ff1_port (echo $ff1_data | jq -r '.port // 1111') set -l action $argv[1] # For commands that need FF1 to be online, check connectivity first set -l needs_connection false switch $action case ping cast top colors chords playlist set needs_connection true end if test "$needs_connection" = true # Quick ping check if not curl -s --connect-timeout 2 "http://$ff1_ip:$ff1_port/" >/dev/null 2>&1 echo "⚠️ FF1 not responding at $ff1_ip:$ff1_port" echo "🔍 Scanning network for FF1..." # Find a host to scan from set -l host_target (__ac_ff1_find_host) if test -z "$host_target" echo "❌ No reachable host to scan from. Update machine IPs in machines.json" echo " Run on your host: hostname -I | awk '{print \$1}'" return 1 end echo " Using $host_target for scan..." echo -n " Scanning " # Run nmap with progress indication set -l scan_result (ssh -o StrictHostKeyChecking=no $host_target "nmap -sn 192.168.1.0/24 2>/dev/null" 2>/dev/null) echo "done!" # Look for FF1 in results set -l new_ip (echo $scan_result | grep -oP 'FF1[^\(]*\(\K[0-9.]+') if test -n "$new_ip" echo "✅ Found FF1 at $new_ip" if test "$new_ip" != "$ff1_ip" echo "📝 Updating machines.json ($ff1_ip → $new_ip)" __ac_ff1_update_ip $new_ip set ff1_ip $new_ip end # Verify new IP works if curl -s --connect-timeout 2 "http://$ff1_ip:$ff1_port/" >/dev/null 2>&1 echo "✅ FF1 responding at new IP!" echo "" else echo "❌ FF1 found but not responding on port $ff1_port" return 1 end else echo "❌ FF1 not found on network" echo "" echo "Is the FF1 powered on and connected to WiFi?" echo "Full scan results:" echo $scan_result | grep -E "^Nmap|Host is up" | head -20 return 1 end end end switch $action case scan echo "🔍 Scanning for FF1 on network..." set -l host_target (__ac_ff1_find_host) if test -z "$host_target" echo "❌ No reachable host to scan from" return 1 end echo " Using $host_target..." ssh -o StrictHostKeyChecking=no $host_target "nmap -sn 192.168.1.0/24 2>/dev/null | grep -B2 -i 'ff1'" case ping echo "🏓 Pinging FF1 at $ff1_ip:$ff1_port..." curl -s --connect-timeout 3 "http://$ff1_ip:$ff1_port/" >/dev/null 2>&1 && echo "✅ FF1 responding!" || echo "❌ FF1 not responding" case cast if test (count $argv) -lt 2 echo "Usage: ac-ff1 cast <piece|url> [options]" echo "" echo "Pieces starting with \$ are KidLisp and auto-use device.kidlisp.com" echo "Regular pieces use aesthetic.computer with ?device param" echo "" echo "Examples:" echo " ac-ff1 cast \$mtz # KidLisp → device.kidlisp.com/mtz" echo " ac-ff1 cast \$mtz --perf # With FPS/performance HUD" echo " ac-ff1 cast \$mtz --nohud # No code-label, QR, or progress bar" echo " ac-ff1 cast \$mtz --socklogs # Enable remote console logging" echo " ac-ff1 cast ceo # Regular → aesthetic.computer/ceo?device" echo " ac-ff1 cast https://example.com/art.html" echo "" echo "Options:" echo " --perf Enable KidLisp performance/FPS HUD overlay" echo " --nohud Hide device.html overlays (code-label, QR, progress bar)" echo " --nolabel Hide AC piece label (for regular pieces)" echo " --socklogs Enable remote console logs (view with: ac-ff1 logs)" echo " --relay Use cloud relay (requires API key)" return 1 end set -l input $argv[2] set -l use_relay false set -l use_perf false set -l use_socklogs false set -l use_nohud false set -l use_nolabel false
# Parse flags for arg in $argv[3..-1] switch $arg case '--relay' set use_relay true case '--perf' set use_perf true case '--socklogs' set use_socklogs true case '--nohud' set use_nohud true case '--nolabel' set use_nolabel true end end # Build URL from input - auto-detect KidLisp ($) vs regular pieces set -l url # Auto-prepend https:// for domain-like inputs (e.g., top.kidlisp.com) if string match -rq '\.[a-z]{2,}(/|$)' $input; and not string match -q 'http*' $input set input "https://$input" end if string match -q 'http*' $input # Already a full URL # Don't add &device for device/top.kidlisp.com (already device-optimized) if string match -q '*device.kidlisp.com*' $input; or string match -q '*top.kidlisp.com*' $input set url $input else if string match -q '*?*' $input set url "$input&device" else set url "$input?device" end # Add perf param if requested if test "$use_perf" = true if string match -q '*?*' $url set url "$url&perf" else set url "$url?perf" end end # Add socklogs param if requested if test "$use_socklogs" = true if string match -q '*?*' $url set url "$url&socklogs" else set url "$url?socklogs" end end else if string match -rq '^\$' $input # KidLisp piece (starts with $) - use device.kidlisp.com # Keep the $ prefix - device.kidlisp.com passes it to aesthetic.computer set -l code_id $input set -l params "" if test "$use_perf" = true set params "perf=true" end if test "$use_socklogs" = true if test -n "$params" set params "$params&socklogs" else set params "socklogs" end end if test "$use_nohud" = true if test -n "$params" set params "$params&nohud" else set params "nohud" end end if test -n "$params" set url "https://device.kidlisp.com/$code_id?$params" else set url "https://device.kidlisp.com/$code_id" end if test "$use_perf" = true echo "🎨 KidLisp piece detected (perf mode)" else echo "🎨 KidLisp piece detected" end if test "$use_socklogs" = true echo "🔌 Remote logging enabled - run 'ac-ff1 logs' to view" end else # Regular aesthetic.computer piece - add &device if has query, else ?device # density=4 for a good balance of quality and performance on FF1 4K display if string match -q '*?*' $input set url "https://aesthetic.computer/$input&device&density=4" else set url "https://aesthetic.computer/$input?device&density=4" end # Add perf param if requested if test "$use_perf" = true set url "$url&perf" end # Add nolabel param if requested if test "$use_nolabel" = true set url "$url&nolabel" end end if test "$use_relay" = true # Use Feral File cloud relay API (requires topicId and API key) set -l topic_id (echo $ff1_data | jq -r '.topicId') set -l api_key (echo $ff1_data | jq -r '.apiKey // ""') if test -z "$topic_id" -o "$topic_id" = "null" echo "❌ No topicId configured for FF1 in machines.json" return 1 end if test -z "$api_key" -o "$api_key" = "null" echo "⚠️ Cloud relay requires API-KEY. Get it from FF1 app or use direct mode." echo " Add 'apiKey' to machines.json or use: ac-ff1 cast <url> (without --relay)" return 1 end echo "☁️ Casting $url to FF1 via cloud relay..." set -l payload (printf '{"command":"displayPlaylist","request":{"playlist":{"dpVersion":"1.0.0","items":[{"source":"%s","duration":0}]},"intent":{"action":"now_display"}}}' "$url") curl -s -X POST -H 'Content-Type: application/json' -H "topicID: $topic_id" -H "API-KEY: $api_key" "https://artwork-info.feral-file.workers.dev/api/cast" -d "$payload" else # Direct network access to FF1 # Check if it's a playlist URL (feed server or contains /playlists/ or /api/playlist) if string match -q '*feed.aesthetic.computer*' $url; or string match -q '*playlists*' $url; or string match -q '*/api/playlist*' $url # It's a playlist URL - send as playlistUrl so FF1 fetches and displays it echo "📺 Casting playlist to FF1: $url" curl -s --connect-timeout 5 -X POST -H 'Content-Type: application/json' "http://$ff1_ip:$ff1_port/api/cast" -d "{\"command\":\"displayPlaylist\",\"request\":{\"playlistUrl\":\"$url\",\"intent\":{\"action\":\"now_display\"}}}" else # Single URL - wrap in a minimal playlist echo "📺 Casting $url to FF1..." curl -s --connect-timeout 5 -X POST -H 'Content-Type: application/json' "http://$ff1_ip:$ff1_port/api/cast" -d "{\"command\":\"displayPlaylist\",\"request\":{\"dp1_call\":{\"dpVersion\":\"1.1.0\",\"items\":[{\"source\":\"$url\",\"duration\":0}]},\"intent\":{\"action\":\"now_display\"}}}" end end case top # Top 100 KidLisp hits - uses TV endpoint directly (no Cloudflare KV dependency) set -l limit 100 set -l duration 24 # Parse options for arg in $argv[2..-1] if string match -q -- '--limit=*' "$arg" set limit (string replace -- '--limit=' '' "$arg") else if string match -q -- '--duration=*' "$arg" set duration (string replace -- '--duration=' '' "$arg") end end set -l playlist_url "https://aesthetic.computer/api/tv?types=kidlisp&sort=hits&limit=$limit&format=dp1&duration=$duration" echo "🎵 Fetching Top $limit KidLisp Hits from TV endpoint..." echo " URL: $playlist_url" echo "📺 Casting playlist to FF1..." curl -s --connect-timeout 5 -X POST -H 'Content-Type: application/json' "http://$ff1_ip:$ff1_port/api/cast" -d "{\"command\":\"displayPlaylist\",\"request\":{\"playlistUrl\":\"$playlist_url\",\"intent\":{\"action\":\"now_display\"}}}" case colors chords # Legacy feed server playlists (may hit KV limits) set -l playlist_name $action echo "🎵 Looking up $playlist_name playlist from feed server..." echo "⚠️ Note: Feed server may hit Cloudflare KV limits. Consider using 'ac-ff1 top' instead." # Map playlist names to their IDs set -l playlist_id switch $playlist_name case colors set playlist_id "4b872517-e4d8-4433-af8b-a9a4a8204cc9" case chords set playlist_id "49f0ee0e-0303-4192-9cb2-aa3c5abb64b5" end set -l playlist_url "https://feed.aesthetic.computer/api/v1/playlists/$playlist_id" echo "📺 Casting playlist to FF1: $playlist_url" curl -s --connect-timeout 5 -X POST -H 'Content-Type: application/json' "http://$ff1_ip:$ff1_port/api/cast" -d "{\"command\":\"displayPlaylist\",\"request\":{\"playlistUrl\":\"$playlist_url\",\"intent\":{\"action\":\"now_display\"}}}" case tunnel echo "🚇 Starting SSH tunnel to FF1 (localhost:1111 -> $ff1_ip:$ff1_port)..." echo "Press Ctrl+C to stop the tunnel" ssh -L 1111:$ff1_ip:$ff1_port jas@host.docker.internal -N case playlist # Generate and push a DP-1 playlist of top KidLisp hits set -l limit 10 set -l duration 60 set -l handle "" # Parse options using string match with -- separator for arg in $argv[2..-1] if string match -q -- '--limit=*' "$arg" set limit (string replace -- '--limit=' '' "$arg") else if string match -q -- '--duration=*' "$arg" set duration (string replace -- '--duration=' '' "$arg") else if string match -q -- '--handle=*' "$arg" set handle (string replace -- '--handle=' '' "$arg") else if string match -q -- 'handle=*' "$arg" # Support handle=jeffrey without -- set handle (string replace -- 'handle=' '' "$arg") end end # Build playlist API URL set -l playlist_url "https://aesthetic.computer/api/playlist?limit=$limit&duration=$duration&density=8" if test -n "$handle" set playlist_url "$playlist_url&handle=$handle" echo "🎵 Fetching top $limit KidLisp hits by @$handle..." else echo "🎵 Fetching top $limit KidLisp hits..." end # Fetch playlist from API set -l playlist_json (curl -s "$playlist_url") if test -z "$playlist_json" echo "❌ Failed to fetch playlist" return 1 end # Extract codes for display set -l codes (echo $playlist_json | jq -r '.items[].title | ltrimstr("$")') set codes (string split \n -- $codes) echo "📺 Pushing playlist to FF1 ("(count $codes)" items, "$duration"s each)..." echo " Codes:" $codes # Extract items array from playlist response set -l items_json (echo $playlist_json | jq -c '.items | map({source, duration})') # Build full DP-1 payload set -l playlist_payload (printf '{"command":"displayPlaylist","request":{"dp1_call":{"dpVersion":"1.0.0","items":%s},"intent":{"action":"now_display"}}}' "$items_json") # Send to FF1 directly curl -s --connect-timeout 5 -X POST -H 'Content-Type: application/json' "http://$ff1_ip:$ff1_port/api/cast" -d "$playlist_payload" case logs # Stream remote console logs from devices with ?socklogs enabled echo "👁️ Connecting to session-server for remote logs..." echo " Cast with: ac-ff1 cast \$code --socklogs" echo " Press Ctrl+C to stop" echo "" # Determine session server URL - use local if available, else production # Production uses direct IP since Cloudflare SSL isn't configured for WebSocket origin set -l session_url "ws://157.245.134.225:8889/socklogs?role=viewer" set -l websocat_opts "" # Check if local session server is running if nc -z localhost 8889 2>/dev/null set session_url "wss://localhost:8889/socklogs?role=viewer" set websocat_opts "-k" # Skip cert verification for local dev echo "📡 Using local session server (localhost:8889)" else echo "📡 Using production session server (direct IP)" end # Use websocat if available, otherwise fall back to node script if command -v websocat >/dev/null 2>&1 websocat $websocat_opts "$session_url" 2>/dev/null | while read -l line set -l json $line set -l type (echo $json | jq -r '.type // ""' 2>/dev/null) if test "$type" = "log" set -l level (echo $json | jq -r '.level // "info"' 2>/dev/null) set -l msg (echo $json | jq -r '.message // ""' 2>/dev/null) set -l device (echo $json | jq -r '.deviceId // "unknown"' 2>/dev/null) set -l ts (echo $json | jq -r '.timestamp // 0' 2>/dev/null) set -l time_str (date -d "@"(math $ts / 1000) "+%H:%M:%S" 2>/dev/null; or echo "??:??:??") # Color based on level switch $level case error set_color red case warn set_color yellow case debug set_color brblack case '*' set_color normal end printf "[%s] %s [%s] %s\n" $time_str $device (string upper $level) $msg set_color normal else if test "$type" = "status" set -l device_count (echo $json | jq -r '.devices | length' 2>/dev/null) set_color cyan echo "📱 $device_count device(s) connected" set_color normal else if test "$type" = "device-connected" set -l device (echo $json | jq -r '.deviceId // "unknown"' 2>/dev/null) set_color green echo "📱 Device connected: $device" set_color normal else if test "$type" = "device-disconnected" set -l device (echo $json | jq -r '.deviceId // "unknown"' 2>/dev/null) set_color red echo "📱 Device disconnected: $device" set_color normal end end else echo "⚠️ websocat not installed. Install with: cargo install websocat" echo " Or run: npm install -g wscat && wscat -c '$session_url'" end case info '*' set -l topic_id (echo $ff1_data | jq -r '.topicId // "not set"') echo "🖼️ FF1 Art Computer" echo " IP: $ff1_ip" echo " Port: $ff1_port" echo " Device ID: "(echo $ff1_data | jq -r '.deviceId') echo " Topic ID: $topic_id" echo "" echo "Commands:" echo " ac-ff1 - Show this help" echo " ac-ff1 scan - Find FF1 via mDNS" echo " ac-ff1 ping - Check if FF1 is responding" echo " ac-ff1 cast \$mtz - Cast KidLisp piece (auto device.kidlisp.com)" echo " ac-ff1 cast \$mtz --perf - Cast with FPS/performance HUD" echo " ac-ff1 cast \$mtz --nohud - Hide code-label, QR, and progress bar" echo " ac-ff1 cast \$mtz --socklogs - Enable remote console logging" echo " ac-ff1 cast ceo - Cast regular piece (auto ?device param)" echo " ac-ff1 cast <url> - Cast any URL" echo " ac-ff1 top - Cast Top KidLisp Hits playlist" echo " ac-ff1 colors - Cast KidLisp Colors playlist" echo " ac-ff1 chords - Cast KidLisp Chords playlist" echo " ac-ff1 playlist [options] - Push dynamic KidLisp playlist" echo " ac-ff1 tunnel - Create SSH tunnel for local dev" echo " ac-ff1 logs - Stream remote console logs (requires ?socklogs)" echo "" echo "Playlist options:" echo " --limit=N Number of items (default: 10)" echo " --duration=S Seconds per item (default: 60)" echo " --handle=H Filter by user handle" endend
# 📊 Bundle Telemetry - query performance data from inline bundlesfunction ac-telemetry --description "Query bundle telemetry from MongoDB" set -l action $argv[1] set -l limit 20 set -l piece "" # Parse options for arg in $argv[2..-1] if string match -q -- '--limit=*' "$arg" set limit (string replace -- '--limit=' '' "$arg") else if string match -q -- '--piece=*' "$arg" set piece (string replace -- '--piece=' '' "$arg") end end set -l base_url "https://aesthetic.computer/api/bundle-telemetry-query" set -l piece_param "" if test -n "$piece" set piece_param "&piece=$piece" end switch $action case recent boot echo "📊 Recent bundle boots (last $limit)..." curl -s "$base_url?type=boot&limit=$limit$piece_param" | jq -r ' .results[] | "\(.timestamp | split(".")[0] | gsub("T"; " ")) | \(.piece // "?") | boot:\(.bootTime // "?")ms | density:\(.density // "?") | \(.screen // "?")" ' 2>/dev/null || echo "❌ Query failed" case fps perf echo "📈 FPS data from recent sessions..." curl -s "$base_url?type=perf&limit=$limit$piece_param" | jq -r ' .results[] | "\(.timestamp | split(".")[0] | gsub("T"; " ")) | \(.piece // "?") | samples:\(.samples | length // 0) | density:\(.density // "?")" ' 2>/dev/null || echo "❌ Query failed" case errors error echo "❌ Recent bundle errors..." curl -s "$base_url?type=error&limit=$limit$piece_param" | jq -r ' .results[] | "\(.timestamp | split(".")[0] | gsub("T"; " ")) | \(.piece // "?") | \(.message // "unknown")" ' 2>/dev/null || echo "❌ Query failed" case summary echo "📊 Bundle telemetry summary (last 24h)..." curl -s "$base_url?type=summary" | jq '.' 2>/dev/null || echo "❌ Query failed" case raw echo "🔍 Raw telemetry documents..." curl -s "$base_url?type=boot&limit=$limit$piece_param" | jq '.' 2>/dev/null || echo "❌ Query failed" case '*' echo "📊 Bundle Telemetry Query Tool" echo "" echo "Usage: ac-telemetry <command> [options]" echo "" echo "Commands:" echo " ac-telemetry recent - Show recent bundle boots" echo " ac-telemetry fps - Show FPS data from sessions" echo " ac-telemetry errors - Show recent errors" echo " ac-telemetry summary - Show 24h summary stats" echo " ac-telemetry raw - Show raw JSON" echo "" echo "Options:" echo " --limit=N Number of results (default: 20)" echo " --piece=NAME Filter by piece name" endend
# 🎮 Xbox helper - connect to Xbox on local networkfunction ac-xbox --description "Interact with Xbox on 1244 Innes Ave LAN" set -l vault_path "$HOME/aesthetic-computer/aesthetic-computer-vault/machines.json" if not test -f $vault_path echo "❌ machines.json not found" return 1 end set -l xbox_ip (jq -r '.machines.xbox.ip' $vault_path) set -l portal_port (jq -r '.machines.xbox.devicePortalPort' $vault_path) if test -z "$xbox_ip" -o "$xbox_ip" = "null" echo "❌ Xbox not configured in machines.json" return 1 end switch $argv[1] case ping echo "🏓 Pinging Xbox at $xbox_ip..." ssh jas@host.docker.internal "ping -c 3 $xbox_ip" case portal echo "🌐 Xbox Device Portal: https://$xbox_ip:$portal_port" echo " Opening in browser..." ssh jas@host.docker.internal "open 'https://$xbox_ip:$portal_port'" case info echo "🎮 Xbox Series X" echo " IP: $xbox_ip" echo " Device Portal: https://$xbox_ip:$portal_port" echo " Network: 1244 Innes Ave Home LAN" echo "" echo "Deploy from Mac:" echo " 1. Push to trigger GitHub Actions build" echo " 2. Download .msix from Actions artifacts" echo " 3. ac-xbox deploy <path-to.msix>" echo "" echo "Or open Device Portal: ac-xbox portal" case tunnel echo "🚇 Creating SSH tunnel to Xbox Device Portal..." echo " Local: https://localhost:11443 → Xbox: $xbox_ip:$portal_port" echo " Press Ctrl+C to stop" ssh -L 11443:$xbox_ip:$portal_port jas@host.docker.internal -N case deploy if test (count $argv) -lt 2 echo "Usage: ac-xbox deploy <package.msix>" echo "" echo "Get the package from GitHub Actions:" echo " 1. Push changes to xbox/ folder" echo " 2. Go to Actions → Build Xbox App → Download artifact" echo " 3. ac-xbox deploy ~/Downloads/AestheticComputer.msix" return 1 end set -l pkg $argv[2] if not test -f $pkg echo "❌ Package not found: $pkg" return 1 end echo "📦 Deploying $pkg to Xbox..." # Upload via Mac host curl to Device Portal ssh jas@host.docker.internal "curl -k -X POST -F 'file=@$pkg' 'https://$xbox_ip:$portal_port/api/app/packagemanager/package'" case build echo "🔨 Triggering GitHub Actions build..." echo " This will build the Xbox app in the cloud." gh workflow run xbox-build.yml echo "" echo " Watch progress: gh run watch" echo " Download when done: gh run download" case '*' echo "🎮 Xbox Helper (1244 Innes Ave LAN)" echo "" echo "Usage: ac-xbox <command>" echo "" echo "Commands:" echo " ac-xbox ping - Check if Xbox is reachable" echo " ac-xbox portal - Open Device Portal in browser" echo " ac-xbox info - Show connection info" echo " ac-xbox tunnel - Create SSH tunnel to Device Portal" echo " ac-xbox build - Trigger GitHub Actions build" echo " ac-xbox deploy <file.msix> - Deploy package to Xbox" echo "" echo "Xbox IP: $xbox_ip" endend
if set -q AC_TASK_LAUNCH set -e AC_TASK_LAUNCH # Clear it so nested shells don't re-trigger aestheticend