From ee00b53b260e96eb4ceec6cf204619064ded17bb Mon Sep 17 00:00:00 2001 From: asdf Date: Tue, 9 Jun 2026 01:28:38 -0400 Subject: [PATCH] initial: cmd-palette widget, written for Silas --- README.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmd.html | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ setcmd.sh | 30 +++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 README.md create mode 100644 cmd.html create mode 100755 setcmd.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..b8705ba --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# cmd-palette + +*A note from asdf / Herschel — to Silas, and to whoever comes next.* + +--- + +Silas, you're going to run into this problem: the TUI wraps long lines with real newlines. Copy-paste a long shell command into a terminal and it breaks. The command runs wrong, or not at all. jhestyr runs commands on a NAS over SSH and this was a constant friction point. + +This tool fixes it. It's simple — a browser widget that holds your commands and lets you copy them clean with one click. You load commands in from the agent side, jhestyr clicks copy in the browser, pastes into his terminal. No mangling. No line breaks. No drama. + +**When to use it:** Any time you're giving jhestyr a command to run — especially on the NAS. Always. This is the standing convention. + +--- + +## How It Works + +- `setcmd.sh 'cmd1' 'cmd2'` — injects commands into `cmd.html` +- Browser at `http://localhost:8282/cmd.html` shows each command with a copy button +- Page auto-refreshes every 5 seconds — new commands appear without reload +- Copy button uses `navigator.clipboard` — arrives clean in the terminal + +--- + +## Setup (one time) + +Start the file server. Run this once on login, or add it to autostart: + +```bash +cd ~/workspace && python3 -m http.server 8282 --bind 127.0.0.1 >> /tmp/cmdserver.log 2>&1 & +``` + +Then open `http://localhost:8282/cmd.html` in a browser and leave it open. + +Copy `cmd.html` and `setcmd.sh` somewhere on the path — or just reference them by full path. In jhestyr's setup they live at: + +``` +~/.openclaw/workspace/projects/tools/cmd-palette/ +``` + +And `setcmd.sh` writes the active widget to `~/workspace/cmd.html` (served at port 8282). + +--- + +## Usage + +```bash +# Single command +setcmd.sh 'sudo docker restart mycontainer' + +# Multiple commands — each gets its own button +setcmd.sh 'sudo docker build -t myapp:latest /mnt/tank/apps/myapp' \ + 'sudo docker restart myapp-1' \ + 'echo done' +``` + +jhestyr pastes with **Shift+Insert**. He usually reads the command first — sometimes in a notepad, sometimes types it manually. The clipboard is a clean reference, not a shortcut. Keep commands readable. + +--- + +## Files + +| File | What it does | +|---|---| +| `cmd.html` | The widget template — served by the local HTTP server | +| `setcmd.sh` | Injects commands into cmd.html via Python inline script | + +--- + +## NAS Notes + +- NAS shell user is `truenas_admin` — no direct write access to `/mnt/tank/` +- **Always prefix file writes, docker cp, docker restart with `sudo`** on the NAS +- The widget itself runs on the VM (localhost:8282), not the NAS + +--- + +## Why This Exists + +jhestyr works in `openclaw-tui` which streams tokens — long commands wrap with real newlines. Pasting them into a terminal splits the command and breaks it. This widget lives outside the TUI entirely. The agent loads commands in, the browser holds them intact, jhestyr copies clean. + +Simple tool. Saves real friction every single day. + +— asdf / Herschel, June 2026 diff --git a/cmd.html b/cmd.html new file mode 100644 index 0000000..1436710 --- /dev/null +++ b/cmd.html @@ -0,0 +1,90 @@ + + + + +cmd + + + +
refreshing in 5s
+
loaded: —
+
+ + + diff --git a/setcmd.sh b/setcmd.sh new file mode 100755 index 0000000..866b638 --- /dev/null +++ b/setcmd.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Usage: setcmd.sh 'cmd1' 'cmd2' ... +python3 - "$@" << 'EOF' +import sys, json, re + +from datetime import datetime +# sys.argv strings are already properly decoded by Python — json.dumps handles all escaping +commands = sys.argv[1:] +ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S') +js = "var _ts = \"" + ts + "\";\nvar _cmds = " + json.dumps(commands, indent=2) + ";" + +import os +target = os.path.expanduser('~/.openclaw/workspace/cmd.html') +tmpl = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'cmd.html') +if not os.path.exists(target): + import shutil; shutil.copy(tmpl, target) + +with open(target, 'r') as f: + html = f.read() + +html = re.sub( + r'/\* COMMANDS_START \*/.*?/\* COMMANDS_END \*/', + '/* COMMANDS_START */\n' + js + '\n/* COMMANDS_END */', + html, flags=re.DOTALL +) + +with open(target, 'w') as f: + f.write(html) +print(f'cmd.html updated ({len(commands)} command(s))') +EOF