···11+Scripts
22+=======
33+44+Various scripts and Nushell modules that I maintain or use.
55+66+# Nushell Modules
77+88+## Nebula
99+Wrapper to make setting up new hosts in a nebula network easier.
1010+1111+1212+# Scripts
1313+1414+## Nushell
1515+1616+### ADB Auto Connect
1717+Automatically find and connect to an Android device with Wireless Debugging over the local network.
1818+1919+You'll need to create a file at `~/.config/adb-auto-connect.nuon` with the MAC Addresses you want to search for. For example:
2020+```json
2121+[
2222+ "ff:ff:ff:ff:ff:ff",
2323+ "aa:aa:aa:aa:aa:aa"
2424+]
2525+```
2626+2727+### Auto Benchmark
2828+Set up and run a Blender benchmark on the specified device, then automatically compare your results to the results on [Blender Open Data](https://opendata.blender.org/).
2929+3030+### ln-bin
3131+Quickly symlink executable files to `~/.local/bin/`
3232+3333+### yt-rss
3434+Get an RSS feed for the specified YouTube channel.
3535+3636+## Bash
3737+3838+### oscavmgr-launch
3939+Slightly modified version of Galister's oscavmgr start script. Launches oscavmgr and VrcAdvert together.
+15
bash/oscavmgr-launch.sh
···11+#!/usr/bin/env bash
22+33+# stop VrcAdvert after OscAvMgr quits
44+trap 'jobs -p | xargs kill' EXIT
55+66+VrcAdvert OscAvMgr 9402 9002 --tracking &
77+88+# If using WiVRn
99+oscavmgr openxr
1010+1111+## If using ALVR
1212+#oscavmgr alvr
1313+1414+## If using Project Babble and/or EyeTrackVR
1515+#oscavmgr babble
+33
nushell/adb-auto-connect.nu
···11+#!/usr/bin/nu
22+33+const mac = open "~/.config/adb-auto-connect.nuon"
44+55+def main [] {
66+ let connected = (adb devices -l | lines | skip | drop | length) > 0
77+88+ if $connected {
99+ print "Already Connected"
1010+ } else {
1111+ let ip = ip neigh
1212+ | lines
1313+ | parse "{ip} {type} {device} lladdr {mac} {status}"
1414+ | where mac in $mac
1515+ | get ip.0
1616+1717+ let port = nmap -p 37000-65000 --open --max-retries 1 --min-rate 500000 -T5 -oX - $ip
1818+ | lines
1919+ | skip 4
2020+ | str join "\n"
2121+ | from xml
2222+ | get content
2323+ | where tag == host
2424+ | get content.0
2525+ | where tag == ports
2626+ | get content.0
2727+ | where tag == port
2828+ | get attributes.portid.0
2929+3030+ adb connect $"($ip):($port)"
3131+ }
3232+3333+}
+63
nushell/auto-benchmark.nu
···11+#!/usr/bin/nu
22+33+use std [log assert]
44+55+def main [
66+ --debug: string # NUON
77+] {
88+ log info "Getting latest version"
99+ let version = benchmark-launcher-cli blender list
1010+ | parse "{version}\t{download_size}"
1111+ | get 0.version
1212+1313+ log info "Getting latest scenes"
1414+ let scenes = benchmark-launcher-cli scenes list --blender-version=($version)
1515+ | parse "{name}\t{download_size}"
1616+ | get name
1717+1818+ log info "Selecting device"
1919+ let device = benchmark-launcher-cli devices --blender-version=($version)
2020+ | parse "{device}\t{type}"
2121+ | each {|e| $e.type + ": " + $e.device}
2222+ | input list "Choose a device to benchmark"
2323+ | split row ": "
2424+ | {name: $in.1, type: $in.0}
2525+2626+ log info "Starting benchmark"
2727+ let results = if $debug != null {$debug | from nuon} else {
2828+ benchmark-launcher-cli benchmark ...$scenes --blender-version=($version) --device-type=($device.type) --device-name=($device.name) --json | from json
2929+ }
3030+ log info "Done!"
3131+3232+ let sum = $results.stats.samples_per_minute | math sum
3333+ # HACK: It doesn't look like open data has a normal API so we have to do a bit of a workaround
3434+ let opendata = http get $"https://opendata.blender.org/benchmarks/query/?device_name=($device.name)&blender_version=($version)&response_type=datatables"
3535+ | $"[($in.columns.display_name | to nuon); ($in.rows | to nuon | str substring 1..-2)]"
3636+ | from nuon
3737+ let opendata_median = $opendata | get 'Median Score' | math median
3838+ let diff = $sum - $opendata_median
3939+4040+ log debug ""
4141+ log debug $"Blender Version: ($version)"
4242+ log debug $"Benchmark Launcher Version: ($results.0.benchmark_launcher.label)"
4343+ log debug $"Benchmark Script Version: ($results.0.benchmark_script.label)"
4444+ log debug $"Device: ($device.name)"
4545+ log debug $"Scenes: ($scenes | str join ', ')"
4646+ log info ""
4747+ log info "Results:"
4848+ $results
4949+ | each {|result|
5050+ log debug $"-> ($result.scene.label): (ansi blue)($result.stats.samples_per_minute)(ansi reset) samples/min"
5151+ }
5252+ log info $"-> Total: (ansi blue)($sum)(ansi reset) samples/min"
5353+ log info $"-> Open Data: (ansi blue)($opendata_median)(ansi reset) samples/min"
5454+ log info $"-> Delta: (
5555+ if $diff < 0 {
5656+ (ansi red)
5757+ } else if $diff == 0 {
5858+ (ansi grey) + "±"
5959+ } else {
6060+ (ansi green) + "+"
6161+ }
6262+ )($diff)(ansi reset) samples/min"
6363+}
+6
nushell/ln-bin.nu
···11+#!/usr/bin/nu
22+33+def main [bin: path, name?: string] {
44+ let name = $name | default ($bin | path parse | get stem)
55+ ln -sf ($bin | path expand) $"($env.HOME)/.local/bin/($name)"
66+}
+44
nushell/nebula/mod.nu
···11+# SPDX-License-Identifier: AGPL-3.0-only
22+# SPDX-FileCopyrightText: 2025 Shiloh Fen <shiloh@shilohfen.com>
33+44+const path_data: path = "~/.local/share/nebula" | path expand
55+const path_ca_cert: path = $path_data | path join "ca.crt"
66+const path_ca_key: path = $path_data | path join "ca.key"
77+const path_state: path = $path_data | path join "state.nuon"
88+99+export def sign [
1010+ name: string
1111+ groups: list<string>
1212+]: nothing -> record<path_cert: path, path_key: path> {
1313+ if not ($path_ca_key | path exists) {
1414+ error make {
1515+ text: "No CA key found."
1616+ help: "Run submodule `ca` to generate a CA before attempting to sign a device cert."
1717+ }
1818+ }
1919+2020+ let tmp = mktemp -td "nebula-XXXXX"
2121+ let path_device_cert = $tmp | path join $"($name).crt"
2222+ let path_device_key = $tmp | path join $"($name).key"
2323+ let ip_part = (open $path_state | get last_ip) + 1
2424+2525+ nebula-cert sign -name $name -ca-crt $path_ca_cert -ca-key $path_ca_key -ip $"192.168.100.($ip_part)/24" -groups ($groups | str join ",") -out-crt $path_device_cert -out-key $path_device_key
2626+2727+ {last_ip: $ip_part} | save -f $path_state
2828+2929+ {
3030+ path_cert: $path_device_cert
3131+ path_key: $path_device_key
3232+ }
3333+}
3434+3535+export def ca [
3636+ name: string
3737+] {
3838+ mkdir $path_data
3939+ nebula-cert ca -name $name -out-crt $path_ca_cert -out-key $path_ca_key -encrypt
4040+4141+ print "Certificate will be valid for one year. Be sure to set up an alert or calendar event to rotate your CA and certificates before then to ensure continued connectivity!"
4242+4343+ {last_ip: 0} | save -f $path_state
4444+}