feat: add yq, pandoc, graphviz, tldr (tealdeer) + dot-watch — CLI parity with pi-devbox
- apt: yq (YAML query), pandoc (doc converter), graphviz (dot rendering) - tealdeer: tldr command as ~5MB static musl binary (TEALDEER_VERSION arg) - dot-watch: auto-render .dot to PNG on save (graphviz-only, no imagemagick) - README: document new tools - smoke-test: presence assertions for yq, pandoc, dot, tldr, dot-watch Excludes socat, imagemagick, studio-expose (pi-studio-specific).
This commit is contained in:
@@ -50,6 +50,7 @@ RUN apt-get update && \
|
|||||||
openssh-client \
|
openssh-client \
|
||||||
gnupg \
|
gnupg \
|
||||||
jq \
|
jq \
|
||||||
|
yq \
|
||||||
ripgrep \
|
ripgrep \
|
||||||
fd-find \
|
fd-find \
|
||||||
tree \
|
tree \
|
||||||
@@ -72,6 +73,8 @@ RUN apt-get update && \
|
|||||||
python3-pip \
|
python3-pip \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
nano \
|
nano \
|
||||||
|
pandoc \
|
||||||
|
graphviz \
|
||||||
&& ln -s /usr/bin/fdfind /usr/local/bin/fd \
|
&& ln -s /usr/bin/fdfind /usr/local/bin/fd \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
@@ -295,6 +298,22 @@ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64"
|
|||||||
rm -rf /tmp/uv-* && \
|
rm -rf /tmp/uv-* && \
|
||||||
uv --version
|
uv --version
|
||||||
|
|
||||||
|
# tealdeer — Rust port of tldr (community-maintained command examples)
|
||||||
|
# Provides the `tldr` command; ~5 MB static binary, ~135 MB smaller than
|
||||||
|
# the Node tldr global. Same UX as the Node version.
|
||||||
|
ARG TEALDEER_VERSION=latest
|
||||||
|
RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64" ;; *) echo "x86_64" ;; esac) && \
|
||||||
|
V="${TEALDEER_VERSION}" && \
|
||||||
|
if [ "$V" = "latest" ]; then \
|
||||||
|
V=$(curl -sI --retry 5 --retry-delay 5 --retry-all-errors "https://github.com/tealdeer-rs/tealdeer/releases/latest" | awk 'tolower($1)=="location:" { sub(/\r$/,"",$2); n=split($2,a,"/"); print a[n] }'); \
|
||||||
|
fi && \
|
||||||
|
V="${V#v}" && \
|
||||||
|
[ -n "$V" ] && \
|
||||||
|
echo "Installing tealdeer ${V}" && \
|
||||||
|
curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://github.com/tealdeer-rs/tealdeer/releases/download/v${V}/tealdeer-linux-${ARCH}-musl" -o /usr/local/bin/tldr && \
|
||||||
|
chmod +x /usr/local/bin/tldr && \
|
||||||
|
tldr --version
|
||||||
|
|
||||||
# ── MemPalace — local-first AI memory system ─────────────────────────
|
# ── MemPalace — local-first AI memory system ─────────────────────────
|
||||||
# Provides semantic search over conversation history via 29 MCP tools.
|
# Provides semantic search over conversation history via 29 MCP tools.
|
||||||
# Always installed in the base (variant-independent). Set
|
# Always installed in the base (variant-independent). Set
|
||||||
@@ -466,6 +485,7 @@ COPY rootfs/home/developer/.gitignore_global /etc/skel-devbox/.gitignore_global
|
|||||||
|
|
||||||
# ── Entrypoint ────────────────────────────────────────────────────────
|
# ── Entrypoint ────────────────────────────────────────────────────────
|
||||||
COPY rootfs/usr/local/lib/opencode-devbox/ /usr/local/lib/opencode-devbox/
|
COPY rootfs/usr/local/lib/opencode-devbox/ /usr/local/lib/opencode-devbox/
|
||||||
|
COPY rootfs/usr/local/bin/dot-watch /usr/local/bin/dot-watch
|
||||||
# Image-baked skills + harness instruction. Under /usr/local so a named volume
|
# Image-baked skills + harness instruction. Under /usr/local so a named volume
|
||||||
# over a home dir (e.g. devbox-opencode-config on ~/.config/opencode) can't
|
# over a home dir (e.g. devbox-opencode-config on ~/.config/opencode) can't
|
||||||
# shadow them; entrypoint-user.sh links them into ~/.agents/skills/ and
|
# shadow them; entrypoint-user.sh links them into ~/.agents/skills/ and
|
||||||
@@ -475,6 +495,7 @@ COPY rootfs/usr/local/share/opencode-devbox/ /usr/local/share/opencode-devbox/
|
|||||||
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
COPY entrypoint-user.sh /usr/local/bin/entrypoint-user.sh
|
COPY entrypoint-user.sh /usr/local/bin/entrypoint-user.sh
|
||||||
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/entrypoint-user.sh \
|
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/entrypoint-user.sh \
|
||||||
|
/usr/local/bin/dot-watch \
|
||||||
/usr/local/lib/opencode-devbox/*.py
|
/usr/local/lib/opencode-devbox/*.py
|
||||||
|
|
||||||
# Start as root — entrypoint adjusts UID/GID then drops to developer
|
# Start as root — entrypoint adjusts UID/GID then drops to developer
|
||||||
|
|||||||
@@ -835,7 +835,11 @@ Container (Debian trixie)
|
|||||||
├── oh-my-opencode-slim (optional — multi-agent orchestration plugin, includes Bun)
|
├── oh-my-opencode-slim (optional — multi-agent orchestration plugin, includes Bun)
|
||||||
├── AWS CLI v2 (SSO + Bedrock auth)
|
├── AWS CLI v2 (SSO + Bedrock auth)
|
||||||
├── neovim 0.12, nano, micro, tmux, htop, bat, eza, zoxide, uv, rustup, make, gcc, g++, rsync
|
├── neovim 0.12, nano, micro, tmux, htop, bat, eza, zoxide, uv, rustup, make, gcc, g++, rsync
|
||||||
├── git, git-crypt, age, gitleaks, ssh, ripgrep, fd, fzf, jq, curl, tree
|
├── git, git-crypt, age, gitleaks, ssh, ripgrep, fd, fzf, jq, yq, curl, tree
|
||||||
|
├── pandoc (Markdown↔HTML/PDF/etc. conversion)
|
||||||
|
├── graphviz (dot diagram rendering)
|
||||||
|
├── tldr (tealdeer — quick command examples)
|
||||||
|
├── dot-watch (auto-render .dot to PNG on save)
|
||||||
├── Node.js (for MCP servers)
|
├── Node.js (for MCP servers)
|
||||||
├── Bun (optional — included with oh-my-opencode-slim)
|
├── Bun (optional — included with oh-my-opencode-slim)
|
||||||
├── entrypoint.sh (UID adjustment, git config, provider setup)
|
├── entrypoint.sh (UID adjustment, git config, provider setup)
|
||||||
|
|||||||
Executable
+59
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# dot-watch — auto-rerender a graphviz .dot file to PNG on every save.
|
||||||
|
#
|
||||||
|
# WHY THIS EXISTS
|
||||||
|
# pi-studio renders mermaid natively but has no graphviz/DOT renderer.
|
||||||
|
# Its markdown preview DOES render local image links (.png/.jpg/.gif/.webp),
|
||||||
|
# and the editor offers "refresh from disk". This helper closes the loop:
|
||||||
|
# edit a .dot file -> dot-watch regenerates <name>.png -> hit refresh in
|
||||||
|
# Studio to see the update. Uses mtime polling (no inotify dependency,
|
||||||
|
# which isn't in the trixie-slim base).
|
||||||
|
#
|
||||||
|
# USAGE
|
||||||
|
# dot-watch <file.dot> [layout] [dpi]
|
||||||
|
# layout: dot|neato|fdp|circo|twopi (default: dot)
|
||||||
|
# dpi: output resolution (default: 150)
|
||||||
|
# env: DOT_WATCH_INTERVAL=<seconds> poll interval (default: 1)
|
||||||
|
#
|
||||||
|
# EXAMPLES
|
||||||
|
# dot-watch /workspace/graph.dot
|
||||||
|
# dot-watch graph.dot neato 200
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SRC="${1:?usage: dot-watch <file.dot> [layout] [dpi]}"
|
||||||
|
LAYOUT="${2:-dot}"
|
||||||
|
DPI="${3:-150}"
|
||||||
|
|
||||||
|
[[ -f "$SRC" ]] || { echo "error: no such file: $SRC" >&2; exit 1; }
|
||||||
|
command -v "$LAYOUT" >/dev/null || { echo "error: layout engine '$LAYOUT' not found" >&2; exit 1; }
|
||||||
|
|
||||||
|
OUT="${SRC%.dot}.png"
|
||||||
|
INTERVAL="${DOT_WATCH_INTERVAL:-1}" # seconds between polls
|
||||||
|
ERRLOG="$(mktemp -t dot-watch.XXXXXX.err)"
|
||||||
|
trap 'rm -f "$ERRLOG"' EXIT
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if "$LAYOUT" -Tpng -Gdpi="$DPI" "$SRC" -o "$OUT" 2> "$ERRLOG"; then
|
||||||
|
printf '[%s] rendered -> %s\n' "$(date +%H:%M:%S)" "$OUT"
|
||||||
|
else
|
||||||
|
printf '[%s] DOT error:\n' "$(date +%H:%M:%S)"
|
||||||
|
sed 's/^/ /' "$ERRLOG"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# portable mtime (GNU stat, fallback to BSD stat)
|
||||||
|
mtime() { stat -c %Y "$1" 2>/dev/null || stat -f %m "$1" 2>/dev/null; }
|
||||||
|
|
||||||
|
echo "watching $SRC ($LAYOUT, ${DPI}dpi) -> $OUT [Ctrl-C to stop]"
|
||||||
|
render
|
||||||
|
last="$(mtime "$SRC")"
|
||||||
|
while true; do
|
||||||
|
sleep "$INTERVAL"
|
||||||
|
[[ -f "$SRC" ]] || continue
|
||||||
|
now="$(mtime "$SRC")"
|
||||||
|
if [[ "$now" != "$last" ]]; then
|
||||||
|
last="$now"
|
||||||
|
render
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -131,12 +131,17 @@ run "fzf" "fzf --version"
|
|||||||
run "fd" "fd --version"
|
run "fd" "fd --version"
|
||||||
run "rg" "rg --version | head -1"
|
run "rg" "rg --version | head -1"
|
||||||
run "jq" "jq --version"
|
run "jq" "jq --version"
|
||||||
|
run "yq" "yq --version"
|
||||||
run "git-crypt" "git-crypt --version | head -1"
|
run "git-crypt" "git-crypt --version | head -1"
|
||||||
run "gitleaks" "gitleaks version"
|
run "gitleaks" "gitleaks version"
|
||||||
run "aws" "aws --version"
|
run "aws" "aws --version"
|
||||||
run "gitea-mcp" "gitea-mcp --version"
|
run "gitea-mcp" "gitea-mcp --version"
|
||||||
run "gosu" "gosu --version"
|
run "gosu" "gosu --version"
|
||||||
run "tmux" "tmux -V"
|
run "tmux" "tmux -V"
|
||||||
|
run "pandoc" "pandoc --version | head -1"
|
||||||
|
run "graphviz (dot)" "dot -V"
|
||||||
|
run "tldr (tealdeer)" "tldr --version"
|
||||||
|
run "dot-watch" "test -x /usr/local/bin/dot-watch && bash -n /usr/local/bin/dot-watch && echo ok"
|
||||||
|
|
||||||
# SSH ControlMaster baked defaults: the config file must exist (image-level)
|
# SSH ControlMaster baked defaults: the config file must exist (image-level)
|
||||||
# and ssh -G must report ControlPath rooted at /tmp/sshcm/ for an arbitrary
|
# and ssh -G must report ControlPath rooted at /tmp/sshcm/ for an arbitrary
|
||||||
|
|||||||
Reference in New Issue
Block a user