One-command install + launchd agent for deploying to another Mac

Answers "do we have to start the server in terminal?" -- no. install.sh installs
deps and registers a launchd agent with RunAtLoad + KeepAlive, so the daemon
starts at login and comes back if it dies. Verified by killing the process: back
up and serving within seconds under a new pid. Deploying to the M3 Air is then:

    git clone ssh://git@100.71.119.27:222/monster/yourovo.git
    cd yourovo/mac && ./install.sh

Also revises the A/B/C recommendation. Grepping the extension shows it calls only
six endpoints: /status, /write-tag, /read-tag (all served here) plus /weight,
/scale/start, /scale/stop (the DYMO M10 postal scale, Node-only). So the gap to a
single service is the scale and nothing else -- which makes option B (Node on
:7791 with this one proxying) the wrong trade: one URL but two processes, two
dependency stacks and two things to keep alive, against a stated goal of one
button on a second machine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-22 16:49:21 +10:00
parent 6d9a49ea42
commit d4b16c075b
2 changed files with 113 additions and 6 deletions

View File

@ -42,13 +42,29 @@ PRINT <set>, <copy> # note the space after the co
All plain ASCII, one command per line, CRLF-terminated. `writeUHF("H",2,12,"E",epc)`
emits exactly `UHF WRITE H,2,12,E,"<24 hex chars>"`.
## Setup
## Install on a new Mac (one command)
```bash
./setup.sh # brew install libusb + venv with pyusb/pillow/segno
git clone ssh://git@100.71.119.27:222/monster/yourovo.git
cd yourovo/mac && ./install.sh
```
Then double-click **`start-daemon.command`** (leave it open while you work).
That installs libusb + the venv, then registers a **launchd agent** so the daemon starts
at login and restarts itself if it ever dies. **No Terminal window to keep open, and
nothing to click** — after this it is simply always running. Verified: `kill` the process
and launchd has it serving again within seconds.
Re-run `./install.sh` any time to pull the latest commit and reload. `./install.sh
uninstall` removes the agent.
| | |
|---|---|
| logs | `tail -f ~/Library/Logs/pricegod-labeld.log` |
| restart | `launchctl kickstart -k gui/$(id -u)/com.pricegod.labeld` |
| check | `curl -s localhost:7790/devices` |
For a foreground run while developing (agent must be stopped first), use `./setup.sh`
then double-click **`start-daemon.command`**.
## Files
@ -106,9 +122,21 @@ handles this, and the resolution difference (203 vs 300 dpi) comes from `profile
(`CF [ADDR] [CMD] [LEN] [DATA] [CRC16]` @115200) plus the postal-scale and inventory
routes, none of which this service implements — so it is *not* a full replacement.
Pick one of: run this on `--port 7791`; keep the Chafon daemon for the gun and run this
one elsewhere; or fold the Chafon protocol in here. Unresolved — decide before wiring the
extension.
The extension calls only six endpoints:
| endpoint | owner today |
|---|---|
| `/status` `/write-tag` `/read-tag` | ✅ this service |
| `/weight` `/scale/start` `/scale/stop` | ❌ Node only — DYMO M10 postal scale over USB HID |
So the gap to a genuinely single service is **the postal scale, and nothing else**. Once
`/weight` and `/scale/*` are ported here (the M10 is USB HID `0922:8003/8004`; the parsing
is ~130 lines of `index.js`), the Node daemon can be retired for the extension's purposes
and there is one process, one venv, one launchd agent.
Until then the honest position is: two services, or no scale. Running this one on
`--port 7791` behind the Node daemon works but gives one URL and *two* processes, which
defeats the point of a single launchd agent.
## Media profiles

79
mac/install.sh Executable file
View File

@ -0,0 +1,79 @@
#!/bin/bash
# One-command install on a fresh Apple Silicon Mac.
#
# git clone ssh://git@100.71.119.27:222/monster/yourovo.git
# cd yourovo/mac && ./install.sh
#
# Installs dependencies, then registers a launchd agent so the daemon starts at login
# and restarts itself if it dies. No Terminal needed after this, ever.
#
# Re-runnable: pulls the latest commit and reloads the agent.
set -e
cd "$(dirname "$0")"
HERE="$(pwd -P)"
LABEL="com.pricegod.labeld"
PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
LOG="$HOME/Library/Logs/pricegod-labeld.log"
case "${1:-install}" in
uninstall)
launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true
rm -f "$PLIST"
echo "removed $LABEL"
exit 0
;;
esac
echo "==> updating from git"
git -C "$HERE/.." pull --ff-only 2>/dev/null || echo " (skipped -- not a clean fast-forward)"
echo "==> dependencies"
./setup.sh
echo "==> launchd agent"
mkdir -p "$(dirname "$PLIST")" "$(dirname "$LOG")"
cat > "$PLIST" <<PLISTEOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key> <string>$LABEL</string>
<key>ProgramArguments</key>
<array>
<string>$HERE/venv/bin/python</string>
<string>$HERE/daemon.py</string>
</array>
<key>WorkingDirectory</key> <string>$HERE</string>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>ProcessType</key> <string>Interactive</string>
<key>StandardOutPath</key> <string>$LOG</string>
<key>StandardErrorPath</key> <string>$LOG</string>
</dict>
</plist>
PLISTEOF
launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true
launchctl bootstrap "gui/$(id -u)" "$PLIST"
launchctl kickstart -k "gui/$(id -u)/$LABEL"
echo "==> waiting for the daemon"
for i in $(seq 1 20); do
if curl -fsS -m 2 http://localhost:7790/ >/dev/null 2>&1; then
echo
curl -s http://localhost:7790/devices
echo
echo
echo "OK -- running on http://localhost:7790/ and will start at every login."
echo " logs: tail -f $LOG"
echo " restart: launchctl kickstart -k gui/$(id -u)/$LABEL"
echo " uninstall: ./install.sh uninstall"
exit 0
fi
sleep 0.5
done
echo "daemon did not come up -- check $LOG" >&2
tail -20 "$LOG" 2>/dev/null || true
exit 1