From 4b2927067af5a05e03c455c8e3477188d05e5b9f Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Thu, 16 Apr 2026 19:29:07 +0200 Subject: [PATCH] =?UTF-8?q?toys:=20rename=20ollie.sh=E2=86=92osh,=20oq=20r?= =?UTF-8?q?estored?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- toys/ollie.sh | 124 ------------------------------------ toys/oq | 88 ++++++++++++++++++++++++++ toys/osh | 172 ++++++++++++++++++++++++++++++-------------------- 3 files changed, 192 insertions(+), 192 deletions(-) delete mode 100755 toys/ollie.sh create mode 100755 toys/oq diff --git a/toys/ollie.sh b/toys/ollie.sh deleted file mode 100755 index 9715918..0000000 --- a/toys/ollie.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env bash -# ollie.sh: a simplified, fully-functional chat interface for ollie AI agents. -set -o history - -MOUNT=${OLLIE_9MOUNT:-$HOME/mnt/ollie} -LAST=$HOME/.config/ollie/last-session - -# load_session → prints session ID for that dir, or nothing -load_session() { - awk -F'\t' -v d="$1" 'NF==2 && $1==d {print $2; exit}' "$LAST" 2>/dev/null -} - -# save_session → upserts {dir}\t{id} in $LAST -save_session() { - local dir="$1" id="$2" tmp - mkdir -p "$(dirname "$LAST")" - tmp=$(mktemp) - awk -F'\t' -v d="$dir" 'NF==2 && $1!=d' "$LAST" 2>/dev/null > "$tmp" || true - printf '%s\t%s\n' "$dir" "$id" >> "$tmp" - mv "$tmp" "$LAST" -} - -die() { printf 'error: %s\n' "$*" >&2; exit 1; } -is_idle() { [ "$(tr -d '[:space:]' < "$S/state" 2>/dev/null)" = "idle" ]; } -drain() { - local n - n=$(wc -c < "$S/chat" 2>/dev/null || echo 0) - [ "$n" -gt "$chat_off" ] && tail -c "+$((chat_off+1))" "$S/chat" | head -c "$((n-chat_off))" - chat_off=$n -} - -# Parse flags -new=0; resume=0; id_flag= -backend=; model=; agent=; cwd= -while [ $# -gt 0 ]; do - case $1 in - -new) new=1 ;; - -resume) resume=1 ;; - -session) id_flag=$2; shift ;; - -backend) backend=$2; shift ;; - -model) model=$2; shift ;; - -agent) agent=$2; shift ;; - -cwd) cwd=$2; shift ;; - *) die "unknown flag: $1" ;; - esac - shift -done - -# Build "new" ctl command with opts -new_cmd() { - local cmd=new - [ -n "$backend" ] && cmd="$cmd backend=$backend" - [ -n "$model" ] && cmd="$cmd model=$model" - [ -n "$agent" ] && cmd="$cmd agent=$agent" - cmd="$cmd cwd=${cwd:-$(pwd)}" - printf '%s\n' "$cmd" -} - -# Create a fresh session, sets $id -create_session() { - local before d deadline - before=$(ls "$MOUNT/s" 2>/dev/null) - new_cmd > "$MOUNT/ctl" || die "write ctl" - deadline=$(($(date +%s) + 5)) - while [ "$(date +%s)" -lt "$deadline" ]; do - for d in $(ls "$MOUNT/s" 2>/dev/null); do - printf '%s\n' "$before" | grep -qxF "$d" || { id=$d; return; } - done - sleep 0.05 - done - die "timeout waiting for new session" -} - -# Resolve session -_dir=${cwd:-$(pwd)} -if [ -n "$id_flag" ]; then - [ -d "$MOUNT/s/$id_flag" ] || die "session not found: $id_flag" - id=$id_flag; printf 'session: %s (resumed)\n' "$id" >&2 -elif [ "$resume" = 1 ]; then - id=$(load_session "$_dir") - [ -n "$id" ] && [ -d "$MOUNT/s/$id" ] || die "no resumable session for $_dir" - printf 'session: %s (resumed)\n' "$id" >&2 -elif [ "$new" = 1 ]; then - create_session - save_session "$_dir" "$id" - printf 'session: %s\n' "$id" >&2 -else - id=$(load_session "$_dir") - if [ -n "$id" ] && [ -d "$MOUNT/s/$id" ]; then - printf 'session: %s (resumed)\n' "$id" >&2 - else - create_session - save_session "$_dir" "$id" - printf 'session: %s\n' "$id" >&2 - fi -fi - -S=$MOUNT/s/$id -chat_off=$(wc -c < "$S/chat" 2>/dev/null || echo 0) - -trap ' - [ "$(tr -d "[:space:]" < "$S/state" 2>/dev/null)" = "idle" ] && exit - printf "\n" >&2; printf "stop\n" > "$S/ctl" -' INT - -while true; do - printf '> ' - IFS= read -re input || break - [ -z "$input" ] && continue - history -s "$input" - case "$input" in - /kill) printf 'kill %s\n' "$id" > "$MOUNT/ctl"; break ;; - "/q "*) printf '%s\n' "${input#/q }" > "$S/enqueue" - printf 'queued\n' >&2; continue ;; - /*) printf '%s\n' "$input" > "$S/ctl" ;; - *) printf '%s' "$input" > "$S/prompt" || { printf 'submit failed\n' >&2; continue; } ;; - esac - sleep 0.15 - while true; do - drain - is_idle && { drain; printf '\n'; break; } - sleep 0.05 - done -done diff --git a/toys/oq b/toys/oq new file mode 100755 index 0000000..95fd9c0 --- /dev/null +++ b/toys/oq @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# oq: non-interactive ollie query via b/ namespace. +# +# Usage: oq [flags] [prompt...] +# echo "prompt" | oq [flags] +# +# Flags: +# -name base name for the job(s); suffixed with -0, -1, ... +# -cwd working directory (default: pwd) +# -agent agent config name +# -backend backend override +# -model model override +# -output output format hint (e.g. json) +# -parallel run the same prompt N times in parallel (default: 1) +# -keep do not remove job(s) after printing result + +set -euo pipefail + +MOUNT=${OLLIE_9MOUNT:-$HOME/mnt/ollie} + +die() { printf 'oq: %s\n' "$*" >&2; exit 1; } + +name=; cwd=; agent=; backend=; model=; output=; parallel=1; keep=0 + +while [ $# -gt 0 ]; do + case $1 in + -name) name=$2; shift ;; + -cwd) cwd=$2; shift ;; + -agent) agent=$2; shift ;; + -backend) backend=$2; shift ;; + -model) model=$2; shift ;; + -output) output=$2; shift ;; + -parallel) parallel=$2; shift ;; + -keep) keep=1 ;; + --) shift; break ;; + -*) die "unknown flag: $1" ;; + *) break ;; + esac + shift +done + +# Prompt from remaining args or stdin. +if [ $# -gt 0 ]; then + prompt="$*" +elif ! [ -t 0 ]; then + prompt=$(cat) +else + die "prompt required: pass as arguments or pipe via stdin" +fi + +[ -z "$prompt" ] && die "empty prompt" + +cwd=${cwd:-$(pwd)} +[ -z "$name" ] && name=$(date +%s%N)-$$ + +{ + printf 'name=%s\n' "$name" + printf 'cwd=%s\n' "$cwd" + [ -n "$agent" ] && printf 'agent=%s\n' "$agent" + [ -n "$backend" ] && printf 'backend=%s\n' "$backend" + [ -n "$model" ] && printf 'model=%s\n' "$model" + [ -n "$output" ] && printf 'output=%s\n' "$output" + printf 'parallel=%s\n' "$parallel" + printf -- '---\n' + printf '%s\n' "$prompt" +} > "$MOUNT/b/new" || die "failed to submit job" + +rc=0 +for i in $(seq 0 $((parallel - 1))); do + id="${name}-${i}" + B="$MOUNT/b/$id" + + while true; do + status=$(tr -d '[:space:]' < "$B/status" 2>/dev/null) || { sleep 0.05; continue; } + case "$status" in + done*) break ;; + failed*) rc=1; break ;; + *) sleep 0.05 ;; + esac + done + + [ "$parallel" -gt 1 ] && printf '=== %s ===\n' "$id" + cat "$B/result" 2>/dev/null + + [ "$keep" -eq 0 ] && rm -r "$B" 2>/dev/null || true +done + +exit $rc diff --git a/toys/osh b/toys/osh index 21b520b..9715918 100755 --- a/toys/osh +++ b/toys/osh @@ -1,88 +1,124 @@ #!/usr/bin/env bash -# osh: non-interactive ollie query via b/ namespace. -# -# Usage: osh [flags] [prompt...] -# echo "prompt" | osh [flags] -# -# Flags: -# -name base name for the job(s); suffixed with -0, -1, ... -# -cwd working directory (default: pwd) -# -agent agent config name -# -backend backend override -# -model model override -# -output output format hint (e.g. json) -# -parallel run the same prompt N times in parallel (default: 1) -# -keep do not remove job(s) after printing result - -set -euo pipefail +# ollie.sh: a simplified, fully-functional chat interface for ollie AI agents. +set -o history MOUNT=${OLLIE_9MOUNT:-$HOME/mnt/ollie} +LAST=$HOME/.config/ollie/last-session -die() { printf 'osh: %s\n' "$*" >&2; exit 1; } +# load_session → prints session ID for that dir, or nothing +load_session() { + awk -F'\t' -v d="$1" 'NF==2 && $1==d {print $2; exit}' "$LAST" 2>/dev/null +} -name=; cwd=; agent=; backend=; model=; output=; parallel=1; keep=0 +# save_session → upserts {dir}\t{id} in $LAST +save_session() { + local dir="$1" id="$2" tmp + mkdir -p "$(dirname "$LAST")" + tmp=$(mktemp) + awk -F'\t' -v d="$dir" 'NF==2 && $1!=d' "$LAST" 2>/dev/null > "$tmp" || true + printf '%s\t%s\n' "$dir" "$id" >> "$tmp" + mv "$tmp" "$LAST" +} +die() { printf 'error: %s\n' "$*" >&2; exit 1; } +is_idle() { [ "$(tr -d '[:space:]' < "$S/state" 2>/dev/null)" = "idle" ]; } +drain() { + local n + n=$(wc -c < "$S/chat" 2>/dev/null || echo 0) + [ "$n" -gt "$chat_off" ] && tail -c "+$((chat_off+1))" "$S/chat" | head -c "$((n-chat_off))" + chat_off=$n +} + +# Parse flags +new=0; resume=0; id_flag= +backend=; model=; agent=; cwd= while [ $# -gt 0 ]; do case $1 in - -name) name=$2; shift ;; - -cwd) cwd=$2; shift ;; - -agent) agent=$2; shift ;; - -backend) backend=$2; shift ;; - -model) model=$2; shift ;; - -output) output=$2; shift ;; - -parallel) parallel=$2; shift ;; - -keep) keep=1 ;; - --) shift; break ;; - -*) die "unknown flag: $1" ;; - *) break ;; + -new) new=1 ;; + -resume) resume=1 ;; + -session) id_flag=$2; shift ;; + -backend) backend=$2; shift ;; + -model) model=$2; shift ;; + -agent) agent=$2; shift ;; + -cwd) cwd=$2; shift ;; + *) die "unknown flag: $1" ;; esac shift done -# Prompt from remaining args or stdin. -if [ $# -gt 0 ]; then - prompt="$*" -elif ! [ -t 0 ]; then - prompt=$(cat) +# Build "new" ctl command with opts +new_cmd() { + local cmd=new + [ -n "$backend" ] && cmd="$cmd backend=$backend" + [ -n "$model" ] && cmd="$cmd model=$model" + [ -n "$agent" ] && cmd="$cmd agent=$agent" + cmd="$cmd cwd=${cwd:-$(pwd)}" + printf '%s\n' "$cmd" +} + +# Create a fresh session, sets $id +create_session() { + local before d deadline + before=$(ls "$MOUNT/s" 2>/dev/null) + new_cmd > "$MOUNT/ctl" || die "write ctl" + deadline=$(($(date +%s) + 5)) + while [ "$(date +%s)" -lt "$deadline" ]; do + for d in $(ls "$MOUNT/s" 2>/dev/null); do + printf '%s\n' "$before" | grep -qxF "$d" || { id=$d; return; } + done + sleep 0.05 + done + die "timeout waiting for new session" +} + +# Resolve session +_dir=${cwd:-$(pwd)} +if [ -n "$id_flag" ]; then + [ -d "$MOUNT/s/$id_flag" ] || die "session not found: $id_flag" + id=$id_flag; printf 'session: %s (resumed)\n' "$id" >&2 +elif [ "$resume" = 1 ]; then + id=$(load_session "$_dir") + [ -n "$id" ] && [ -d "$MOUNT/s/$id" ] || die "no resumable session for $_dir" + printf 'session: %s (resumed)\n' "$id" >&2 +elif [ "$new" = 1 ]; then + create_session + save_session "$_dir" "$id" + printf 'session: %s\n' "$id" >&2 else - die "prompt required: pass as arguments or pipe via stdin" + id=$(load_session "$_dir") + if [ -n "$id" ] && [ -d "$MOUNT/s/$id" ]; then + printf 'session: %s (resumed)\n' "$id" >&2 + else + create_session + save_session "$_dir" "$id" + printf 'session: %s\n' "$id" >&2 + fi fi -[ -z "$prompt" ] && die "empty prompt" +S=$MOUNT/s/$id +chat_off=$(wc -c < "$S/chat" 2>/dev/null || echo 0) -cwd=${cwd:-$(pwd)} -[ -z "$name" ] && name=$(date +%s%N)-$$ - -{ - printf 'name=%s\n' "$name" - printf 'cwd=%s\n' "$cwd" - [ -n "$agent" ] && printf 'agent=%s\n' "$agent" - [ -n "$backend" ] && printf 'backend=%s\n' "$backend" - [ -n "$model" ] && printf 'model=%s\n' "$model" - [ -n "$output" ] && printf 'output=%s\n' "$output" - printf 'parallel=%s\n' "$parallel" - printf -- '---\n' - printf '%s\n' "$prompt" -} > "$MOUNT/b/new" || die "failed to submit job" - -rc=0 -for i in $(seq 0 $((parallel - 1))); do - id="${name}-${i}" - B="$MOUNT/b/$id" +trap ' + [ "$(tr -d "[:space:]" < "$S/state" 2>/dev/null)" = "idle" ] && exit + printf "\n" >&2; printf "stop\n" > "$S/ctl" +' INT +while true; do + printf '> ' + IFS= read -re input || break + [ -z "$input" ] && continue + history -s "$input" + case "$input" in + /kill) printf 'kill %s\n' "$id" > "$MOUNT/ctl"; break ;; + "/q "*) printf '%s\n' "${input#/q }" > "$S/enqueue" + printf 'queued\n' >&2; continue ;; + /*) printf '%s\n' "$input" > "$S/ctl" ;; + *) printf '%s' "$input" > "$S/prompt" || { printf 'submit failed\n' >&2; continue; } ;; + esac + sleep 0.15 while true; do - status=$(tr -d '[:space:]' < "$B/status" 2>/dev/null) || { sleep 0.05; continue; } - case "$status" in - done*) break ;; - failed*) rc=1; break ;; - *) sleep 0.05 ;; - esac + drain + is_idle && { drain; printf '\n'; break; } + sleep 0.05 done - - [ "$parallel" -gt 1 ] && printf '=== %s ===\n' "$id" - cat "$B/result" 2>/dev/null - - [ "$keep" -eq 0 ] && rm -r "$B" 2>/dev/null || true done - -exit $rc