first commit

This commit is contained in:
Luxferre
2025-10-19 11:55:24 +03:00
commit 7954949269
8 changed files with 620 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
# sh-goodies: a useful collection of single-file, POSIX-compatible shell scripts
## List
* [DaemonTamer V2](./dt): local service manager to daemonize any command
* [Outport](./outport): expose reverse tunnels via SSH
* [shee.sh](./shee.sh): simple LLM chat application for OpenAI-compatible endpoints
* [ShellFlow](./flow.sh): simple workflow orchestration script
* [LShar](./lshar.sh): create self-extracting archives in the form of shell scripts
* [ShellBeat](./shellbeat.sh): a [bytebeat](https://stellartux.github.io/websynth/guide.html) audio formula player in pure shell (also depends on xxd)
* [GHMD](./ghmd.sh): a command suite to set up your own self-hosted Git repositories
Executable
+106
View File
@@ -0,0 +1,106 @@
#!/bin/sh
# DaemonTamer: simple and portable local service manager in pure sh
# that allows to quickly daemonize any command and manage it as a service
# Depends on POSIX versions of sh, nohup, cut and grep
# the unreg option also depends on mktemp (not POSIX but present everywhere)
# Run sh dt help to see the parameters
# Created by Luxferre in 2025, released into public domain with no warranties
err() {
echo $*
exit 1
}
msgexit() {
echo $*
exit 0
}
find_service() {
sline="$(grep -E "^$1\s+" $CONF)"
[ -z "$sline" ] && err "No such service found in the config!"
echo "$sline"
}
CONF="$HOME/.dt.conf"
TMPDIR="$(dirname $(mktemp -u))"
srv_name="$2"
[ "$1" != "help" ] && [ -z "$srv_name" ] && err "Service name is required!"
pidfile="$TMPDIR/$srv_name.pid"
case $1 in
help)
echo 'DaemonTamer v2'
echo '--------------'
echo "Usage: $0 {reg|unreg|start|stop|restart} SERVICE_NAME [REG_PARAMS]"
echo 'Parameters for the reg subcommand:'
echo "$0 reg 'COMMAND' [WORKDIR] [STDOUT_LOG_FILE] [STDERR_LOG_FILE] [PRESTART] [POSTSTART] [PRESTOP] [POSTSTOP]"
echo '(the COMMAND must be specified in single quotes and without nohup)'
echo
echo 'Created by Luxferre in 2025, released into public domain with no warranties'
;;
reg)
srv_cmd="$3"
[ -z "$srv_cmd" ] && err "Service runner command is required!"
srv_wdir="$4"
srv_outlog="$5"
srv_errlog="$6"
srv_prestart="$7"
srv_poststart="$8"
srv_prestop="$9"
srv_poststop="$10"
[ -z "$srv_wdir" ] && srv_wdir='-'
[ -z "$srv_outlog" ] && srv_outlog='/dev/null'
[ -z "$srv_errlog" ] && srv_errlog='/dev/null'
[ -z "$srv_prestart" ] && srv_prestart='-'
[ -z "$srv_poststart" ] && srv_poststart='-'
[ -z "$srv_prestop" ] && srv_prestop='-'
[ -z "$srv_poststop" ] && srv_poststop='-'
printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" "$srv_name" "$srv_cmd" \
"$srv_wdir" "$srv_outlog" "$srv_errlog" "$srv_prestart" "$srv_poststart" \
"$srv_prestop" "$srv_poststop" >> $CONF
echo "Service $srv_name registered"
;;
unreg)
srvline="$(find_service "$srv_name")"
TCONF="$(mktemp)"
grep -v "$srv_name" $CONF > $TCONF && mv $TCONF $CONF
echo "Service $srv_name unregistered"
;;
start)
[ -f "$pidfile" ] && msgexit "Service $srv is already running";
srvline="$(find_service "$srv_name")"
srv_cmd="$(echo "$srvline" | cut -f2)"
srv_wdir="$(echo "$srvline" | cut -f3)"
srv_outlog="$(echo "$srvline" | cut -f4)"
srv_errlog="$(echo "$srvline" | cut -f5)"
srv_prestart="$(echo "$srvline" | cut -f6)"
srv_poststart="$(echo "$srvline" | cut -f7)"
echo "Starting service $srv_name"
[ "$srv_wdir" != "-" ] && cd "$srv_wdir"
[ "$srv_prestart" != "-" ] && $SHELL -c "$srv_prestart"
nohup $srv_cmd >$srv_outlog 2>$srv_errlog & echo $! > $pidfile
[ "$srv_poststart" != "-" ] && $SHELL -c "$srv_poststart"
echo "Service $srv_name started"
;;
stop)
[ ! -f "$pidfile" ] && msgexit "Service $srv is not running";
srvline="$(find_service "$srv_name")"
srv_prestop="$(echo "$srvline" | cut -f8)"
srv_poststop="$(echo "$srvline" | cut -f9)"
echo "Stopping service $srv_name"
[ "$srv_prestop" != "-" ] && $SHELL -c "$srv_prestop"
kill $(cat $pidfile)
rm -f $pidfile
[ "$srv_poststop" != "-" ] && $SHELL -c "$srv_poststop"
echo "Service $srv_name stopped"
;;
restart)
srvline="$(find_service "$srv_name")"
$SHELL $0 stop $srv_name
$SHELL $0 start $srv_name
;;
*)
echo 'Invalid operation! Pick start, stop, restart, reg, unreg or help'
;;
esac
Executable
+60
View File
@@ -0,0 +1,60 @@
#!/bin/sh
#
# ShellFlow: the simplest workflow orchestrator in under
# 20 SLOC of POSIX shell, using Unix pipelines
#
# Usage: echo some_input | /path/to/flow.sh /path/to/workflow
#
# Exposes the following variables to workflows and tasks:
# - SFLIB - resolved path to this file
# - WFDIR - directory of the workflow
#
# Also exposes the debugout function to output to stderr
#
# Typical workflow script structure: ./task1 | ./task2 | ./task3 ...
# (the workflow directory becomes the current working directory)
#
# Data format: workflow name, tab character, all other input
#
# Typical task script structure (if shell-based):
# #!/bin/sh
# . $SFLIB # source the library part of the script
# do_task() { # accepts workflow name into $1 and all input into $2
# # your actions here
# }
# run-task
#
# You can call different workflows from inside tasks by referring to
# this very script using the $SFLIB environment variable or by calling
# the sfrun function
#
# Created by Luxferre in 2025, released into public domain
# ShellFlow task function library
# print to stderr
debugout() { printf '%s\n' "$*" >&2; }
# format the message and print to stdout
flowout() { printf '%s\t%s' "$1" "$2"; }
# use this in all task definitions
run-task() {
input="$(cat)"
wf="$(printf '%s' "$input" | cut -f 1)"
body="$(printf '%s' "$input" | cut -f 2-)"
flowout "$wf" "$(do_task "$wf" "$body")"
}
# ShellFlow entry point
sfrun() {
SFLIB="$(readlink -f "$0")"
wfbase="$(basename "$1")"
WFDIR="$(readlink -f $(dirname -- "$1"))"
cd "$WFDIR"
debugout "Starting workflow $wfbase..."
flowout $wfbase "$(cat)" | SFLIB="$SFLIB" WFDIR="$WFDIR" $SHELL $wfbase | cut -f 2-
}
[ "$(basename "$0")" == "flow.sh" ] && sfrun $*
Executable
+157
View File
@@ -0,0 +1,157 @@
#!/bin/sh
# GHMD is a shell script for setting up any of the following things:
# 1) a Git user on a remote instance (initial setup)
# 2) a private Git repo on the remote instance
# 3) a public Git repo using git:// protocol on the remote instance (if it uses systemd)
# 4) Git daemon (serving git:// protocol) on the remote instance
# 5) access via the Git daemon to individual repositories
# 6) repository description and clone URL for some services like stagit
# Usage: ghmd.sh [newsrv | newrepo | gitd-sd | gitd-publish | gitd-unpublish | set-repo-desc | set-repo-url | set-repo-owner] user@host [name] ("[desc|url]")
# regardless of the actions, the user must have root permissions
# prerequisite: git package must be already installed on the server
# Created by Luxferre in 2024, released into public domain
GITUSR="git"
PARAM="$1"
USERHOST="$2"
THOST="${USERHOST##*@}"
SSHCMD="ssh $USERHOST" # instance ssh cmd
help() {
cat <<EOF
GHMD: single shell script for self-hosted Git repository management
Initialize Git user on a new server (must have Git installed):
$0 newsrv root@hostname
Create a new Git repository (e.g. git@hostname:myrepo.git):
$0 newrepo root@hostname myrepo
Set up Git protocol daemon (if the host is running systemd):
$0 gitd-sd root@hostname
Publish an existing Git repository on the daemon (e.g. git://hostname/myrepo.git):
$0 gitd-publish root@hostname myrepo
Unpublish an existing Git repository on the daemon:
$0 gitd-unpublish root@hostname myrepo
Set the (displayed) description of an existing Git repository:
$0 set-repo-desc root@hostname myrepo "This is a cool repo"
Set the (displayed) clone URL of an existing Git repository:
$0 set-repo-url root@hostname myrepo git://hostname/myrepo.git
Set the (displayed) owner name of an existing Git repository:
$0 set-repo-owner root@hostname myrepo "Me, the Author"
Created by Luxferre in 2024, released into public domain
EOF
}
case "$PARAM" in
"help")
help
;;
"newsrv") # set up Git user on the server
[[ -z "$USERHOST" ]] && echo "Error: no user and hostname!" && exit 1
$SSHCMD "useradd ${GITUSR}; passwd ${GITUSR}; mkdir -p /home/${GITUSR}/.ssh; echo 'no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty' > /home/${GITUSR}/.ssh/authorized_keys; chown -R ${GITUSR} /home/${GITUSR}/.ssh"
echo "Now, you'll need to enter the newly created ${GITUSR} user password:"
ssh-copy-id ${GITUSR}@${THOST}
$SSHCMD "chsh $GITUSR -s $(which git-shell)"
echo "Git user set up on ${THOST}. Now use $0 newrepo ${GITUSR}@${THOST} [name] to set up new Git repos"
;;
"newrepo") # set up a new Git repo
[[ -z "$USERHOST" ]] && echo "Error: no user and hostname!" && exit 1
REPONAME="$3"
[[ -z "$REPONAME" ]] && echo "Error: no repository name!" && exit 1
REPODIR="${REPONAME}.git"
FULLREPODIR="/home/${GITUSR}/$REPODIR"
$SSHCMD "mkdir -p $FULLREPODIR;cd $FULLREPODIR;git init --bare --shared;chown -R ${GITUSR}:${GITUSR} ."
echo "Empty repository created at ${USERHOST}:${REPODIR}"
;;
"set-repo-desc") # set the repo description for some services
[[ -z "$USERHOST" ]] && echo "Error: no user and hostname!" && exit 1
REPONAME="$3"
[[ -z "$REPONAME" ]] && echo "Error: no repository name!" && exit 1
VALUE="$4" # it may be empty
REPODIR="${REPONAME}.git"
FULLREPODIR="/home/${GITUSR}/$REPODIR"
$SSHCMD "cd $FULLREPODIR;echo \"$VALUE\" > description;chown -R ${GITUSR}:${GITUSR} description"
echo "Description set for ${REPODIR}"
;;
"set-repo-url") # set the repo clone URL for some services
[[ -z "$USERHOST" ]] && echo "Error: no user and hostname!" && exit 1
REPONAME="$3"
[[ -z "$REPONAME" ]] && echo "Error: no repository name!" && exit 1
VALUE="$4" # it may be empty
REPODIR="${REPONAME}.git"
FULLREPODIR="/home/${GITUSR}/$REPODIR"
$SSHCMD "cd $FULLREPODIR;echo \"$VALUE\" > url;chown -R ${GITUSR}:${GITUSR} url"
echo "Clone URL set for ${REPODIR}"
;;
"set-repo-owner") # set the repo owner name for some services
[[ -z "$USERHOST" ]] && echo "Error: no user and hostname!" && exit 1
REPONAME="$3"
[[ -z "$REPONAME" ]] && echo "Error: no repository name!" && exit 1
VALUE="$4" # it may be empty
REPODIR="${REPONAME}.git"
FULLREPODIR="/home/${GITUSR}/$REPODIR"
$SSHCMD "cd $FULLREPODIR;echo \"$VALUE\" > owner;chown -R ${GITUSR}:${GITUSR} owner"
echo "Owner name set for ${REPODIR}"
;;
"gitd-sd") # set up the Git daemon using a systemd unit
[[ -z "$USERHOST" ]] && echo "Error: no user and hostname!" && exit 1
TMPUNIT="/tmp/git-daemon.service"
cat << EOF > $TMPUNIT
[Unit]
Description=Start Git Daemon
[Service]
ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/home/${GITUSR} /home/${GITUSR}
Restart=always
RestartSec=500ms
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=git-daemon
User=$GITUSR
Group=$GITUSR
[Install]
WantedBy=multi-user.target
EOF
scp $TMPUNIT ${USERHOST}:/etc/systemd/system/git-daemon.service
$SSHCMD "systemctl enable git-daemon; systemctl start git-daemon"
echo "Git daemon installed on ${THOST} via systemd"
rm -f $TMPUNIT
;;
"gitd-publish") # make a repository public via the Git daemon
[[ -z "$USERHOST" ]] && echo "Error: no user and hostname!" && exit 1
REPONAME="$3"
[[ -z "$REPONAME" ]] && echo "Error: no repository name!" && exit 1
REPODIR="${REPONAME}.git"
ACCFILE="/home/${GITUSR}/${REPODIR}/git-daemon-export-ok"
$SSHCMD "touch $ACCFILE; chown $GITUSR $ACCFILE"
echo "Repository git://${THOST}/$REPODIR made public"
;;
"gitd-unpublish") # revoke repository publishing via the Git daemon
[[ -z "$USERHOST" ]] && echo "Error: no user and hostname!" && exit 1
REPONAME="$3"
[[ -z "$REPONAME" ]] && echo "Error: no repository name!" && exit 1
REPODIR="${REPONAME}.git"
ACCFILE="/home/${GITUSR}/${REPODIR}/git-daemon-export-ok"
$SSHCMD "rm -f $ACCFILE"
echo "Repository git://${THOST}/$REPODIR made private"
;;
*)
help
esac
+70
View File
@@ -0,0 +1,70 @@
#!/bin/sh
# lshar.sh - Luxferre's shell archiver
# Outputs to stdout only (for GNU and BSD shar compatibility)
# No external deps for .shars (sh commands required: mkdir, printf)
# Archive creation also depends on sort (Busybox, Toybox or GNU Coreutils)
# Works with any text or binary files and directory structures
# Created by Luxferre in 2023, released into public domain
# a portable, compact, no-deps serializer of any file into printf sequence
f2p() {
c=0 # init counter
l="$2" # get chunk length
[ -z "$l" ] && l=4096 # 4096-byte chunks by default (shouldn't be a problem)
cprefix="printf '%b' '" # pattern to prepend to every chunk
csuffix="' >> '$1'\n" # pattern to append to every chunk
printf '%b' "$cprefix" # always start with the prefix
IFS='' # separator
while read -r -s -d '' -n1 b; do # convert every byte
if [ "$c" == "$l" ]; then # roll over and output suffix+prefix
c=0 # reinit counter
printf '%b' "$csuffix" # end the chunk
printf '%b' "$cprefix" # begin a new chunk
else
c="$(( c + 1 ))" # increment c
fi
bcode="$(printf '%d' "'$b")" # get the ASCII code of the byte
if [ "$bcode" -gt 32 ] && [ "$bcode" -lt 127 ] &&
[ "$bcode" -ne 39 ] && [ "$bcode" -ne 92 ]
then # printable and not a single quote or a backslash
printf '%b' "$b" # codes from 34 to 126 are safe to print
else # subject to escaping, print the hex representation
printf '\\x%02x' "$bcode"
fi
done <"$1"
printf '%b' "$csuffix" # always end with the suffix
}
# main code
LF=$'\n' # cache the linefeed character
flist='' # pre-build the file list from args
dlist='' # pre-build the directory list from args
for i; do # iterate over args
bname="${i##*/}" # get the basename
if [ -d "$i" ]; then # it's a directory, we skip . and ..
[ "$bname" == '.' ] || [ "$bname" == '..' ] && continue
dlist="${dlist}${i%/}${LF}" # append the directory
else # it's a file
dname="${i%%$bname}" # get the basedir
dlist="${dlist}${dname%/}${LF}" # append the basedir
flist="${flist}${i}${LF}" # append the file
fi
done
flist="$(printf '%s' "$flist" | sort -u)" # remove file duplicates
dlist="$(printf '%s' "$dlist" | sort -u)" # remove directory duplicates
printf '%s\n' '#!/bin/sh' # output the shebang, just in case
IFS="$LF" # update the separator
for indir in $dlist; do # create the directory structure first
[ -z "$indir" ] && continue # skip empty names
printf '# dir: %s\n' "$indir"
printf "printf 'c - %%s\\\n' '%s'\n" "$indir" # announce creation
printf "mkdir -p '%s' > /dev/null 2>&1\n" "$indir" # create the dir
done
for infile in $flist; do # iterate over the file list afterwards
[ -z "$infile" ] && continue # skip empty names
printf '# file: %s\n' "$infile"
printf "printf 'x - %%s\\\n' '%s'\n" "$infile" # announce unrolling
printf "printf '' > '%s'\n" "$infile" # clear the file
f2p "$infile" # run the serializer
IFS="$LF" # update the separator
done
Executable
+31
View File
@@ -0,0 +1,31 @@
#!/bin/sh
# Outport: a simple script to expose reverse tunnels via SSH
# Usage: outport.sh {start|stop} user@host [port1] [port2] ...
# Created by Luxferre in 2025, released into public domain
SOCKET_PATH=/var/run/sshcontrol.sock
ACTION="$1"
TARGET="$2"
shift
shift
PORT_OPTS=""
case "$ACTION" in
start)
while :; do
[ -z "$1" ] && break
PORT_OPTS="$PORT_OPTS -R :$1:localhost:$1"
shift
done
MISC_OPTS="-o ServerAliveInterval=60 -o ServerAliveCountMax=15"
ssh -f -N -M -S $SOCKET_PATH $PORT_OPTS $MISC_OPTS $TARGET
echo "Tunnel started"
;;
stop)
ssh -S $SOCKET_PATH -O cancel $TARGET
ssh -S $SOCKET_PATH -O exit $TARGET
echo "Tunnel stopped"
;;
*)
echo "Invalid action (specify start or stop)"
;;
esac
Executable
+162
View File
@@ -0,0 +1,162 @@
#!/bin/sh
# shee.sh: a shell-based OpenAI-compatible local chat
# Depends on curl, jq, file and mktemp, otherwise fully POSIX-compatible
# (for history/line editing, run it via rlwrap)
# Configurable in-chat and with environment variables
# Created by Luxferre in 2025, released into public domain
# all configuration variables are here
[ -z "$SHEESH_API_ROOT" ] && SHEESH_API_ROOT='https://text.pollinations.ai/openai'
[ -z "$SHEESH_MODEL" ] && SHEESH_MODEL='openai-fast'
[ -z "$SHEESH_TEMP" ] && SHEESH_TEMP='0.6'
[ -z "$SHEESH_SYSPROMPT" ] && SHEESH_SYSPROMPT='You are a helpful assistant.'
histfile="$(mktemp -t furrfu.XXXXXX)"
datafile="$(mktemp -t furrfu.XXXXXX)"
addhistory() {
role="$1"; shift
jq -nc --slurpfile h $histfile --arg r "$role" --arg a "$*" '$h[0]+[{"role":$r,"content":$a}]' > ${histfile}.new
mv ${histfile}.new $histfile
}
prompt() { printf "\e[1;32m>> \e[0m"; }
clearhist() {
printf '[]' > $histfile
addhistory system "$SHEESH_SYSPROMPT"
}
startswith() { case $1 in "$2"*) true;; *) false;; esac; }
setsys() {
SHEESH_SYSPROMPT="$*"
clearhist
}
setsysfile() { setsys "$(<"$1")"; }
addfile() {
tmp="$(mktemp -t furrfufile.XXXXXXX)"
fname="$(basename $1)"
if startswith "$1" "http*://"; then
[ -z "$fname" || "$fname" == "/" ] && $fname="${1//\//_}"
curl -sSLN $1 > $tmp
else
cp "$1" $tmp
fi
if file -b --mime-encoding $tmp | grep binary; then
uuencode -m $tmp $fname > ${tmp}.new
mv ${tmp}.new $tmp
else
echo "Contents of ${fname}:" >> ${tmp}.new
cat $tmp >> ${tmp}.new
mv ${tmp}.new $tmp
fi
jq -nc --slurpfile h $histfile --rawfile a "$tmp" '$h[0]+[{"role":"user","content":$a}]' > ${histfile}.new
mv ${histfile}.new $histfile
rm $tmp
}
listmodels() {
echo 'Available models:'
curl -sSL ${SHEESH_API_ROOT}/models \
${SHEESH_UA:+-H "User-Agent: $SHEESH_UA"} \
${SHEESH_REFERER:+-H "Referer: $SHEESH_REFERER"} \
${SHEESH_ORIGIN:+-H "Origin: $SHEESH_ORIGIN"} \
${OPENAI_API_KEY:+-H "Authorization: Bearer $OPENAI_API_KEY"} \
${SHEESH_COOKIES:+-H "Cookie: $SHEESH_COOKIES"} \
| jq -r '.data[].id' | while read -r modelname; do
printf '\e[0;36m* %s\e[0m\n' "$modelname"
done
}
help() {
printf 'Current settings:\n\n\e[2;37mEndpoint: %s\n' "$SHEESH_API_ROOT"
printf 'Model: %s\n' "$SHEESH_MODEL"
printf 'Temperature: %.2f\n' "$SHEESH_TEMP"
printf 'System prompt: "%s"\e[0m\n\n' "$SHEESH_SYSPROMPT"
echo 'Available commands:'
echo
echo '* /models: list available models'
echo '* /model: change the current model'
echo '* /add: add a file to the context'
echo '* /temp: change the current temperature'
echo '* /endpoint: change the current API endpoint'
echo '* /clear: clear the session context'
echo '* /sys [prompt]: set a system prompt and clear the context'
echo '* /sysload [file]: load the system prompt from a file and clear the context'
echo '* /bye: exit the chat immediately'
echo
echo 'Available environment variables for configuration:'
echo
echo '* OPENAI_API_KEY: API key string (default empty)'
echo '* SHEESH_API_ROOT: API endpoint (default https://text.pollinations.ai/openai)'
echo '* SHEESH_MODEL: model ID (default openai-fast)'
echo '* SHEESH_TEMP: model temperature (default 0.6)'
echo '* SHEESH_SYSPROMPT: system prompt (default "You are a helpful assistant.") '
echo '* SHEESH_UA: user agent override (default empty)'
echo '* SHEESH_REFERER: Referer header value (default empty)'
echo '* SHEESH_ORIGIN: Origin header value (default empty)'
echo '* SHEESH_COOKIES: Cookie header value (default empty)'
echo
}
trap cleanup SIGINT SIGABRT SIGHUP SIGQUIT
cleanup() {
rm $histfile $datafile
printf '\nBye!\n'
exit 0
}
hdrline='----------------------'
title='SHEE.SH v2 by Luxferre'
printf '\e[1;36m%s\n%s\n%s\e[0m\n' "$hdrline" "$title" "$hdrline"
printf '\e[2;37mEndpoint: %s\n' "$SHEESH_API_ROOT"
printf 'Model: %s\n' "$SHEESH_MODEL"
printf 'Temperature: %.2f\n' "$SHEESH_TEMP"
printf '\e[1;36m%s\e[0m\n' "$hdrline"
setsys "$SHEESH_SYSPROMPT"
while prompt && read -r line; do
[ -z "$line" ] && continue
[ "$line" == "/bye" ] && break
[ "$line" == "/clear" ] && clearhist && echo "Session cleared" && continue
[ "$line" == "/help" ] && help && continue
[ "$line" == "/models" ] && listmodels && continue
startswith "$line" '/sys ' && setsys "${line#\/sys }" && echo "System prompt set and session cleared" && continue
startswith "$line" '/sysload ' && setsysfile "${line#\/sysload }" && echo "System prompt loaded and session cleared" && continue
startswith "$line" '/temp ' && SHEESH_TEMP="${line#\/temp }" && echo "Temperature set to $SHEESH_TEMP" && continue
startswith "$line" '/model ' && SHEESH_MODEL="${line#\/model }" && echo "Model set to $SHEESH_MODEL" && continue
startswith "$line" '/endpoint ' && SHEESH_API_ROOT="${line#\/endpoint }" && echo "API endpoint set to $SHEESH_API_ROOT" && continue
if startswith "$line" '/add '; then
filelist="${line#\/add }"
for f in $filelist; do
addfile "$f"
printf "File %s added to the context\n" "$f"
done
continue
fi
# commands end, normal message processing start
addhistory user "$line"
jq -nc --arg m "$SHEESH_MODEL" --arg t "$SHEESH_TEMP" --slurpfile msgs $histfile \
'{model:$m,stream:true,messages:$msgs[0],temperature:($t|tonumber)}' > $datafile
curl -sSLN "${SHEESH_API_ROOT}/chat/completions" \
-H 'Content-Type: application/json' \
${SHEESH_UA:+-H "User-Agent: $SHEESH_UA"} \
${SHEESH_REFERER:+-H "Referer: $SHEESH_REFERER"} \
${SHEESH_ORIGIN:+-H "Origin: $SHEESH_ORIGIN"} \
${OPENAI_API_KEY:+-H "Authorization: Bearer $OPENAI_API_KEY"} \
${SHEESH_COOKIES:+-H "Cookie: $SHEESH_COOKIES"} \
-H 'Accept: text/event-stream' --data-binary "@$datafile" 2>/dev/null | \
while IFS= read -r chunk; do
[ -z "$chunk" ] && continue
data="${chunk#data: }"
[ "$data" == "[DONE]" ] && break
delta=''
startswith "$data" '{' && delta="$(printf '%s' "$data" | jq -rj '.choices[0].delta.content // empty' 2>/dev/null;printf x)"
delta="${delta%?}"
[ -n "$delta" ] && printf "\e[0;33m%s\e[0m" "$delta"
tailbuf="$tailbuf$delta"
done
echo
addhistory assistant "$tailbuf"
tailbuf=""
line=''
done
cleanup
Executable
+23
View File
@@ -0,0 +1,23 @@
#!/bin/sh
# ShellBeat: a Busybox-compatible Bytebeat player with purely shell arithmetic syntax
# depends on xxd only
# By default, SoX is also required, or change the PLAYCMD variable to your engine
# (see some commented examples, some of them only accept 8Khz sample rate)
# Usage: shellbeat.sh 'formula'[ samplerate=8000]
# Example test on "42 melody": sh shellbeat.sh 't * (42 & (t >> 10))'
# Created in 2023 by Luxferre, released into public domain
FORMULA="$1"
SAMPLERATE="$2"
[ -z "$FORMULA" ] && printf 'No formula!\n' && exit
[ -z "$SAMPLERATE" ] && SAMPLERATE=8000
PLAYCMD="play -q -tu8 -r${SAMPLERATE} -c1 -" # most universal option with SoX play command
# or else uncomment one of these if you don't have SoX:
#PLAYCMD="tee /dev/dsp" # if you have bare OSS (only 8KHz input)
#PLAYCMD="aplay -q -f U8 -r ${SAMPLERATE}" # if you have ALSA
#PLAYCMD="pacat --raw --format=u8 --rate=${SAMPLERATE} --channels=1" # if you have PulseAudio
#PLAYCMD="pw-play --format=u8 --rate=${SAMPLERATE} --channels=1 -" # if you have PipeWire
printf 'ShellBeat by Luxferre\nPlaying formula: %s\nSample rate: %d Hz\n' "$FORMULA" "$SAMPLERATE"
(t=0; while true; do printf '%02X\n' "$((255&(${FORMULA})))"; t=$((t+1)); done) | xxd -r -p | $PLAYCMD > /dev/null